From adba83d8df176288083969f2c3f975bbfc1acd9c Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Mon, 9 Jan 2023 06:28:27 +0100 Subject: [PATCH 001/139] feat: add anoncreds package (#1118) Signed-off-by: Karim Stekelenburg --- packages/anoncreds/README.md | 35 ++++++ packages/anoncreds/jest.config.ts | 14 +++ packages/anoncreds/package.json | 34 ++++++ packages/anoncreds/src/index.ts | 2 + packages/anoncreds/src/models/exchange.ts | 110 ++++++++++++++++++ packages/anoncreds/src/models/index.ts | 3 + packages/anoncreds/src/models/internal.ts | 31 +++++ packages/anoncreds/src/models/registry.ts | 38 ++++++ .../src/services/AnonCredsHolderService.ts | 40 +++++++ .../services/AnonCredsHolderServiceOptions.ts | 80 +++++++++++++ .../src/services/AnonCredsIssuerService.ts | 29 +++++ .../services/AnonCredsIssuerServiceOptions.ts | 44 +++++++ .../src/services/AnonCredsRegistry.ts | 41 +++++++ .../src/services/AnonCredsRegistryOptions.ts | 47 ++++++++ .../src/services/AnonCredsVerifierService.ts | 7 ++ .../AnonCredsVerifierServiceOptions.ts | 31 +++++ packages/anoncreds/src/services/index.ts | 8 ++ packages/anoncreds/tsconfig.build.json | 7 ++ packages/anoncreds/tsconfig.json | 6 + 19 files changed, 607 insertions(+) create mode 100644 packages/anoncreds/README.md create mode 100644 packages/anoncreds/jest.config.ts create mode 100644 packages/anoncreds/package.json create mode 100644 packages/anoncreds/src/index.ts create mode 100644 packages/anoncreds/src/models/exchange.ts create mode 100644 packages/anoncreds/src/models/index.ts create mode 100644 packages/anoncreds/src/models/internal.ts create mode 100644 packages/anoncreds/src/models/registry.ts create mode 100644 packages/anoncreds/src/services/AnonCredsHolderService.ts create mode 100644 packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts create mode 100644 packages/anoncreds/src/services/AnonCredsIssuerService.ts create mode 100644 packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts create mode 100644 packages/anoncreds/src/services/AnonCredsRegistry.ts create mode 100644 packages/anoncreds/src/services/AnonCredsRegistryOptions.ts create mode 100644 packages/anoncreds/src/services/AnonCredsVerifierService.ts create mode 100644 packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts create mode 100644 packages/anoncreds/src/services/index.ts create mode 100644 packages/anoncreds/tsconfig.build.json create mode 100644 packages/anoncreds/tsconfig.json diff --git a/packages/anoncreds/README.md b/packages/anoncreds/README.md new file mode 100644 index 0000000000..5bf5e5fbb0 --- /dev/null +++ b/packages/anoncreds/README.md @@ -0,0 +1,35 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript AnonCreds Interfaces

+

+ License + typescript + @aries-framework/anoncreds version + +

+
+ +### Installation + +### Quick start + +### Example of usage diff --git a/packages/anoncreds/jest.config.ts b/packages/anoncreds/jest.config.ts new file mode 100644 index 0000000000..c7c5196637 --- /dev/null +++ b/packages/anoncreds/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + // setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json new file mode 100644 index 0000000000..29946c1b20 --- /dev/null +++ b/packages/anoncreds/package.json @@ -0,0 +1,34 @@ +{ + "name": "@aries-framework/anoncreds", + "main": "build/index", + "types": "build/index", + "version": "0.2.5", + "files": [ + "build" + ], + "private": true, + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/anoncreds", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/anoncreds" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "*" + }, + "peerDependencies": {}, + "devDependencies": { + "typescript": "~4.3.0" + } +} diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts new file mode 100644 index 0000000000..83fdeb7877 --- /dev/null +++ b/packages/anoncreds/src/index.ts @@ -0,0 +1,2 @@ +export * from './models' +export * from './services' diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts new file mode 100644 index 0000000000..420c26a158 --- /dev/null +++ b/packages/anoncreds/src/models/exchange.ts @@ -0,0 +1,110 @@ +// TODO: Maybe we can make this a bit more specific? +export type WalletQuery = Record + +export interface ReferentWalletQuery { + [key: string]: WalletQuery +} + +export interface NonRevokedInterval { + from?: number + to?: number +} + +export interface AnonCredsCredentialOffer { + schema_id: string + cred_def_id: string + nonce: string + key_correctness_proof: Record +} + +export interface AnonCredsCredentialRequest { + // TODO: Why is this needed? It is just used as context in Ursa, can be any string. Should we remove it? + // Should we not make it did related? + prover_did: string + cred_def_id: string + blinded_ms: Record + blinded_ms_correctness_proof: Record + nonce: string +} + +export interface CredValue { + raw: string + encoded: string // Raw value as number in string +} + +export interface AnonCredsCredential { + schema_id: string + cred_def_id: string + rev_reg_id?: string + values: Record + signature: unknown + signature_correctness_proof: unknown +} + +export interface AnonCredsProof { + requested_proof: { + revealed_attrs: Record< + string, + { + sub_proof_index: number + raw: string + encoded: string + } + > + revealed_attr_groups: Record< + string, + { + sub_proof_index: number + values: { + [key: string]: { + raw: string + encoded: string + } + } + } + > + unrevealed_attrs: Record< + string, + { + sub_proof_index: number + } + > + self_attested_attrs: Record + + requested_predicates: Record + } + proof: any + identifiers: Array<{ + schema_id: string + cred_def_id: string + rev_reg_id?: string + timestamp?: number + }> +} + +export interface AnonCredsProofRequest { + name: string + version: string + nonce: string + requested_attributes: Record< + string, + { + name?: string + names?: string + restrictions?: WalletQuery[] + non_revoked?: NonRevokedInterval + } + > + requested_predicates: Record< + string, + { + name: string + p_type: '>=' | '>' | '<=' | '<' + p_value: number + restrictions?: WalletQuery[] + non_revoked?: NonRevokedInterval + } + > + non_revoked?: NonRevokedInterval + ver?: '1.0' | '2.0' +} diff --git a/packages/anoncreds/src/models/index.ts b/packages/anoncreds/src/models/index.ts new file mode 100644 index 0000000000..6dd1a6e3bb --- /dev/null +++ b/packages/anoncreds/src/models/index.ts @@ -0,0 +1,3 @@ +export * from './internal' +export * from './exchange' +export * from './registry' diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts new file mode 100644 index 0000000000..f0b64d614c --- /dev/null +++ b/packages/anoncreds/src/models/internal.ts @@ -0,0 +1,31 @@ +export interface CredentialInfo { + referent: string + attributes: { + [key: string]: string + } + schemaId: string + credentialDefinitionId: string + revocationRegistryId?: number | undefined + credentialRevocationId?: string | undefined +} + +export interface RequestedAttribute { + credentialId: string + timestamp?: number + revealed: boolean + credentialInfo?: CredentialInfo + revoked?: boolean +} + +export interface RequestedPredicate { + credentialId: string + timestamp?: number + credentialInfo?: CredentialInfo + revoked?: boolean +} + +export interface RequestedCredentials { + requestedAttributes?: Record + requestedPredicates?: Record + selfAttestedAttributes: Record +} diff --git a/packages/anoncreds/src/models/registry.ts b/packages/anoncreds/src/models/registry.ts new file mode 100644 index 0000000000..98268e1e84 --- /dev/null +++ b/packages/anoncreds/src/models/registry.ts @@ -0,0 +1,38 @@ +export interface AnonCredsSchema { + name: string + version: string + attrNames: string[] +} + +export interface AnonCredsCredentialDefinition { + schemaId: string + type: 'CL' + tag: string + // TODO: work out in more detail + value: { + primary: Record + revocation?: unknown + } +} + +export interface AnonCredsRevocationRegistryDefinition { + type: 'CL_ACCUM' + credDefId: string + tag: string + publicKeys: { + accumKey: { + z: string + } + } + maxCredNum: number + tailsLocation: string + tailsHash: string +} + +export interface AnonCredsRevocationList { + // TODO: this is a new property, but do we keep abbreviation or not? + revRegId: string + revocationList: number[] + currentAccumulator: string + timestamp: number +} diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts new file mode 100644 index 0000000000..62c7a49aa4 --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -0,0 +1,40 @@ +import type { CredentialInfo } from '../models' +import type { AnonCredsProof } from '../models/exchange' +import type { + CreateCredentialRequestOptions, + CreateCredentialRequestReturn, + CreateProofOptions, + GetCredentialOptions, + StoreCredentialOptions, + GetCredentialsForProofRequestOptions, + GetCredentialsForProofRequestReturn, +} from './AnonCredsHolderServiceOptions' +import type { AgentContext } from '@aries-framework/core' + +export interface AnonCredsHolderService { + createProof( + agentContext: AgentContext, + options: CreateProofOptions, + metadata?: Record + ): Promise + storeCredential( + agentContext: AgentContext, + options: StoreCredentialOptions, + metadata?: Record + ): Promise + + // TODO: indy has different return types for the credential + getCredential(agentContext: AgentContext, options: GetCredentialOptions): Promise + + createCredentialRequest( + agentContext: AgentContext, + options: CreateCredentialRequestOptions, + metadata?: Record + ): Promise + + deleteCredential(agentContext: AgentContext, credentialId: string): Promise + getCredentialsForProofRequest( + agentContext: AgentContext, + options: GetCredentialsForProofRequestOptions + ): Promise +} diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts new file mode 100644 index 0000000000..86cb8bbcf9 --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -0,0 +1,80 @@ +import type { CredentialInfo, RequestedCredentials } from '../models' +import type { + AnonCredsCredential, + AnonCredsCredentialOffer, + AnonCredsCredentialRequest, + AnonCredsProofRequest, + NonRevokedInterval, + ReferentWalletQuery, +} from '../models/exchange' +import type { + AnonCredsCredentialDefinition, + AnonCredsRevocationList, + AnonCredsRevocationRegistryDefinition, + AnonCredsSchema, +} from '../models/registry' + +export interface AttributeInfo { + name?: string + names?: string[] +} + +export interface CreateProofOptions { + proofRequest: AnonCredsProofRequest + requestedCredentials: RequestedCredentials + schemas: { + [schemaId: string]: AnonCredsSchema + } + credentialDefinitions: { + [credentialDefinitionId: string]: AnonCredsCredentialDefinition + } + revocationStates: { + [revocationRegistryDefinitionId: string]: { + definition: AnonCredsRevocationRegistryDefinition + revocationLists: { + [timestamp: string]: AnonCredsRevocationList + } + } + } +} + +export interface StoreCredentialOptions { + // TODO: what is in credential request metadata? + credentialRequestMetadata: Record + credential: AnonCredsCredential + credentialDefinition: AnonCredsCredentialDefinition + credentialDefinitionId: string + credentialId?: string + revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition + revocationRegistryDefinitionId: string +} + +export interface GetCredentialOptions { + credentialId: string +} + +export interface GetCredentialsForProofRequestOptions { + proofRequest: AnonCredsProofRequest + attributeReferent: string + start?: number + limit?: number + extraQuery?: ReferentWalletQuery +} + +export interface GetCredentialsForProofRequestReturn { + credentialInfo: CredentialInfo + interval?: NonRevokedInterval +} + +export interface CreateCredentialRequestOptions { + // TODO: Why is this needed? It is just used as context in Ursa, can be any string. Should we remove it? + // Should we not make it did related? (related to comment in AnonCredsCredentialRequest) + holderDid: string + credentialOffer: AnonCredsCredentialOffer + credentialDefinition: AnonCredsCredentialDefinition +} + +export interface CreateCredentialRequestReturn { + credentialRequest: AnonCredsCredentialRequest + credentialRequestMetadata: Record +} diff --git a/packages/anoncreds/src/services/AnonCredsIssuerService.ts b/packages/anoncreds/src/services/AnonCredsIssuerService.ts new file mode 100644 index 0000000000..f3fb8c128f --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsIssuerService.ts @@ -0,0 +1,29 @@ +import type { AnonCredsCredentialOffer } from '../models/exchange' +import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../models/registry' +import type { + CreateSchemaOptions, + CreateCredentialDefinitionOptions, + CreateCredentialOfferOptions, + CreateCredentialReturn, + CreateCredentialOptions, +} from './AnonCredsIssuerServiceOptions' +import type { AgentContext } from '@aries-framework/core' + +export interface AnonCredsIssuerService { + createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise + + // This should store the private part of the credential definition as in the indy-sdk + // we don't have access to the private part of the credential definition + createCredentialDefinition( + agentContext: AgentContext, + options: CreateCredentialDefinitionOptions, + metadata?: Record + ): Promise + + createCredentialOffer( + agentContext: AgentContext, + options: CreateCredentialOfferOptions + ): Promise + + createCredential(agentContext: AgentContext, options: CreateCredentialOptions): Promise +} diff --git a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts new file mode 100644 index 0000000000..27f5450d2f --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts @@ -0,0 +1,44 @@ +import type { + AnonCredsCredential, + AnonCredsCredentialOffer, + AnonCredsCredentialRequest, + CredValue, +} from '../models/exchange' +import type { AnonCredsSchema } from '../models/registry' + +export interface CreateSchemaOptions { + issuerId: string + name: string + version: string + attrNames: string[] +} + +export interface CreateCredentialDefinitionOptions { + issuerId: string + tag: string // TODO: this was initially defined as optional, is that the case? + supportRevocation?: boolean + + // If schema doesn't include the id, we need to add it as a separate input parameter + schema: { + value: AnonCredsSchema + id: string + } +} + +export interface CreateCredentialOfferOptions { + credentialDefinitionId: string +} + +export interface CreateCredentialOptions { + credentialOffer: AnonCredsCredentialOffer + credentialRequest: AnonCredsCredentialRequest + credentialValues: Record + revocationRegistryId?: string + // TODO: should this just be the tails file instead of a path? + tailsFilePath?: string +} + +export interface CreateCredentialReturn { + credential: AnonCredsCredential + credentialRevocationId?: string +} diff --git a/packages/anoncreds/src/services/AnonCredsRegistry.ts b/packages/anoncreds/src/services/AnonCredsRegistry.ts new file mode 100644 index 0000000000..ead8f8aa70 --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsRegistry.ts @@ -0,0 +1,41 @@ +import type { + AnonCredsCredentialDefinition, + AnonCredsRevocationList, + AnonCredsRevocationRegistryDefinition, + AnonCredsSchema, +} from '../models/registry' +import type { + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, + RegisterRevocationListOptions, + RegisterRevocationListReturn, + RegisterRevocationRegistryDefinitionOptions, + RegisterRevocationRegistryDefinitionReturn, + RegisterSchemaOptions, + RegisterSchemaReturn, +} from './AnonCredsRegistryOptions' +import type { AgentContext } from '@aries-framework/core' + +// This service can be registered multiple times in a single AFJ instance. +// TODO: should all methods have interfaces as input for consistency? +export interface AnonCredsRegistry { + getSchema(agentContext: AgentContext, schemaId: string): Promise + registerSchema(options: RegisterSchemaOptions): Promise + + getCredentialDefinition(credentialDefinitionId: string): Promise + registerCredentialDefinition( + options: RegisterCredentialDefinitionOptions + ): Promise + + getRevocationRegistryDefinition( + revocationRegistryDefinitionId: string + ): Promise + registerRevocationRegistryDefinition( + options: RegisterRevocationRegistryDefinitionOptions + ): Promise + + // TODO: The name of this data model is still tbd. + getRevocationList(revocationRegistryId: string, timestamp: string): Promise + + registerRevocationList(options: RegisterRevocationListOptions): Promise +} diff --git a/packages/anoncreds/src/services/AnonCredsRegistryOptions.ts b/packages/anoncreds/src/services/AnonCredsRegistryOptions.ts new file mode 100644 index 0000000000..206d56fc4d --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsRegistryOptions.ts @@ -0,0 +1,47 @@ +import type { + AnonCredsCredentialDefinition, + AnonCredsRevocationList, + AnonCredsRevocationRegistryDefinition, + AnonCredsSchema, +} from '../models/registry' +import type { AgentContext } from '@aries-framework/core' + +export interface RegisterSchemaOptions { + agentContext: AgentContext + schema: AnonCredsSchema + + // Identifier of issuer that will create the credential definition. + issuerId: string +} +export interface RegisterSchemaReturn { + schemaId: string +} + +export interface RegisterCredentialDefinitionOptions { + credentialDefinition: AnonCredsCredentialDefinition + + // Identifier of issuer that will create the credential definition. + issuerId: string +} + +export interface RegisterCredentialDefinitionReturn { + credentialDefinitionId: string +} + +export interface RegisterRevocationRegistryDefinitionOptions { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition +} + +export interface RegisterRevocationRegistryDefinitionReturn { + revocationRegistryDefinitionId: string +} + +export interface RegisterRevocationListOptions { + // Timestamp is often calculated by the ledger, otherwise method should just take current time + // Return type does include the timestamp. + revocationList: Omit +} + +export interface RegisterRevocationListReturn { + timestamp: string +} diff --git a/packages/anoncreds/src/services/AnonCredsVerifierService.ts b/packages/anoncreds/src/services/AnonCredsVerifierService.ts new file mode 100644 index 0000000000..ec68021817 --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsVerifierService.ts @@ -0,0 +1,7 @@ +import type { VerifyProofOptions } from './AnonCredsVerifierServiceOptions' + +export interface AnonCredsVerifierService { + // TODO: do we want to extend the return type with more info besides a boolean. + // If the value is false it would be nice to have some extra contexts about why it failed + verifyProof(options: VerifyProofOptions): Promise +} diff --git a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts new file mode 100644 index 0000000000..c67470fd55 --- /dev/null +++ b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts @@ -0,0 +1,31 @@ +import type { AnonCredsProof, AnonCredsProofRequest } from '../models/exchange' +import type { + AnonCredsCredentialDefinition, + AnonCredsRevocationList, + AnonCredsRevocationRegistryDefinition, + AnonCredsSchema, +} from '../models/registry' + +export interface VerifyProofOptions { + proofRequest: AnonCredsProofRequest + proof: AnonCredsProof + schemas: { + [schemaId: string]: AnonCredsSchema + } + credentialDefinitions: { + [credentialDefinitionId: string]: AnonCredsCredentialDefinition + } + revocationStates: { + [revocationRegistryDefinitionId: string]: { + definition: AnonCredsRevocationRegistryDefinition + // NOTE: the verifier only needs the accumulator, not the whole state of the revocation registry + // Requiring this to be the full state means we need to retrieve the full state from the ledger + // as a verifier. This is just following the data models from the AnonCreds spec, but for e.g. indy + // this means we need to retrieve _ALL_ deltas from the ledger to verify a proof. While currently we + // only need to fetch the registry. + revocationLists: { + [timestamp: string]: AnonCredsRevocationList + } + } + } +} diff --git a/packages/anoncreds/src/services/index.ts b/packages/anoncreds/src/services/index.ts new file mode 100644 index 0000000000..1a612359ed --- /dev/null +++ b/packages/anoncreds/src/services/index.ts @@ -0,0 +1,8 @@ +export * from './AnonCredsHolderService' +export * from './AnonCredsHolderServiceOptions' +export * from './AnonCredsIssuerService' +export * from './AnonCredsIssuerServiceOptions' +export * from './AnonCredsRegistry' +export * from './AnonCredsRegistryOptions' +export * from './AnonCredsVerifierService' +export * from './AnonCredsVerifierServiceOptions' diff --git a/packages/anoncreds/tsconfig.build.json b/packages/anoncreds/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/anoncreds/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/anoncreds/tsconfig.json b/packages/anoncreds/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/anoncreds/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} From 2f6ae143313b49e71265dc87e5bfd8caa4942127 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 04:33:49 +0100 Subject: [PATCH 002/139] build(deps): bump luxon from 1.28.0 to 1.28.1 (#1196) Bumps [luxon](https://github.com/moment/luxon) from 1.28.0 to 1.28.1. - [Release notes](https://github.com/moment/luxon/releases) - [Changelog](https://github.com/moment/luxon/blob/master/CHANGELOG.md) - [Commits](moment/luxon@1.28.0...1.28.1) --- updated-dependencies: - dependency-name: luxon dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3bd2eab136..e542fe1654 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7402,9 +7402,9 @@ lru_map@^0.4.1: integrity sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg== luxon@^1.27.0: - version "1.28.0" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" - integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== + version "1.28.1" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.1.tgz#528cdf3624a54506d710290a2341aa8e6e6c61b0" + integrity sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw== make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" From 59d1982f5cbfbea82199320fead61038e445d49c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 04:07:44 +0000 Subject: [PATCH 003/139] build(deps): bump json5 from 1.0.1 to 1.0.2 (#1191) --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index e542fe1654..d28283327d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7082,9 +7082,9 @@ json5@2.x, json5@^2.2.1: integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" @@ -7888,9 +7888,9 @@ minimist-options@4.1.0: kind-of "^6.0.3" minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== minipass-collect@^1.0.2: version "1.0.2" From fd006f262a91f901e7f8a9c6e6882ea178230005 Mon Sep 17 00:00:00 2001 From: Kim Ebert Date: Tue, 10 Jan 2023 03:55:48 -0700 Subject: [PATCH 004/139] feat: adding trust ping events and trust ping command (#1182) Signed-off-by: Kim Ebert --- .../src/modules/connections/ConnectionsApi.ts | 40 ++++++++++++++++ .../modules/connections/TrustPingEvents.ts | 24 ++++++++++ .../core/src/modules/connections/index.ts | 1 + .../connections/services/TrustPingService.ts | 30 +++++++++++- packages/core/tests/connections.test.ts | 21 ++++++++- packages/core/tests/helpers.ts | 46 +++++++++++++++++++ 6 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 packages/core/src/modules/connections/TrustPingEvents.ts diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index b437c46b4e..da895de772 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -32,6 +32,11 @@ import { HandshakeProtocol } from './models' import { ConnectionService } from './services/ConnectionService' import { TrustPingService } from './services/TrustPingService' +export interface SendPingOptions { + responseRequested?: boolean + withReturnRouting?: boolean +} + @injectable() export class ConnectionsApi { /** @@ -227,6 +232,41 @@ export class ConnectionsApi { return connectionRecord } + /** + * Send a trust ping to an established connection + * + * @param connectionId the id of the connection for which to accept the response + * @param responseRequested do we want a response to our ping + * @param withReturnRouting do we want a response at the time of posting + * @returns TurstPingMessage + */ + public async sendPing( + connectionId: string, + { responseRequested = true, withReturnRouting = undefined }: SendPingOptions + ) { + const connection = await this.getById(connectionId) + + const { message } = await this.connectionService.createTrustPing(this.agentContext, connection, { + responseRequested: responseRequested, + }) + + if (withReturnRouting === true) { + message.setReturnRouting(ReturnRouteTypes.all) + } + + // Disable return routing as we don't want to receive a response for this message over the same channel + // This has led to long timeouts as not all clients actually close an http socket if there is no response message + if (withReturnRouting === false) { + message.setReturnRouting(ReturnRouteTypes.none) + } + + await this.messageSender.sendMessage( + new OutboundMessageContext(message, { agentContext: this.agentContext, connection }) + ) + + return message + } + public async returnWhenIsConnected(connectionId: string, options?: { timeoutMs: number }): Promise { return this.connectionService.returnWhenIsConnected(this.agentContext, connectionId, options?.timeoutMs) } diff --git a/packages/core/src/modules/connections/TrustPingEvents.ts b/packages/core/src/modules/connections/TrustPingEvents.ts new file mode 100644 index 0000000000..55200e6c5b --- /dev/null +++ b/packages/core/src/modules/connections/TrustPingEvents.ts @@ -0,0 +1,24 @@ +import type { BaseEvent } from '../../agent/Events' +import type { TrustPingMessage, TrustPingResponseMessage } from './messages' +import type { ConnectionRecord } from './repository/ConnectionRecord' + +export enum TrustPingEventTypes { + TrustPingReceivedEvent = 'TrustPingReceivedEvent', + TrustPingResponseReceivedEvent = 'TrustPingResponseReceivedEvent', +} + +export interface TrustPingReceivedEvent extends BaseEvent { + type: typeof TrustPingEventTypes.TrustPingReceivedEvent + payload: { + connectionRecord: ConnectionRecord + message: TrustPingMessage + } +} + +export interface TrustPingResponseReceivedEvent extends BaseEvent { + type: typeof TrustPingEventTypes.TrustPingResponseReceivedEvent + payload: { + connectionRecord: ConnectionRecord + message: TrustPingResponseMessage + } +} diff --git a/packages/core/src/modules/connections/index.ts b/packages/core/src/modules/connections/index.ts index 52fe834617..e9dd5862d9 100644 --- a/packages/core/src/modules/connections/index.ts +++ b/packages/core/src/modules/connections/index.ts @@ -3,6 +3,7 @@ export * from './models' export * from './repository' export * from './services' export * from './ConnectionEvents' +export * from './TrustPingEvents' export * from './ConnectionsApi' export * from './DidExchangeProtocol' export * from './ConnectionsModuleConfig' diff --git a/packages/core/src/modules/connections/services/TrustPingService.ts b/packages/core/src/modules/connections/services/TrustPingService.ts index 17032e089e..5d4b10eb20 100644 --- a/packages/core/src/modules/connections/services/TrustPingService.ts +++ b/packages/core/src/modules/connections/services/TrustPingService.ts @@ -1,14 +1,31 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../TrustPingEvents' import type { TrustPingMessage } from '../messages' import type { ConnectionRecord } from '../repository/ConnectionRecord' +import { EventEmitter } from '../../../agent/EventEmitter' import { OutboundMessageContext } from '../../../agent/models' import { injectable } from '../../../plugins' +import { TrustPingEventTypes } from '../TrustPingEvents' import { TrustPingResponseMessage } from '../messages' @injectable() export class TrustPingService { + private eventEmitter: EventEmitter + + public constructor(eventEmitter: EventEmitter) { + this.eventEmitter = eventEmitter + } + public processPing({ message, agentContext }: InboundMessageContext, connection: ConnectionRecord) { + this.eventEmitter.emit(agentContext, { + type: TrustPingEventTypes.TrustPingReceivedEvent, + payload: { + connectionRecord: connection, + message: message, + }, + }) + if (message.responseRequested) { const response = new TrustPingResponseMessage({ threadId: message.id, @@ -18,8 +35,17 @@ export class TrustPingService { } } - // eslint-disable-next-line @typescript-eslint/no-unused-vars public processPingResponse(inboundMessage: InboundMessageContext) { - // TODO: handle ping response message + const { agentContext, message } = inboundMessage + + const connection = inboundMessage.assertReadyConnection() + + this.eventEmitter.emit(agentContext, { + type: TrustPingEventTypes.TrustPingResponseReceivedEvent, + payload: { + connectionRecord: connection, + message: message, + }, + }) } } diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 598a6d8fe0..24c03ad907 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -18,7 +18,7 @@ import { Agent } from '../src/agent/Agent' import { didKeyToVerkey } from '../src/modules/dids/helpers' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' -import { getAgentOptions } from './helpers' +import { getAgentOptions, waitForTrustPingResponseReceivedEvent } from './helpers' describe('connections', () => { let faberAgent: Agent @@ -85,6 +85,25 @@ describe('connections', () => { await mediatorAgent.wallet.delete() }) + it('one agent should be able to send and receive a ping', async () => { + const faberOutOfBandRecord = await faberAgent.oob.createInvitation({ + handshakeProtocols: [HandshakeProtocol.Connections], + multiUseInvitation: true, + }) + + const invitation = faberOutOfBandRecord.outOfBandInvitation + const invitationUrl = invitation.toUrl({ domain: 'https://example.com' }) + + // Receive invitation with alice agent + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveInvitationFromUrl(invitationUrl) + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + const ping = await aliceAgent.connections.sendPing(aliceFaberConnection.id, {}) + + await waitForTrustPingResponseReceivedEvent(aliceAgent, { threadId: ping.threadId }) + }) + it('one should be able to make multiple connections using a multi use invite', async () => { const faberOutOfBandRecord = await faberAgent.oob.createInvitation({ handshakeProtocols: [HandshakeProtocol.Connections], diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 70864471ff..7354fbe5a4 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -8,6 +8,7 @@ import type { ConnectionRecordProps, CredentialDefinitionTemplate, CredentialStateChangedEvent, + TrustPingResponseReceivedEvent, InitConfig, InjectionToken, ProofStateChangedEvent, @@ -45,6 +46,7 @@ import { ConnectionRecord, CredentialEventTypes, CredentialState, + TrustPingEventTypes, DependencyManager, DidExchangeRole, DidExchangeState, @@ -240,6 +242,50 @@ export function waitForProofExchangeRecordSubject( ) } +export async function waitForTrustPingResponseReceivedEvent( + agent: Agent, + options: { + threadId?: string + parentThreadId?: string + state?: ProofState + previousState?: ProofState | null + timeoutMs?: number + } +) { + const observable = agent.events.observable( + TrustPingEventTypes.TrustPingResponseReceivedEvent + ) + + return waitForTrustPingResponseReceivedEventSubject(observable, options) +} + +export function waitForTrustPingResponseReceivedEventSubject( + subject: ReplaySubject | Observable, + { + threadId, + timeoutMs = 10000, + }: { + threadId?: string + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( + observable.pipe( + filter((e) => threadId === undefined || e.payload.message.threadId === threadId), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `TrustPingResponseReceivedEvent event not emitted within specified timeout: { + threadId: ${threadId}, +}` + ) + }), + map((e) => e.payload.message) + ) + ) +} + export function waitForCredentialRecordSubject( subject: ReplaySubject | Observable, { From da7abdebeb6d5d9de5281a095d1f67e1c3e08e5b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 11 Jan 2023 00:54:57 +0800 Subject: [PATCH 005/139] chore: rename plugin to module (#1201) Signed-off-by: Timo Glastra --- packages/action-menu/README.md | 8 ++++---- packages/question-answer/README.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/action-menu/README.md b/packages/action-menu/README.md index ffd98caf55..c47c6a4ac7 100644 --- a/packages/action-menu/README.md +++ b/packages/action-menu/README.md @@ -6,7 +6,7 @@ height="250px" />

-

Aries Framework JavaScript Action Menu Plugin

+

Aries Framework JavaScript Action Menu Module


-Action Menu plugin for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). Implements [Aries RFC 0509](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0509-action-menu/README.md). +Action Menu module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). Implements [Aries RFC 0509](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0509-action-menu/README.md). ### Installation @@ -38,7 +38,7 @@ Make sure you have set up the correct version of Aries Framework JavaScript acco npm info "@aries-framework/action-menu" peerDependencies ``` -Then add the action-menu plugin to your project. +Then add the action-menu module to your project. ```sh yarn add @aries-framework/action-menu @@ -46,7 +46,7 @@ yarn add @aries-framework/action-menu ### Quick start -In order for this plugin to work, we have to inject it into the agent to access agent functionality. See the example for more information. +In order for this module to work, we have to inject it into the agent to access agent functionality. See the example for more information. ### Example of usage diff --git a/packages/question-answer/README.md b/packages/question-answer/README.md index 00ebca6637..33d1b17750 100644 --- a/packages/question-answer/README.md +++ b/packages/question-answer/README.md @@ -6,7 +6,7 @@ height="250px" />

-

Aries Framework JavaScript Question Answer Plugin

+

Aries Framework JavaScript Question Answer Module


-Question Answer plugin for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). Implements [Aries RFC 0113](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0113-question-answer/README.md). +Question Answer module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). Implements [Aries RFC 0113](https://github.com/hyperledger/aries-rfcs/blob/1795d5c2d36f664f88f5e8045042ace8e573808c/features/0113-question-answer/README.md). ### Installation @@ -38,7 +38,7 @@ Make sure you have set up the correct version of Aries Framework JavaScript acco npm info "@aries-framework/question-answer" peerDependencies ``` -Then add the question-answer plugin to your project. +Then add the question-answer module to your project. ```sh yarn add @aries-framework/question-answer @@ -46,7 +46,7 @@ yarn add @aries-framework/question-answer ### Quick start -In order for this plugin to work, we have to inject it into the agent to access agent functionality. See the example for more information. +In order for this module to work, we have to inject it into the agent to access agent functionality. See the example for more information. ### Example of usage From 9933b35a6aa4524caef8a885e71b742cd0d7186b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 11 Jan 2023 18:33:45 +0800 Subject: [PATCH 006/139] feat(indy-sdk): add indy-sdk package (#1200) Co-authored-by: Karim Stekelenburg Signed-off-by: Timo Glastra --- packages/action-menu/package.json | 7 +- packages/anoncreds/package.json | 6 +- packages/anoncreds/src/models/exchange.ts | 2 +- packages/anoncreds/src/models/internal.ts | 6 +- packages/anoncreds/src/models/registry.ts | 5 +- .../src/services/AnonCredsHolderService.ts | 11 +- .../services/AnonCredsHolderServiceOptions.ts | 14 +- .../services/AnonCredsIssuerServiceOptions.ts | 9 +- .../src/services/AnonCredsRegistry.ts | 41 - .../src/services/AnonCredsRegistryOptions.ts | 47 -- .../AnonCredsVerifierServiceOptions.ts | 2 +- packages/anoncreds/src/services/index.ts | 3 +- .../services/registry/AnonCredsRegistry.ts | 48 ++ .../registry/CredentialDefinitionOptions.ts | 45 ++ .../registry/RevocationListOptions.ts | 18 + .../RevocationRegistryDefinitionOptions.ts | 18 + .../src/services/registry/SchemaOptions.ts | 46 ++ .../anoncreds/src/services/registry/base.ts | 19 + .../anoncreds/src/services/registry/index.ts | 6 + packages/core/package.json | 2 +- packages/core/src/index.ts | 5 +- .../IndyCredentialFormatService.test.ts | 6 +- ...v2.ldproof.credentials-auto-accept.test.ts | 25 +- .../indy/services/IndyRevocationService.ts | 4 +- .../indy/services/IndyVerifierService.ts | 16 +- .../ledger/__tests__/LedgerApi.test.ts | 6 +- .../core/src/modules/ledger/error/index.ts | 3 + .../modules/routing/__tests__/pickup.test.ts | 34 +- .../modules/vc/__tests__/documentLoader.ts | 30 +- packages/core/src/utils/validators.ts | 2 +- packages/core/tests/helpers.ts | 52 +- packages/indy-sdk/README.md | 31 + packages/indy-sdk/jest.config.ts | 14 + packages/indy-sdk/package.json | 40 + packages/indy-sdk/src/IndySdkModule.ts | 17 + packages/indy-sdk/src/IndySdkModuleConfig.ts | 45 ++ packages/indy-sdk/src/anoncreds/index.ts | 4 + .../services/IndySdkAnonCredsRegistry.ts | 514 ++++++++++++ .../services/IndySdkHolderService.ts | 327 ++++++++ .../services/IndySdkIssuerService.ts | 129 +++ .../services/IndySdkIssuerServiceMetadata.ts | 3 + .../services/IndySdkRevocationService.ts | 177 ++++ .../services/IndySdkUtilitiesService.ts | 65 ++ .../services/IndySdkVerifierService.ts | 86 ++ .../utils/__tests__/identifiers.test.ts | 48 ++ .../utils/__tests__/transform.test.ts | 114 +++ .../src/anoncreds/utils/identifiers.ts | 41 + .../indy-sdk/src/anoncreds/utils/transform.ts | 153 ++++ .../src/dids/IndySdkSovDidRegistrar.ts | 265 ++++++ .../src/dids/IndySdkSovDidResolver.ts | 95 +++ packages/indy-sdk/src/dids/didSovUtil.ts | 132 +++ packages/indy-sdk/src/dids/index.ts | 7 + packages/indy-sdk/src/error/IndySdkError.ts | 11 + packages/indy-sdk/src/error/index.ts | 2 + packages/indy-sdk/src/error/indyError.ts | 100 +++ packages/indy-sdk/src/index.ts | 26 + packages/indy-sdk/src/ledger/IndySdkPool.ts | 208 +++++ .../indy-sdk/src/ledger/IndySdkPoolService.ts | 338 ++++++++ .../__tests__/IndySdkPoolService.test.ts | 444 ++++++++++ .../src/ledger/__tests__/util.test.ts | 45 ++ .../src/ledger/error/IndySdkPoolError.ts | 7 + .../error/IndySdkPoolNotConfiguredError.ts | 7 + .../ledger/error/IndySdkPoolNotFoundError.ts | 7 + packages/indy-sdk/src/ledger/error/index.ts | 3 + packages/indy-sdk/src/ledger/index.ts | 2 + packages/indy-sdk/src/ledger/util.ts | 9 + .../src/storage/IndySdkStorageService.ts | 324 ++++++++ .../__tests__/IndySdkStorageService.test.ts | 297 +++++++ packages/indy-sdk/src/storage/index.ts | 1 + packages/indy-sdk/src/types.ts | 6 + .../indy-sdk/src/utils/__tests__/did.test.ts | 73 ++ .../indy-sdk/src/utils/assertIndySdkWallet.ts | 13 + packages/indy-sdk/src/utils/did.ts | 89 ++ packages/indy-sdk/src/utils/promises.ts | 44 + packages/indy-sdk/src/wallet/IndySdkWallet.ts | 686 ++++++++++++++++ .../wallet/__tests__/IndySdkWallet.test.ts | 125 +++ packages/indy-sdk/src/wallet/index.ts | 1 + packages/indy-sdk/tsconfig.build.json | 7 + packages/indy-sdk/tsconfig.json | 6 + packages/question-answer/package.json | 6 +- packages/react-native/package.json | 2 +- tests/transport/SubjectOutboundTransport.ts | 2 +- yarn.lock | 761 +++++++++--------- 83 files changed, 5940 insertions(+), 557 deletions(-) delete mode 100644 packages/anoncreds/src/services/AnonCredsRegistry.ts delete mode 100644 packages/anoncreds/src/services/AnonCredsRegistryOptions.ts create mode 100644 packages/anoncreds/src/services/registry/AnonCredsRegistry.ts create mode 100644 packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts create mode 100644 packages/anoncreds/src/services/registry/RevocationListOptions.ts create mode 100644 packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts create mode 100644 packages/anoncreds/src/services/registry/SchemaOptions.ts create mode 100644 packages/anoncreds/src/services/registry/base.ts create mode 100644 packages/anoncreds/src/services/registry/index.ts create mode 100644 packages/core/src/modules/ledger/error/index.ts create mode 100644 packages/indy-sdk/README.md create mode 100644 packages/indy-sdk/jest.config.ts create mode 100644 packages/indy-sdk/package.json create mode 100644 packages/indy-sdk/src/IndySdkModule.ts create mode 100644 packages/indy-sdk/src/IndySdkModuleConfig.ts create mode 100644 packages/indy-sdk/src/anoncreds/index.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkIssuerServiceMetadata.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts create mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/identifiers.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/transform.ts create mode 100644 packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts create mode 100644 packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts create mode 100644 packages/indy-sdk/src/dids/didSovUtil.ts create mode 100644 packages/indy-sdk/src/dids/index.ts create mode 100644 packages/indy-sdk/src/error/IndySdkError.ts create mode 100644 packages/indy-sdk/src/error/index.ts create mode 100644 packages/indy-sdk/src/error/indyError.ts create mode 100644 packages/indy-sdk/src/index.ts create mode 100644 packages/indy-sdk/src/ledger/IndySdkPool.ts create mode 100644 packages/indy-sdk/src/ledger/IndySdkPoolService.ts create mode 100644 packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts create mode 100644 packages/indy-sdk/src/ledger/__tests__/util.test.ts create mode 100644 packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts create mode 100644 packages/indy-sdk/src/ledger/error/IndySdkPoolNotConfiguredError.ts create mode 100644 packages/indy-sdk/src/ledger/error/IndySdkPoolNotFoundError.ts create mode 100644 packages/indy-sdk/src/ledger/error/index.ts create mode 100644 packages/indy-sdk/src/ledger/index.ts create mode 100644 packages/indy-sdk/src/ledger/util.ts create mode 100644 packages/indy-sdk/src/storage/IndySdkStorageService.ts create mode 100644 packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts create mode 100644 packages/indy-sdk/src/storage/index.ts create mode 100644 packages/indy-sdk/src/types.ts create mode 100644 packages/indy-sdk/src/utils/__tests__/did.test.ts create mode 100644 packages/indy-sdk/src/utils/assertIndySdkWallet.ts create mode 100644 packages/indy-sdk/src/utils/did.ts create mode 100644 packages/indy-sdk/src/utils/promises.ts create mode 100644 packages/indy-sdk/src/wallet/IndySdkWallet.ts create mode 100644 packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts create mode 100644 packages/indy-sdk/src/wallet/index.ts create mode 100644 packages/indy-sdk/tsconfig.build.json create mode 100644 packages/indy-sdk/tsconfig.json diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index cedf2db5ef..7537700f4c 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -26,13 +26,10 @@ "dependencies": { "class-transformer": "0.5.1", "class-validator": "0.13.1", - "rxjs": "^7.2.0" - }, - "peerDependencies": { - "@aries-framework/core": "0.2.5" + "rxjs": "^7.2.0", + "@aries-framework/core": "0.3.2" }, "devDependencies": { - "@aries-framework/node": "0.3.2", "reflect-metadata": "^0.1.13", "rimraf": "~3.0.2", "typescript": "~4.3.0" diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 29946c1b20..25033d94a2 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.2.5", + "version": "0.3.2", "files": [ "build" ], @@ -25,10 +25,10 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "*" + "@aries-framework/core": "0.3.2" }, - "peerDependencies": {}, "devDependencies": { + "rimraf": "~3.0.2", "typescript": "~4.3.0" } } diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index 420c26a158..bd30979a86 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -90,7 +90,7 @@ export interface AnonCredsProofRequest { string, { name?: string - names?: string + names?: string[] restrictions?: WalletQuery[] non_revoked?: NonRevokedInterval } diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index f0b64d614c..c838dcf865 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -5,7 +5,7 @@ export interface CredentialInfo { } schemaId: string credentialDefinitionId: string - revocationRegistryId?: number | undefined + revocationRegistryId?: string | undefined credentialRevocationId?: string | undefined } @@ -13,14 +13,14 @@ export interface RequestedAttribute { credentialId: string timestamp?: number revealed: boolean - credentialInfo?: CredentialInfo + credentialInfo: CredentialInfo revoked?: boolean } export interface RequestedPredicate { credentialId: string timestamp?: number - credentialInfo?: CredentialInfo + credentialInfo: CredentialInfo revoked?: boolean } diff --git a/packages/anoncreds/src/models/registry.ts b/packages/anoncreds/src/models/registry.ts index 98268e1e84..1e5e6d7879 100644 --- a/packages/anoncreds/src/models/registry.ts +++ b/packages/anoncreds/src/models/registry.ts @@ -1,10 +1,12 @@ export interface AnonCredsSchema { + issuerId: string name: string version: string attrNames: string[] } export interface AnonCredsCredentialDefinition { + issuerId: string schemaId: string type: 'CL' tag: string @@ -16,6 +18,7 @@ export interface AnonCredsCredentialDefinition { } export interface AnonCredsRevocationRegistryDefinition { + issuerId: string type: 'CL_ACCUM' credDefId: string tag: string @@ -30,7 +33,7 @@ export interface AnonCredsRevocationRegistryDefinition { } export interface AnonCredsRevocationList { - // TODO: this is a new property, but do we keep abbreviation or not? + issuerId: string revRegId: string revocationList: number[] currentAccumulator: string diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts index 62c7a49aa4..3e287bde00 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderService.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -12,11 +12,7 @@ import type { import type { AgentContext } from '@aries-framework/core' export interface AnonCredsHolderService { - createProof( - agentContext: AgentContext, - options: CreateProofOptions, - metadata?: Record - ): Promise + createProof(agentContext: AgentContext, options: CreateProofOptions): Promise storeCredential( agentContext: AgentContext, options: StoreCredentialOptions, @@ -28,13 +24,12 @@ export interface AnonCredsHolderService { createCredentialRequest( agentContext: AgentContext, - options: CreateCredentialRequestOptions, - metadata?: Record + options: CreateCredentialRequestOptions ): Promise deleteCredential(agentContext: AgentContext, credentialId: string): Promise getCredentialsForProofRequest( agentContext: AgentContext, options: GetCredentialsForProofRequestOptions - ): Promise + ): Promise } diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index 86cb8bbcf9..3de66df703 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -28,8 +28,10 @@ export interface CreateProofOptions { credentialDefinitions: { [credentialDefinitionId: string]: AnonCredsCredentialDefinition } - revocationStates: { + revocationRegistries: { [revocationRegistryDefinitionId: string]: { + // tails file MUST already be downloaded on a higher level and stored + tailsFilePath: string definition: AnonCredsRevocationRegistryDefinition revocationLists: { [timestamp: string]: AnonCredsRevocationList @@ -45,8 +47,10 @@ export interface StoreCredentialOptions { credentialDefinition: AnonCredsCredentialDefinition credentialDefinitionId: string credentialId?: string - revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition - revocationRegistryDefinitionId: string + revocationRegistry?: { + id: string + definition: AnonCredsRevocationRegistryDefinition + } } export interface GetCredentialOptions { @@ -61,10 +65,10 @@ export interface GetCredentialsForProofRequestOptions { extraQuery?: ReferentWalletQuery } -export interface GetCredentialsForProofRequestReturn { +export type GetCredentialsForProofRequestReturn = Array<{ credentialInfo: CredentialInfo interval?: NonRevokedInterval -} +}> export interface CreateCredentialRequestOptions { // TODO: Why is this needed? It is just used as context in Ursa, can be any string. Should we remove it? diff --git a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts index 27f5450d2f..e3bb8dcdfb 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts @@ -15,14 +15,11 @@ export interface CreateSchemaOptions { export interface CreateCredentialDefinitionOptions { issuerId: string - tag: string // TODO: this was initially defined as optional, is that the case? + tag: string supportRevocation?: boolean - // If schema doesn't include the id, we need to add it as a separate input parameter - schema: { - value: AnonCredsSchema - id: string - } + schemaId: string + schema: AnonCredsSchema } export interface CreateCredentialOfferOptions { diff --git a/packages/anoncreds/src/services/AnonCredsRegistry.ts b/packages/anoncreds/src/services/AnonCredsRegistry.ts deleted file mode 100644 index ead8f8aa70..0000000000 --- a/packages/anoncreds/src/services/AnonCredsRegistry.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { - AnonCredsCredentialDefinition, - AnonCredsRevocationList, - AnonCredsRevocationRegistryDefinition, - AnonCredsSchema, -} from '../models/registry' -import type { - RegisterCredentialDefinitionOptions, - RegisterCredentialDefinitionReturn, - RegisterRevocationListOptions, - RegisterRevocationListReturn, - RegisterRevocationRegistryDefinitionOptions, - RegisterRevocationRegistryDefinitionReturn, - RegisterSchemaOptions, - RegisterSchemaReturn, -} from './AnonCredsRegistryOptions' -import type { AgentContext } from '@aries-framework/core' - -// This service can be registered multiple times in a single AFJ instance. -// TODO: should all methods have interfaces as input for consistency? -export interface AnonCredsRegistry { - getSchema(agentContext: AgentContext, schemaId: string): Promise - registerSchema(options: RegisterSchemaOptions): Promise - - getCredentialDefinition(credentialDefinitionId: string): Promise - registerCredentialDefinition( - options: RegisterCredentialDefinitionOptions - ): Promise - - getRevocationRegistryDefinition( - revocationRegistryDefinitionId: string - ): Promise - registerRevocationRegistryDefinition( - options: RegisterRevocationRegistryDefinitionOptions - ): Promise - - // TODO: The name of this data model is still tbd. - getRevocationList(revocationRegistryId: string, timestamp: string): Promise - - registerRevocationList(options: RegisterRevocationListOptions): Promise -} diff --git a/packages/anoncreds/src/services/AnonCredsRegistryOptions.ts b/packages/anoncreds/src/services/AnonCredsRegistryOptions.ts deleted file mode 100644 index 206d56fc4d..0000000000 --- a/packages/anoncreds/src/services/AnonCredsRegistryOptions.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { - AnonCredsCredentialDefinition, - AnonCredsRevocationList, - AnonCredsRevocationRegistryDefinition, - AnonCredsSchema, -} from '../models/registry' -import type { AgentContext } from '@aries-framework/core' - -export interface RegisterSchemaOptions { - agentContext: AgentContext - schema: AnonCredsSchema - - // Identifier of issuer that will create the credential definition. - issuerId: string -} -export interface RegisterSchemaReturn { - schemaId: string -} - -export interface RegisterCredentialDefinitionOptions { - credentialDefinition: AnonCredsCredentialDefinition - - // Identifier of issuer that will create the credential definition. - issuerId: string -} - -export interface RegisterCredentialDefinitionReturn { - credentialDefinitionId: string -} - -export interface RegisterRevocationRegistryDefinitionOptions { - revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition -} - -export interface RegisterRevocationRegistryDefinitionReturn { - revocationRegistryDefinitionId: string -} - -export interface RegisterRevocationListOptions { - // Timestamp is often calculated by the ledger, otherwise method should just take current time - // Return type does include the timestamp. - revocationList: Omit -} - -export interface RegisterRevocationListReturn { - timestamp: string -} diff --git a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts index c67470fd55..f3ecb3b70c 100644 --- a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts @@ -24,7 +24,7 @@ export interface VerifyProofOptions { // this means we need to retrieve _ALL_ deltas from the ledger to verify a proof. While currently we // only need to fetch the registry. revocationLists: { - [timestamp: string]: AnonCredsRevocationList + [timestamp: number]: AnonCredsRevocationList } } } diff --git a/packages/anoncreds/src/services/index.ts b/packages/anoncreds/src/services/index.ts index 1a612359ed..fe7b176754 100644 --- a/packages/anoncreds/src/services/index.ts +++ b/packages/anoncreds/src/services/index.ts @@ -2,7 +2,6 @@ export * from './AnonCredsHolderService' export * from './AnonCredsHolderServiceOptions' export * from './AnonCredsIssuerService' export * from './AnonCredsIssuerServiceOptions' -export * from './AnonCredsRegistry' -export * from './AnonCredsRegistryOptions' +export * from './registry' export * from './AnonCredsVerifierService' export * from './AnonCredsVerifierServiceOptions' diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts new file mode 100644 index 0000000000..966a1afb95 --- /dev/null +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts @@ -0,0 +1,48 @@ +import type { + GetCredentialDefinitionReturn, + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, +} from './CredentialDefinitionOptions' +import type { GetRevocationListReturn } from './RevocationListOptions' +import type { GetRevocationRegistryDefinitionReturn } from './RevocationRegistryDefinitionOptions' +import type { GetSchemaReturn, RegisterSchemaOptions, RegisterSchemaReturn } from './SchemaOptions' +import type { AgentContext } from '@aries-framework/core' + +// This service can be registered multiple times in a single AFJ instance. +export interface AnonCredsRegistry { + getSchema(agentContext: AgentContext, schemaId: string): Promise + registerSchema(agentContext: AgentContext, options: RegisterSchemaOptions): Promise + + getCredentialDefinition( + agentContext: AgentContext, + credentialDefinitionId: string + ): Promise + registerCredentialDefinition( + agentContext: AgentContext, + options: RegisterCredentialDefinitionOptions + ): Promise + + getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise + + // TODO: issuance of revocable credentials + // registerRevocationRegistryDefinition( + // agentContext: AgentContext, + // options: RegisterRevocationRegistryDefinitionOptions + // ): Promise + + // TODO: The name of this data model is still tbd. + getRevocationList( + agentContext: AgentContext, + revocationRegistryId: string, + timestamp: number + ): Promise + + // TODO: issuance of revocable credentials + // registerRevocationList( + // agentContext: AgentContext, + // options: RegisterRevocationListOptions + // ): Promise +} diff --git a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts new file mode 100644 index 0000000000..e2f7e14298 --- /dev/null +++ b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts @@ -0,0 +1,45 @@ +import type { AnonCredsCredentialDefinition } from '../../models/registry' +import type { + AnonCredsResolutionMetadata, + Extensible, + AnonCredsOperationStateFailed, + AnonCredsOperationStateFinished, + AnonCredsOperationState, +} from './base' + +export interface GetCredentialDefinitionReturn { + credentialDefinition: AnonCredsCredentialDefinition | null + credentialDefinitionId: string + resolutionMetadata: AnonCredsResolutionMetadata + credentialDefinitionMetadata: Extensible +} + +export interface RegisterCredentialDefinitionOptions { + credentialDefinition: AnonCredsCredentialDefinition + options: Extensible +} + +export interface RegisterCredentialDefinitionReturnStateFailed extends AnonCredsOperationStateFailed { + credentialDefinition: AnonCredsCredentialDefinition + credentialDefinitionId?: string +} + +export interface RegisterCredentialDefinitionReturnStateFinished extends AnonCredsOperationStateFinished { + credentialDefinition: AnonCredsCredentialDefinition + credentialDefinitionId?: string +} + +export interface RegisterCredentialDefinitionReturnState extends AnonCredsOperationState { + credentialDefinition: AnonCredsCredentialDefinition + credentialDefinitionId?: string +} + +export interface RegisterCredentialDefinitionReturn { + jobId?: string + credentialDefinitionState: + | RegisterCredentialDefinitionReturnState + | RegisterCredentialDefinitionReturnStateFinished + | RegisterCredentialDefinitionReturnStateFailed + credentialDefinitionMetadata: Extensible + registrationMetadata: AnonCredsResolutionMetadata +} diff --git a/packages/anoncreds/src/services/registry/RevocationListOptions.ts b/packages/anoncreds/src/services/registry/RevocationListOptions.ts new file mode 100644 index 0000000000..aea0c5d5b9 --- /dev/null +++ b/packages/anoncreds/src/services/registry/RevocationListOptions.ts @@ -0,0 +1,18 @@ +import type { AnonCredsRevocationList } from '../../models/registry' +import type { AnonCredsResolutionMetadata, Extensible } from './base' + +export interface GetRevocationListReturn { + revocationList: AnonCredsRevocationList | null + resolutionMetadata: AnonCredsResolutionMetadata + revocationListMetadata: Extensible +} + +// TODO: Support for issuance of revocable credentials +// export interface RegisterRevocationListOptions { +// // Timestamp is often calculated by the ledger, otherwise method should just take current time +// // Return type does include the timestamp. +// revocationList: Omit +// } +// export interface RegisterRevocationListReturn { +// timestamp: string +// } diff --git a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts new file mode 100644 index 0000000000..5e63f79995 --- /dev/null +++ b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts @@ -0,0 +1,18 @@ +import type { AnonCredsRevocationRegistryDefinition } from '../../models/registry' +import type { AnonCredsResolutionMetadata, Extensible } from './base' + +export interface GetRevocationRegistryDefinitionReturn { + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition | null + revocationRegistryDefinitionId: string + resolutionMetadata: AnonCredsResolutionMetadata + revocationRegistryDefinitionMetadata: Extensible +} + +// TODO: Support for issuance of revocable credentials +// export interface RegisterRevocationRegistryDefinitionOptions { +// revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition +// } + +// export interface RegisterRevocationRegistryDefinitionReturn { +// revocationRegistryDefinitionId: string +// } diff --git a/packages/anoncreds/src/services/registry/SchemaOptions.ts b/packages/anoncreds/src/services/registry/SchemaOptions.ts new file mode 100644 index 0000000000..f4d706223a --- /dev/null +++ b/packages/anoncreds/src/services/registry/SchemaOptions.ts @@ -0,0 +1,46 @@ +import type { AnonCredsSchema } from '../../models/registry' +import type { + AnonCredsResolutionMetadata, + Extensible, + AnonCredsOperationStateFailed, + AnonCredsOperationStateFinished, + AnonCredsOperationState, +} from './base' + +// Get Schema +export interface GetSchemaReturn { + schema: AnonCredsSchema | null + schemaId: string + // Can contain e.g. the ledger transaction request/response + resolutionMetadata: AnonCredsResolutionMetadata + // Can contain additional fields + schemaMetadata: Extensible +} + +// +export interface RegisterSchemaOptions { + schema: AnonCredsSchema + options: Extensible +} + +export interface RegisterSchemaReturnStateFailed extends AnonCredsOperationStateFailed { + schema: AnonCredsSchema + schemaId?: string +} + +export interface RegisterSchemaReturnStateFinished extends AnonCredsOperationStateFinished { + schema: AnonCredsSchema + schemaId: string +} + +export interface RegisterSchemaReturnState extends AnonCredsOperationState { + schema: AnonCredsSchema + schemaId?: string +} + +export interface RegisterSchemaReturn { + jobId?: string + schemaState: RegisterSchemaReturnState | RegisterSchemaReturnStateFinished | RegisterSchemaReturnStateFailed + schemaMetadata: Extensible + registrationMetadata: Extensible +} diff --git a/packages/anoncreds/src/services/registry/base.ts b/packages/anoncreds/src/services/registry/base.ts new file mode 100644 index 0000000000..af9b52ee43 --- /dev/null +++ b/packages/anoncreds/src/services/registry/base.ts @@ -0,0 +1,19 @@ +export type Extensible = Record + +export interface AnonCredsOperationState { + state: 'action' | 'wait' +} + +export interface AnonCredsOperationStateFinished { + state: 'finished' +} + +export interface AnonCredsOperationStateFailed { + state: 'failed' + reason: string +} + +export interface AnonCredsResolutionMetadata extends Extensible { + error?: 'invalid' | 'notFound' | 'unsupportedAnonCredsMethod' | string + message?: string +} diff --git a/packages/anoncreds/src/services/registry/index.ts b/packages/anoncreds/src/services/registry/index.ts new file mode 100644 index 0000000000..5d36ce3dd9 --- /dev/null +++ b/packages/anoncreds/src/services/registry/index.ts @@ -0,0 +1,6 @@ +export * from './AnonCredsRegistry' +export * from './CredentialDefinitionOptions' +export * from './SchemaOptions' +export * from './RevocationRegistryDefinitionOptions' +export * from './RevocationListOptions' +export { AnonCredsResolutionMetadata } from './base' diff --git a/packages/core/package.json b/packages/core/package.json index 3325039d25..cd0e119a36 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,7 +30,7 @@ "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", - "@types/indy-sdk": "^1.16.21", + "@types/indy-sdk": "1.16.24", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.6", "abort-controller": "^3.0.0", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f551cb8d59..5b2eaf1762 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -24,6 +24,8 @@ export type { JsonArray, JsonObject, JsonValue, + WalletConfigRekey, + WalletExportImportConfig, } from './types' export { DidCommMimeType, KeyDerivationMethod } from './types' export type { FileSystem } from './storage/FileSystem' @@ -31,7 +33,7 @@ export * from './storage/BaseRecord' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' export * from './storage/RepositoryEvents' -export { StorageService, Query } from './storage/StorageService' +export { StorageService, Query, BaseRecordConstructor } from './storage/StorageService' export * from './storage/migration' export { getDirFromFilePath } from './utils/path' export { InjectionSymbols } from './constants' @@ -63,6 +65,7 @@ export { parseMessageType, IsValidMessageType } from './utils/messageType' export type { Constructor } from './utils/mixins' export * from './agent/Events' export * from './crypto/' +export { PersistedLruCache, CacheRepository } from './cache' import { parseInvitationUrl } from './utils/parseInvitation' import { uuid } from './utils/uuid' diff --git a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts index ebc32c4785..90b6db58c0 100644 --- a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts @@ -67,7 +67,11 @@ const revDef: RevocRegDef = { maxCredNum: 33, tailsHash: 'd', tailsLocation: 'x', - publicKeys: ['x'], + publicKeys: { + accumKey: { + z: 'x', + }, + }, }, ver: 't', } diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index 3870b22bb3..a9bf76f0f5 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -310,7 +310,17 @@ describe('credentials', () => { const aliceExchangeCredentialRecord = await aliceAgent.credentials.negotiateOffer({ credentialRecordId: aliceCredentialRecord.id, credentialFormats: { - jsonld: signCredentialOptions, + // Send a different object + jsonld: { + ...signCredentialOptions, + credential: { + ...signCredentialOptions.credential, + credentialSubject: { + ...signCredentialOptions.credential.credentialSubject, + name: 'Different Property', + }, + }, + }, }, comment: 'v2 propose credential test', }) @@ -328,6 +338,7 @@ describe('credentials', () => { aliceCredentialRecord = await aliceAgent.credentials.getById(aliceCredentialRecord.id) aliceCredentialRecord.assertState(CredentialState.ProposalSent) }) + test('Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { testLogger.test('Alice sends credential proposal to Faber') const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ @@ -348,7 +359,17 @@ describe('credentials', () => { await faberAgent.credentials.negotiateProposal({ credentialRecordId: faberCredentialRecord.id, credentialFormats: { - jsonld: signCredentialOptions, + // Send a different object + jsonld: { + ...signCredentialOptions, + credential: { + ...signCredentialOptions.credential, + credentialSubject: { + ...signCredentialOptions.credential.credentialSubject, + name: 'Different Property', + }, + }, + }, }, }) diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts index 52c666d3ca..4dd89c2e4d 100644 --- a/packages/core/src/modules/indy/services/IndyRevocationService.ts +++ b/packages/core/src/modules/indy/services/IndyRevocationService.ts @@ -1,7 +1,7 @@ import type { AgentContext } from '../../../agent' import type { IndyRevocationInterval } from '../../credentials' import type { RequestedCredentials } from '../../proofs/formats/indy/models/RequestedCredentials' -import type { default as Indy } from 'indy-sdk' +import type { default as Indy, RevState, RevStates } from 'indy-sdk' import { AgentDependencies } from '../../../agent/AgentDependencies' import { InjectionSymbols } from '../../../constants' @@ -48,7 +48,7 @@ export class IndyRevocationService { proofRequest, requestedCredentials, }) - const revocationStates: Indy.RevStates = {} + const revocationStates: RevStates = {} const referentCredentials = [] //Retrieve information for referents and push to single array diff --git a/packages/core/src/modules/indy/services/IndyVerifierService.ts b/packages/core/src/modules/indy/services/IndyVerifierService.ts index b6cc387c31..c6ad15bb77 100644 --- a/packages/core/src/modules/indy/services/IndyVerifierService.ts +++ b/packages/core/src/modules/indy/services/IndyVerifierService.ts @@ -26,7 +26,7 @@ export class IndyVerifierService { { proofRequest, proof, schemas, credentialDefinitions }: VerifyProofOptions ): Promise { try { - const { revocationRegistryDefinitions, revocationRegistryStates } = await this.getRevocationRegistries( + const { revocationRegistryDefinitions, revocationRegistries } = await this.getRevocationRegistries( agentContext, proof ) @@ -37,7 +37,7 @@ export class IndyVerifierService { schemas, credentialDefinitions, revocationRegistryDefinitions, - revocationRegistryStates + revocationRegistries ) } catch (error) { throw isIndyError(error) ? new IndySdkError(error) : error @@ -46,7 +46,7 @@ export class IndyVerifierService { private async getRevocationRegistries(agentContext: AgentContext, proof: Indy.IndyProof) { const revocationRegistryDefinitions: Indy.RevocRegDefs = {} - const revocationRegistryStates: Indy.RevStates = Object.create(null) + const revocationRegistries: Indy.RevRegs = Object.create(null) for (const identifier of proof.identifiers) { const revocationRegistryId = identifier.rev_reg_id const timestamp = identifier.timestamp @@ -61,19 +61,19 @@ export class IndyVerifierService { } //Fetch Revocation Registry by Timestamp if not already fetched - if (revocationRegistryId && timestamp && !revocationRegistryStates[revocationRegistryId]?.[timestamp]) { - if (!revocationRegistryStates[revocationRegistryId]) { - revocationRegistryStates[revocationRegistryId] = Object.create(null) + if (revocationRegistryId && timestamp && !revocationRegistries[revocationRegistryId]?.[timestamp]) { + if (!revocationRegistries[revocationRegistryId]) { + revocationRegistries[revocationRegistryId] = Object.create(null) } const { revocationRegistry } = await this.ledgerService.getRevocationRegistry( agentContext, revocationRegistryId, timestamp ) - revocationRegistryStates[revocationRegistryId][timestamp] = revocationRegistry + revocationRegistries[revocationRegistryId][timestamp] = revocationRegistry } } - return { revocationRegistryDefinitions, revocationRegistryStates } + return { revocationRegistryDefinitions, revocationRegistries } } } diff --git a/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts b/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts index 5ca80f5fcd..cf7b35bbc5 100644 --- a/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts +++ b/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts @@ -77,7 +77,11 @@ const revocRegDef: Indy.RevocRegDef = { maxCredNum: 3, tailsHash: 'abcde', tailsLocation: 'xyz', - publicKeys: ['abcde', 'fghijk'], + publicKeys: { + accumKey: { + z: 'z', + }, + }, }, ver: 'abcde', } diff --git a/packages/core/src/modules/ledger/error/index.ts b/packages/core/src/modules/ledger/error/index.ts new file mode 100644 index 0000000000..79c42fc2b6 --- /dev/null +++ b/packages/core/src/modules/ledger/error/index.ts @@ -0,0 +1,3 @@ +export * from './LedgerError' +export * from './LedgerNotConfiguredError' +export * from './LedgerNotFoundError' diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts b/packages/core/src/modules/routing/__tests__/pickup.test.ts index 69d43e8c46..8b33fcacf2 100644 --- a/packages/core/src/modules/routing/__tests__/pickup.test.ts +++ b/packages/core/src/modules/routing/__tests__/pickup.test.ts @@ -5,7 +5,7 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' -import { getAgentOptions, waitForBasicMessage } from '../../../../tests/helpers' +import { getAgentOptions, waitForBasicMessage, waitForTrustPingReceivedEvent } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' @@ -16,7 +16,7 @@ const recipientOptions = getAgentOptions('Mediation: Recipient Pickup', { }) const mediatorOptions = getAgentOptions('Mediation: Mediator Pickup', { autoAcceptConnections: true, - endpoints: ['rxjs:mediator'], + endpoints: ['wss://mediator'], indyLedgers: [], }) @@ -25,17 +25,17 @@ describe('E2E Pick Up protocol', () => { let mediatorAgent: Agent afterEach(async () => { - await recipientAgent?.shutdown() - await recipientAgent?.wallet.delete() - await mediatorAgent?.shutdown() - await mediatorAgent?.wallet.delete() + await recipientAgent.shutdown() + await recipientAgent.wallet.delete() + await mediatorAgent.shutdown() + await mediatorAgent.wallet.delete() }) test('E2E Pick Up V1 protocol', async () => { const mediatorMessages = new Subject() const subjectMap = { - 'rxjs:mediator': mediatorMessages, + 'wss://mediator': mediatorMessages, } // Initialize mediatorReceived message @@ -74,7 +74,7 @@ describe('E2E Pick Up protocol', () => { const message = 'hello pickup V1' await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) - await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection) + await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection, MediatorPickupStrategy.PickUpV1) const basicMessage = await waitForBasicMessage(recipientAgent, { content: message, @@ -86,8 +86,12 @@ describe('E2E Pick Up protocol', () => { test('E2E Pick Up V2 protocol', async () => { const mediatorMessages = new Subject() + // FIXME: we harcoded that pickup of messages MUST be using ws(s) scheme when doing implicit pickup + // For liver delivery we need a duplex transport. however that means we can't test it with the subject transport. Using wss here to 'hack' this. We should + // extend the API to allow custom schemes (or maybe add a `supportsDuplex` transport / `supportMultiReturnMessages`) + // For pickup v2 pickup message (which we're testing here) we could just as well use `http` as it is just request/response. const subjectMap = { - 'rxjs:mediator': mediatorMessages, + 'wss://mediator': mediatorMessages, } // Initialize mediatorReceived message @@ -124,14 +128,20 @@ describe('E2E Pick Up protocol', () => { mediatorRecipientConnection = await mediatorAgent.connections.returnWhenIsConnected(mediatorRecipientConnection!.id) const message = 'hello pickup V2' - await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) - await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection, MediatorPickupStrategy.PickUpV2) + await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) - const basicMessage = await waitForBasicMessage(recipientAgent, { + const basicMessagePromise = waitForBasicMessage(recipientAgent, { content: message, }) + const trustPingPromise = waitForTrustPingReceivedEvent(mediatorAgent, {}) + await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection, MediatorPickupStrategy.PickUpV2) + const basicMessage = await basicMessagePromise expect(basicMessage.content).toBe(message) + + // Wait for trust ping to be received and stop message pickup + await trustPingPromise + await recipientAgent.mediationRecipient.stopMessagePickup() }) }) diff --git a/packages/core/src/modules/vc/__tests__/documentLoader.ts b/packages/core/src/modules/vc/__tests__/documentLoader.ts index 29c47d7d91..544ae02972 100644 --- a/packages/core/src/modules/vc/__tests__/documentLoader.ts +++ b/packages/core/src/modules/vc/__tests__/documentLoader.ts @@ -32,35 +32,45 @@ export const DOCUMENTS = { [DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV['id']]: DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV, [DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa[ 'id' - ]]: DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa, + ]]: + DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa, [DID_EXAMPLE_48939859['id']]: DID_EXAMPLE_48939859, [DID_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh[ 'id' - ]]: DID_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh, + ]]: + DID_zUC73JKGpX1WG4CWbFM15ni3faANPet6m8WJ6vaF5xyFsM3MeoBVNgQ6jjVPCcUnTAnJy6RVKqsUXa4AvdRKwV5hhQhwhMWFT9so9jrPekKmqpikTjYBXa3RYWqRpCWHY4u4hxh, [DID_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn[ 'id' - ]]: DID_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn, + ]]: + DID_zUC73YqdRJ3t8bZsFUoxYFPNVruHzn4o7u78GSrMXVSkcb3xAYtUxRD2kSt2bDcmQpRjKfygwLJ1HEGfkosSN7gr4acjGkXLbLRXREueknFN4AU19m8BxEgWnLM84CAvsw6bhYn, [DID_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ[ 'id' - ]]: DID_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ, + ]]: + DID_zUC76qMTDAaupy19pEk8JKH5LJwPwmscNQn24SYpqrgqEoYWPFgCSm4CnTfupADRfbB6CxdwYhVaTFjT4fmPvMh7gWY87LauhaLmNpPamCv4LAepcRfBDndSdtCpZKSTELMjzGJ, [DID_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox[ 'id' - ]]: DID_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox, + ]]: + DID_zUC7DMETzdZM6woUjvs2fieEyFTbHABXwBvLYPBs4NDWKut4H41h8V3KTqGNRUziXLYqa1sFYYw9Zjpt6pFUf7hra4Q1zXMA9JjXcXxDpxuDNpUKEpiDPSYYUztVchUJHQJJhox, [DID_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F[ 'id' - ]]: DID_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F, + ]]: + DID_zUC7F9Jt6YzVW9fGhwYjVrjdS8Xzg7oQc2CeDcVNgEcEAaJXAtPz3eXu2sewq4xtwRK3DAhQRYwwoYiT3nNzLCPsrKoP72UGZKhh4cNuZD7RkmwzAa1Bye4C5a9DcyYBGKZrE5F, [DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4[ 'id' - ]]: DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4, + ]]: + DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4, [DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4[ 'id' - ]]: DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4, + ]]: + DID_zUC7H7TxvhWmvfptpu2zSwo5EZ1kr3MPNsjovaD2ipbuzj6zi1vk4FHTiunCJrFvUYV77Mk3QcWUUAHojPZdU8oG476cvMK2ozP1gVq63x5ovj6e4oQ9qg9eF4YjPhWJs6FPuT4, [DID_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD[ 'id' - ]]: DID_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD, + ]]: + DID_zUC72to2eJiFMrt8a89LoaEPHC76QcfAxQdFys3nFGCmDKAmLbdE4ByyQ54kh42XgECCyZfVKe3m41Kk35nzrBKYbk6s9K7EjyLJcGGPkA7N15tDNBQJaY7cHD4RRaTwF6qXpmD, [DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN[ 'id' - ]]: DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN, + ]]: + DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN, [DID_SOV_QqEfJxe752NCmWqR5TssZ5['id']]: DID_SOV_QqEfJxe752NCmWqR5TssZ5, SECURITY_CONTEXT_V1_URL: SECURITY_V1, SECURITY_CONTEXT_V2_URL: SECURITY_V2, diff --git a/packages/core/src/utils/validators.ts b/packages/core/src/utils/validators.ts index 8e7240b5f2..57b1a1ec17 100644 --- a/packages/core/src/utils/validators.ts +++ b/packages/core/src/utils/validators.ts @@ -69,7 +69,7 @@ export const UriValidator = /\w+:(\/?\/?)[^\s]+/ export function IsUri(validationOptions?: ValidationOptions): PropertyDecorator { return ValidateBy( { - name: 'isInstanceOrArrayOfInstances', + name: 'isUri', validator: { validate: (value): boolean => { return UriValidator.test(value) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 7354fbe5a4..9e57c21943 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -8,7 +8,6 @@ import type { ConnectionRecordProps, CredentialDefinitionTemplate, CredentialStateChangedEvent, - TrustPingResponseReceivedEvent, InitConfig, InjectionToken, ProofStateChangedEvent, @@ -16,6 +15,7 @@ import type { Wallet, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' +import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' import type { ProofAttributeInfo, ProofPredicateInfo } from '../src/modules/proofs/formats/indy/models' import type { AutoAcceptProof } from '../src/modules/proofs/models/ProofAutoAcceptType' @@ -73,6 +73,7 @@ import { PresentationPreviewPredicate, } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import { customDocumentLoader } from '../src/modules/vc/__tests__/documentLoader' +import { KeyDerivationMethod } from '../src/types' import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' @@ -96,7 +97,8 @@ export function getAgentOptions { throw new Error( - `ProofStateChangedEvent event not emitted within specified timeout: { + `ProofStateChangedEvent event not emitted within specified timeout: ${timeoutMs} previousState: ${previousState}, threadId: ${threadId}, parentThreadId: ${parentThreadId}, @@ -242,13 +244,49 @@ export function waitForProofExchangeRecordSubject( ) } +export async function waitForTrustPingReceivedEvent( + agent: Agent, + options: { + threadId?: string + timeoutMs?: number + } +) { + const observable = agent.events.observable(TrustPingEventTypes.TrustPingReceivedEvent) + + return waitForTrustPingReceivedEventSubject(observable, options) +} + +export function waitForTrustPingReceivedEventSubject( + subject: ReplaySubject | Observable, + { + threadId, + timeoutMs = 10000, + }: { + threadId?: string + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return firstValueFrom( + observable.pipe( + filter((e) => threadId === undefined || e.payload.message.threadId === threadId), + timeout(timeoutMs), + catchError(() => { + throw new Error( + `TrustPingReceivedEvent event not emitted within specified timeout: ${timeoutMs} + threadId: ${threadId}, +}` + ) + }), + map((e) => e.payload.message) + ) + ) +} + export async function waitForTrustPingResponseReceivedEvent( agent: Agent, options: { threadId?: string - parentThreadId?: string - state?: ProofState - previousState?: ProofState | null timeoutMs?: number } ) { @@ -276,7 +314,7 @@ export function waitForTrustPingResponseReceivedEventSubject( timeout(timeoutMs), catchError(() => { throw new Error( - `TrustPingResponseReceivedEvent event not emitted within specified timeout: { + `TrustPingResponseReceivedEvent event not emitted within specified timeout: ${timeoutMs} threadId: ${threadId}, }` ) diff --git a/packages/indy-sdk/README.md b/packages/indy-sdk/README.md new file mode 100644 index 0000000000..368d25db71 --- /dev/null +++ b/packages/indy-sdk/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript IndySDK Module

+

+ License + typescript + @aries-framework/indy-sdk version + +

+
+ +IndySDK module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). diff --git a/packages/indy-sdk/jest.config.ts b/packages/indy-sdk/jest.config.ts new file mode 100644 index 0000000000..c7c5196637 --- /dev/null +++ b/packages/indy-sdk/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + // setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json new file mode 100644 index 0000000000..4c19732005 --- /dev/null +++ b/packages/indy-sdk/package.json @@ -0,0 +1,40 @@ +{ + "name": "@aries-framework/indy-sdk", + "main": "build/index", + "types": "build/index", + "version": "0.2.5", + "private": true, + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/indy-sdk", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/indy-sdk" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/anoncreds": "0.3.2", + "@aries-framework/core": "0.3.2", + "@types/indy-sdk": "1.16.24", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "rxjs": "^7.2.0", + "tsyringe": "^4.7.0" + }, + "devDependencies": { + "rimraf": "~3.0.2", + "typescript": "~4.3.0" + } +} diff --git a/packages/indy-sdk/src/IndySdkModule.ts b/packages/indy-sdk/src/IndySdkModule.ts new file mode 100644 index 0000000000..ea3baa5a9a --- /dev/null +++ b/packages/indy-sdk/src/IndySdkModule.ts @@ -0,0 +1,17 @@ +import type { IndySdkModuleConfigOptions } from './IndySdkModuleConfig' +import type { DependencyManager, Module } from '@aries-framework/core' + +import { IndySdkModuleConfig } from './IndySdkModuleConfig' +import { IndySdkSymbol } from './types' + +export class IndySdkModule implements Module { + public readonly config: IndySdkModuleConfig + + public constructor(config: IndySdkModuleConfigOptions) { + this.config = new IndySdkModuleConfig(config) + } + + public register(dependencyManager: DependencyManager) { + dependencyManager.registerInstance(IndySdkSymbol, this.config.indySdk) + } +} diff --git a/packages/indy-sdk/src/IndySdkModuleConfig.ts b/packages/indy-sdk/src/IndySdkModuleConfig.ts new file mode 100644 index 0000000000..a01bf813b3 --- /dev/null +++ b/packages/indy-sdk/src/IndySdkModuleConfig.ts @@ -0,0 +1,45 @@ +import type * as IndySdk from 'indy-sdk' + +/** + * IndySdkModuleConfigOptions defines the interface for the options of the IndySdkModuleConfig class. + */ +export interface IndySdkModuleConfigOptions { + /** + * Implementation of the IndySdk interface according to the @types/indy-sdk package. + * + * + * ## Node.JS + * + * ```ts + * import * as indySdk from 'indy-sdk' + * + * const indySdkModule = new IndySdkModule({ + * indySdk + * }) + * ``` + * + * ## React Native + * + * ```ts + * import * as indySdk from 'indy-sdk-react-native' + * + * const indySdkModule = new IndySdkModule({ + * indySdk + * }) + * ``` + */ + indySdk: typeof IndySdk +} + +export class IndySdkModuleConfig { + private options: IndySdkModuleConfigOptions + + public constructor(options: IndySdkModuleConfigOptions) { + this.options = options + } + + /** See {@link IndySdkModuleConfigOptions.resolvers} */ + public get indySdk() { + return this.options.indySdk + } +} diff --git a/packages/indy-sdk/src/anoncreds/index.ts b/packages/indy-sdk/src/anoncreds/index.ts new file mode 100644 index 0000000000..adba521ce0 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/index.ts @@ -0,0 +1,4 @@ +export { IndySdkAnonCredsRegistry } from './services/IndySdkAnonCredsRegistry' +export { IndySdkHolderService } from './services/IndySdkHolderService' +export { IndySdkIssuerService } from './services/IndySdkIssuerService' +export { IndySdkVerifierService } from './services/IndySdkVerifierService' diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts new file mode 100644 index 0000000000..3b5c6a08ce --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -0,0 +1,514 @@ +import type { + AnonCredsRegistry, + GetCredentialDefinitionReturn, + GetRevocationListReturn, + GetRevocationRegistryDefinitionReturn, + GetSchemaReturn, + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, + RegisterSchemaOptions, + RegisterSchemaReturn, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' +import type { Schema as IndySdkSchema } from 'indy-sdk' + +import { inject } from '@aries-framework/core' + +import { IndySdkError, isIndyError } from '../../error' +import { IndySdkPoolService } from '../../ledger' +import { IndySdk, IndySdkSymbol } from '../../types' +import { + didFromCredentialDefinitionId, + didFromRevocationRegistryDefinitionId, + didFromSchemaId, + getLegacyCredentialDefinitionId, + getLegacySchemaId, +} from '../utils/identifiers' +import { + anonCredsRevocationListFromIndySdk, + anonCredsRevocationRegistryDefinitionFromIndySdk, +} from '../utils/transform' + +/** + * TODO: validation of the identifiers. The Indy SDK classes only support the legacy (unqualified) identifiers. + */ +export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { + private indySdk: IndySdk + private indySdkPoolService: IndySdkPoolService + + public constructor(@inject(IndySdkSymbol) indySdk: IndySdk, indySdkPoolService: IndySdkPoolService) { + this.indySdk = indySdk + this.indySdkPoolService = indySdkPoolService + } + + public async getSchema(agentContext: AgentContext, schemaId: string): Promise { + try { + const did = didFromSchemaId(schemaId) + const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`) + + const request = await this.indySdk.buildGetSchemaRequest(null, schemaId) + + agentContext.config.logger.trace( + `Submitting get schema request for schema '${schemaId}' to ledger '${pool.didIndyNamespace}'` + ) + const response = await this.indySdkPoolService.submitReadRequest(pool, request) + + agentContext.config.logger.trace(`Got un-parsed schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`, { + response, + }) + + const [, schema] = await this.indySdk.parseGetSchemaResponse(response) + agentContext.config.logger.debug(`Got schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`, { + schema, + }) + + const issuerId = didFromSchemaId(schema.id) + + return { + schema: { + attrNames: schema.attrNames, + name: schema.name, + version: schema.version, + issuerId: issuerId, + }, + schemaId: schema.id, + resolutionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo: schema.seqNo, + }, + schemaMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`, { + error, + schemaId, + }) + + return { + schema: null, + schemaId, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition: ${error.message}`, + }, + schemaMetadata: {}, + } + } + } + + public async registerSchema( + agentContext: AgentContext, + options: IndySdkRegisterSchemaOptions + ): Promise { + // Make sure didIndyNamespace is passed + if (!options.options.didIndyNamespace) { + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy SDK', + schema: options.schema, + state: 'failed', + }, + } + } + + try { + const pool = this.indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) + agentContext.config.logger.debug( + `Register schema on ledger '${pool.didIndyNamespace}' with did '${options.schema.issuerId}'`, + options.schema + ) + + const schema = { + attrNames: options.schema.attrNames, + name: options.schema.name, + version: options.schema.version, + id: getLegacySchemaId(options.schema.issuerId, options.schema.name, options.schema.version), + ver: '1.0', + // Casted as because the type expect a seqNo, but that's not actually required for the input of + // buildSchemaRequest (seqNo is not yet known) + } as IndySdkSchema + + const request = await this.indySdk.buildSchemaRequest(options.schema.issuerId, schema) + + const response = await this.indySdkPoolService.submitWriteRequest( + agentContext, + pool, + request, + options.schema.issuerId + ) + agentContext.config.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.didIndyNamespace}'`, { + response, + schema, + }) + + return { + schemaState: { + state: 'finished', + schema: { + attrNames: schema.attrNames, + issuerId: options.schema.issuerId, + name: schema.name, + version: schema.version, + }, + schemaId: schema.id, + }, + registrationMetadata: { + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo: schema.seqNo, + didIndyNamespace: pool.didIndyNamespace, + }, + schemaMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error(`Error registering schema for did '${options.schema.issuerId}'`, { + error, + did: options.schema.issuerId, + schema: options.schema, + }) + + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + state: 'failed', + schema: options.schema, + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async getCredentialDefinition( + agentContext: AgentContext, + credentialDefinitionId: string + ): Promise { + try { + const did = didFromCredentialDefinitionId(credentialDefinitionId) + const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug( + `Using ledger '${pool.didIndyNamespace}' to retrieve credential definition '${credentialDefinitionId}'` + ) + const request = await this.indySdk.buildGetCredDefRequest(null, credentialDefinitionId) + + agentContext.config.logger.trace( + `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.didIndyNamespace}'` + ) + + const response = await this.indySdkPoolService.submitReadRequest(pool, request) + agentContext.config.logger.trace( + `Got un-parsed credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, + { + response, + } + ) + + const [, credentialDefinition] = await this.indySdk.parseGetCredDefResponse(response) + agentContext.config.logger.debug( + `Got credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, + { + credentialDefinition, + } + ) + + return { + credentialDefinitionId: credentialDefinition.id, + credentialDefinition: { + issuerId: didFromCredentialDefinitionId(credentialDefinition.id), + schemaId: credentialDefinition.schemaId, + tag: credentialDefinition.tag, + type: 'CL', + value: credentialDefinition.value, + }, + credentialDefinitionMetadata: {}, + resolutionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { + error, + credentialDefinitionId, + }) + + return { + credentialDefinitionId, + credentialDefinition: null, + credentialDefinitionMetadata: {}, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition: ${error.message}`, + }, + } + } + } + + public async registerCredentialDefinition( + agentContext: AgentContext, + options: IndySdkRegisterCredentialDefinitionOptions + ): Promise { + // Make sure didIndyNamespace is passed + if (!options.options.didIndyNamespace) { + return { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy SDK', + credentialDefinition: options.credentialDefinition, + state: 'failed', + }, + } + } + + try { + const pool = this.indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) + agentContext.config.logger.debug( + `Registering credential definition on ledger '${pool.didIndyNamespace}' with did '${options.credentialDefinition.issuerId}'`, + options.credentialDefinition + ) + + // TODO: this will bypass caching if done on a higher level. + const { schema, resolutionMetadata } = await this.getSchema(agentContext, options.credentialDefinition.schemaId) + + if (!schema || !resolutionMetadata.indyLedgerSeqNo || typeof resolutionMetadata.indyLedgerSeqNo !== 'number') { + return { + registrationMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, + credentialDefinitionMetadata: {}, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + state: 'failed', + reason: `error resolving schema with id ${options.credentialDefinition.schemaId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, + }, + } + } + + const credentialDefinitionId = getLegacyCredentialDefinitionId( + options.credentialDefinition.issuerId, + resolutionMetadata.indyLedgerSeqNo, + options.credentialDefinition.tag + ) + + const request = await this.indySdk.buildCredDefRequest(options.credentialDefinition.issuerId, { + id: credentialDefinitionId, + schemaId: options.credentialDefinition.schemaId, + tag: options.credentialDefinition.tag, + type: options.credentialDefinition.type, + value: options.credentialDefinition.value, + ver: '1.0', + }) + + const response = await this.indySdkPoolService.submitWriteRequest( + agentContext, + pool, + request, + options.credentialDefinition.issuerId + ) + + agentContext.config.logger.debug( + `Registered credential definition '${credentialDefinitionId}' on ledger '${pool.didIndyNamespace}'`, + { + response, + credentialDefinition: options.credentialDefinition, + } + ) + + return { + credentialDefinitionMetadata: {}, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + credentialDefinitionId, + state: 'finished', + }, + registrationMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, + } + } catch (error) { + agentContext.config.logger.error( + `Error registering credential definition for schema '${options.credentialDefinition.schemaId}'`, + { + error, + did: options.credentialDefinition.issuerId, + credentialDefinition: options.credentialDefinition, + } + ) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise { + try { + const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) + const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug( + `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` + ) + const request = await this.indySdk.buildGetRevocRegDefRequest(null, revocationRegistryDefinitionId) + + agentContext.config.logger.trace( + `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` + ) + const response = await this.indySdkPoolService.submitReadRequest(pool, request) + agentContext.config.logger.trace( + `Got un-parsed revocation registry definition '${revocationRegistryDefinitionId}' from ledger '${pool.didIndyNamespace}'`, + { + response, + } + ) + + const [, revocationRegistryDefinition] = await this.indySdk.parseGetRevocRegDefResponse(response) + + agentContext.config.logger.debug( + `Got revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, + { + revocationRegistryDefinition, + } + ) + + return { + resolutionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, + revocationRegistryDefinition: anonCredsRevocationRegistryDefinitionFromIndySdk(revocationRegistryDefinition), + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: { + issuanceType: revocationRegistryDefinition.value.issuanceType, + }, + } + } catch (error) { + agentContext.config.logger.error( + `Error retrieving revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, + { + error, + revocationRegistryDefinitionId: revocationRegistryDefinitionId, + } + ) + + return { + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve revocation registry definition: ${error.message}`, + }, + revocationRegistryDefinition: null, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } + } + } + + public async getRevocationList( + agentContext: AgentContext, + revocationRegistryId: string, + timestamp: number + ): Promise { + try { + const did = didFromRevocationRegistryDefinitionId(revocationRegistryId) + const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug( + `Using ledger '${pool.id}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` + ) + + // TODO: implement caching for returned deltas + const request = await this.indySdk.buildGetRevocRegDeltaRequest(null, revocationRegistryId, 0, timestamp) + + agentContext.config.logger.trace( + `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` + ) + + const response = await this.indySdkPoolService.submitReadRequest(pool, request) + agentContext.config.logger.trace( + `Got revocation registry delta unparsed-response '${revocationRegistryId}' from ledger`, + { + response, + } + ) + + const [, revocationRegistryDelta, deltaTimestamp] = await this.indySdk.parseGetRevocRegDeltaResponse(response) + + agentContext.config.logger.debug( + `Got revocation registry deltas '${revocationRegistryId}' until timestamp ${timestamp} from ledger`, + { + revocationRegistryDelta, + deltaTimestamp, + } + ) + + const { resolutionMetadata, revocationRegistryDefinition, revocationRegistryDefinitionMetadata } = + await this.getRevocationRegistryDefinition(agentContext, revocationRegistryId) + + if ( + !revocationRegistryDefinition || + !revocationRegistryDefinitionMetadata.issuanceType || + typeof revocationRegistryDefinitionMetadata.issuanceType !== 'string' + ) { + return { + resolutionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + error: `error resolving revocation registry definition with id ${revocationRegistryId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, + }, + revocationListMetadata: {}, + revocationList: null, + } + } + + const isIssuanceByDefault = revocationRegistryDefinitionMetadata.issuanceType === 'ISSUANCE_BY_DEFAULT' + + return { + resolutionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, + revocationList: anonCredsRevocationListFromIndySdk( + revocationRegistryId, + revocationRegistryDefinition, + revocationRegistryDelta, + deltaTimestamp, + isIssuanceByDefault + ), + revocationListMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error( + `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation?"`, + { + error, + revocationRegistryId: revocationRegistryId, + } + ) + + return { + resolutionMetadata: { + error: 'notFound', + message: `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation: ${error.message}`, + }, + revocationList: null, + revocationListMetadata: {}, + } + } + } +} + +export interface IndySdkRegisterSchemaOptions extends RegisterSchemaOptions { + options: { + didIndyNamespace: string + } +} + +export interface IndySdkRegisterCredentialDefinitionOptions extends RegisterCredentialDefinitionOptions { + options: { + didIndyNamespace: string + } +} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts new file mode 100644 index 0000000000..88179381a9 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -0,0 +1,327 @@ +import type { + AnonCredsHolderService, + AnonCredsProof, + CreateCredentialRequestOptions, + CreateCredentialRequestReturn, + CreateProofOptions, + CredentialInfo, + GetCredentialOptions, + StoreCredentialOptions, + GetCredentialsForProofRequestOptions, + GetCredentialsForProofRequestReturn, + RequestedCredentials, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' +import type { + Cred, + CredentialDefs, + IndyRequestedCredentials, + RevStates, + Schemas, + IndyCredential as IndySdkCredential, +} from 'indy-sdk' + +import { inject } from '@aries-framework/core' + +import { IndySdkError, isIndyError } from '../../error' +import { IndySdk, IndySdkSymbol } from '../../types' +import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' +import { getIndySeqNoFromUnqualifiedCredentialDefinitionId } from '../utils/identifiers' +import { + indySdkCredentialDefinitionFromAnonCreds, + indySdkRevocationRegistryDefinitionFromAnonCreds, + indySdkSchemaFromAnonCreds, +} from '../utils/transform' + +import { IndySdkRevocationService } from './IndySdkRevocationService' + +export class IndySdkHolderService implements AnonCredsHolderService { + private indySdk: IndySdk + private indyRevocationService: IndySdkRevocationService + + public constructor(indyRevocationService: IndySdkRevocationService, @inject(IndySdkSymbol) indySdk: IndySdk) { + this.indySdk = indySdk + this.indyRevocationService = indyRevocationService + } + + public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise { + const { credentialDefinitions, proofRequest, requestedCredentials, schemas } = options + + assertIndySdkWallet(agentContext.wallet) + + try { + agentContext.config.logger.debug('Creating Indy Proof') + const indyRevocationStates: RevStates = await this.indyRevocationService.createRevocationState( + agentContext, + proofRequest, + requestedCredentials, + options.revocationRegistries + ) + + // The AnonCredsSchema doesn't contain the seqNo anymore. However, the indy credential definition id + // does contain the seqNo, so we can extract it from the credential definition id. + const seqNoMap: { [schemaId: string]: number } = {} + + // Convert AnonCreds credential definitions to Indy credential definitions + const indyCredentialDefinitions: CredentialDefs = {} + for (const credentialDefinitionId in credentialDefinitions) { + const credentialDefinition = credentialDefinitions[credentialDefinitionId] + indyCredentialDefinitions[credentialDefinitionId] = indySdkCredentialDefinitionFromAnonCreds( + credentialDefinitionId, + credentialDefinition + ) + + // Get the seqNo for the schemas so we can use it when transforming the schemas + const schemaSeqNo = getIndySeqNoFromUnqualifiedCredentialDefinitionId(credentialDefinitionId) + seqNoMap[credentialDefinition.schemaId] = schemaSeqNo + } + + // Convert AnonCreds schemas to Indy schemas + const indySchemas: Schemas = {} + for (const schemaId in schemas) { + const schema = schemas[schemaId] + indySchemas[schemaId] = indySdkSchemaFromAnonCreds(schemaId, schema, seqNoMap[schemaId]) + } + + const indyProof = await this.indySdk.proverCreateProof( + agentContext.wallet.handle, + proofRequest, + this.parseRequestedCredentials(requestedCredentials), + agentContext.wallet.masterSecretId, + indySchemas, + indyCredentialDefinitions, + indyRevocationStates + ) + + agentContext.config.logger.trace('Created Indy Proof', { + indyProof, + }) + + return indyProof + } catch (error) { + agentContext.config.logger.error(`Error creating Indy Proof`, { + error, + proofRequest, + requestedCredentials, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async storeCredential(agentContext: AgentContext, options: StoreCredentialOptions): Promise { + assertIndySdkWallet(agentContext.wallet) + + const indyRevocationRegistryDefinition = options.revocationRegistry + ? indySdkRevocationRegistryDefinitionFromAnonCreds( + options.revocationRegistry.id, + options.revocationRegistry.definition + ) + : null + + try { + return await this.indySdk.proverStoreCredential( + agentContext.wallet.handle, + options.credentialId ?? null, + options.credentialRequestMetadata, + options.credential, + indySdkCredentialDefinitionFromAnonCreds(options.credentialDefinitionId, options.credentialDefinition), + indyRevocationRegistryDefinition + ) + } catch (error) { + agentContext.config.logger.error(`Error storing Indy Credential '${options.credentialId}'`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async getCredential(agentContext: AgentContext, options: GetCredentialOptions): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + const result = await this.indySdk.proverGetCredential(agentContext.wallet.handle, options.credentialId) + + return { + credentialDefinitionId: result.cred_def_id, + attributes: result.attrs, + referent: result.referent, + schemaId: result.schema_id, + credentialRevocationId: result.cred_rev_id, + revocationRegistryId: result.rev_reg_id, + } + } catch (error) { + agentContext.config.logger.error(`Error getting Indy Credential '${options.credentialId}'`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async createCredentialRequest( + agentContext: AgentContext, + options: CreateCredentialRequestOptions + ): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + const result = await this.indySdk.proverCreateCredentialReq( + agentContext.wallet.handle, + options.holderDid, + options.credentialOffer, + // NOTE: Is it safe to use the cred_def_id from the offer? I think so. You can't create a request + // for a cred def that is not in the offer + indySdkCredentialDefinitionFromAnonCreds(options.credentialOffer.cred_def_id, options.credentialDefinition), + // FIXME: we need to remove the masterSecret from the wallet, as it is AnonCreds specific + // Issue: https://github.com/hyperledger/aries-framework-javascript/issues/1198 + agentContext.wallet.masterSecretId + ) + + return { + credentialRequest: result[0], + credentialRequestMetadata: result[1], + } + } catch (error) { + agentContext.config.logger.error(`Error creating Indy Credential Request`, { + error, + credentialOffer: options.credentialOffer, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async deleteCredential(agentContext: AgentContext, credentialId: string): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + return await this.indySdk.proverDeleteCredential(agentContext.wallet.handle, credentialId) + } catch (error) { + agentContext.config.logger.error(`Error deleting Indy Credential from Wallet`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async getCredentialsForProofRequest( + agentContext: AgentContext, + options: GetCredentialsForProofRequestOptions + ): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + // Open indy credential search + const searchHandle = await this.indySdk.proverSearchCredentialsForProofReq( + agentContext.wallet.handle, + options.proofRequest, + options.extraQuery ?? null + ) + + const start = options.start ?? 0 + + try { + // Make sure database cursors start at 'start' (bit ugly, but no way around in indy) + if (start > 0) { + await this.fetchCredentialsForReferent(agentContext, searchHandle, options.attributeReferent, start) + } + + // Fetch the credentials + const credentials = await this.fetchCredentialsForReferent( + agentContext, + searchHandle, + options.attributeReferent, + options.limit + ) + + // TODO: sort the credentials (irrevocable first) + return credentials.map((credential) => ({ + credentialInfo: { + credentialDefinitionId: credential.cred_info.cred_def_id, + referent: credential.cred_info.referent, + attributes: credential.cred_info.attrs, + schemaId: credential.cred_info.schema_id, + revocationRegistryId: credential.cred_info.rev_reg_id, + credentialRevocationId: credential.cred_info.cred_rev_id, + }, + interval: credential.interval, + })) + } finally { + // Always close search + await this.indySdk.proverCloseCredentialsSearchForProofReq(searchHandle) + } + } catch (error) { + if (isIndyError(error)) { + throw new IndySdkError(error) + } + + throw error + } + } + + private async fetchCredentialsForReferent( + agentContext: AgentContext, + searchHandle: number, + referent: string, + limit?: number + ) { + try { + let credentials: IndySdkCredential[] = [] + + // Allow max of 256 per fetch operation + const chunk = limit ? Math.min(256, limit) : 256 + + // Loop while limit not reached (or no limit specified) + while (!limit || credentials.length < limit) { + // Retrieve credentials + const credentialsJson = await this.indySdk.proverFetchCredentialsForProofReq(searchHandle, referent, chunk) + credentials = [...credentials, ...credentialsJson] + + // If the number of credentials returned is less than chunk + // It means we reached the end of the iterator (no more credentials) + if (credentialsJson.length < chunk) { + return credentials + } + } + + return credentials + } catch (error) { + agentContext.config.logger.error(`Error Fetching Indy Credentials For Referent`, { + error, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + /** + * Converts a public api form of {@link RequestedCredentials} interface into a format {@link Indy.IndyRequestedCredentials} that Indy SDK expects. + **/ + private parseRequestedCredentials(requestedCredentials: RequestedCredentials): IndyRequestedCredentials { + const indyRequestedCredentials: IndyRequestedCredentials = { + requested_attributes: {}, + requested_predicates: {}, + self_attested_attributes: {}, + } + + for (const groupName in requestedCredentials.requestedAttributes) { + indyRequestedCredentials.requested_attributes[groupName] = { + cred_id: requestedCredentials.requestedAttributes[groupName].credentialId, + revealed: requestedCredentials.requestedAttributes[groupName].revealed, + timestamp: requestedCredentials.requestedAttributes[groupName].timestamp, + } + } + + for (const groupName in requestedCredentials.requestedPredicates) { + indyRequestedCredentials.requested_predicates[groupName] = { + cred_id: requestedCredentials.requestedPredicates[groupName].credentialId, + timestamp: requestedCredentials.requestedPredicates[groupName].timestamp, + } + } + + return indyRequestedCredentials + } +} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts new file mode 100644 index 0000000000..f877be4f75 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -0,0 +1,129 @@ +import type { CreateCredentialDefinitionMetadata } from './IndySdkIssuerServiceMetadata' +import type { IndySdkUtilitiesService } from './IndySdkUtilitiesService' +import type { + AnonCredsIssuerService, + CreateCredentialDefinitionOptions, + CreateCredentialOfferOptions, + CreateCredentialOptions, + CreateCredentialReturn, + CreateSchemaOptions, + AnonCredsCredentialOffer, + AnonCredsSchema, + AnonCredsCredentialDefinition, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' + +import { AriesFrameworkError, inject } from '@aries-framework/core' + +import { IndySdkError, isIndyError } from '../../error' +import { IndySdk, IndySdkSymbol } from '../../types' +import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' +import { indySdkSchemaFromAnonCreds } from '../utils/transform' + +export class IndySdkIssuerService implements AnonCredsIssuerService { + private indySdk: IndySdk + private IndySdkUtilitiesService: IndySdkUtilitiesService + + public constructor(IndySdkUtilitiesService: IndySdkUtilitiesService, @inject(IndySdkSymbol) indySdk: IndySdk) { + this.indySdk = indySdk + this.IndySdkUtilitiesService = IndySdkUtilitiesService + } + + public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { + const { issuerId, name, version, attrNames } = options + assertIndySdkWallet(agentContext.wallet) + + try { + const [, schema] = await this.indySdk.issuerCreateSchema(issuerId, name, version, attrNames) + + return { + issuerId, + attrNames: schema.attrNames, + name: schema.name, + version: schema.version, + } + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async createCredentialDefinition( + agentContext: AgentContext, + options: CreateCredentialDefinitionOptions, + metadata?: CreateCredentialDefinitionMetadata + ): Promise { + const { tag, supportRevocation, schema, issuerId, schemaId } = options + + if (!metadata) + throw new AriesFrameworkError('The metadata parameter is required when using Indy, but received undefined.') + + try { + assertIndySdkWallet(agentContext.wallet) + const [, credentialDefinition] = await this.indySdk.issuerCreateAndStoreCredentialDef( + agentContext.wallet.handle, + issuerId, + indySdkSchemaFromAnonCreds(schemaId, schema, metadata.indyLedgerSchemaSeqNo), + tag, + 'CL', + { + support_revocation: supportRevocation, + } + ) + + return { + issuerId, + tag: credentialDefinition.tag, + schemaId: credentialDefinition.schemaId, + type: 'CL', + value: credentialDefinition.value, + } + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async createCredentialOffer( + agentContext: AgentContext, + options: CreateCredentialOfferOptions + ): Promise { + assertIndySdkWallet(agentContext.wallet) + try { + return await this.indySdk.issuerCreateCredentialOffer(agentContext.wallet.handle, options.credentialDefinitionId) + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async createCredential( + agentContext: AgentContext, + options: CreateCredentialOptions + ): Promise { + const { tailsFilePath, credentialOffer, credentialRequest, credentialValues, revocationRegistryId } = options + + assertIndySdkWallet(agentContext.wallet) + try { + // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present + const tailsReaderHandle = tailsFilePath ? await this.IndySdkUtilitiesService.createTailsReader(tailsFilePath) : 0 + + if (revocationRegistryId || tailsFilePath) { + throw new AriesFrameworkError('Revocation not supported yet') + } + + const [credential, credentialRevocationId] = await this.indySdk.issuerCreateCredential( + agentContext.wallet.handle, + credentialOffer, + credentialRequest, + credentialValues, + revocationRegistryId ?? null, + tailsReaderHandle + ) + + return { + credential, + credentialRevocationId, + } + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerServiceMetadata.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerServiceMetadata.ts new file mode 100644 index 0000000000..bb02f17967 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerServiceMetadata.ts @@ -0,0 +1,3 @@ +export type CreateCredentialDefinitionMetadata = { + indyLedgerSchemaSeqNo: number +} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts new file mode 100644 index 0000000000..0ed637a6ee --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts @@ -0,0 +1,177 @@ +import type { + AnonCredsRevocationRegistryDefinition, + AnonCredsRevocationList, + AnonCredsProofRequest, + RequestedCredentials, + CredentialInfo, + NonRevokedInterval, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' +import type { RevStates } from 'indy-sdk' + +import { AriesFrameworkError, inject, injectable } from '@aries-framework/core' + +import { IndySdkError, isIndyError } from '../../error' +import { IndySdk, IndySdkSymbol } from '../../types' +import { + indySdkRevocationDeltaFromAnonCreds, + indySdkRevocationRegistryDefinitionFromAnonCreds, +} from '../utils/transform' + +import { IndySdkUtilitiesService } from './IndySdkUtilitiesService' + +enum RequestReferentType { + Attribute = 'attribute', + Predicate = 'predicate', + SelfAttestedAttribute = 'self-attested-attribute', +} + +/** + * Internal class that handles revocation related logic for the Indy SDK + * + * @internal + */ +@injectable() +export class IndySdkRevocationService { + private indySdk: IndySdk + private indySdkUtilitiesService: IndySdkUtilitiesService + + public constructor(indyUtilitiesService: IndySdkUtilitiesService, @inject(IndySdkSymbol) indySdk: IndySdk) { + this.indySdk = indySdk + this.indySdkUtilitiesService = indyUtilitiesService + } + + /** + * Creates the revocation state for the requested credentials in a format that the Indy SDK expects. + */ + public async createRevocationState( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + requestedCredentials: RequestedCredentials, + revocationRegistries: { + [revocationRegistryDefinitionId: string]: { + // Tails is already downloaded + tailsFilePath: string + definition: AnonCredsRevocationRegistryDefinition + revocationLists: { + [timestamp: string]: AnonCredsRevocationList + } + } + } + ): Promise { + try { + agentContext.config.logger.debug(`Creating Revocation State(s) for proof request`, { + proofRequest, + requestedCredentials, + }) + const indyRevocationStates: RevStates = {} + const referentCredentials: Array<{ + type: RequestReferentType + referent: string + credentialInfo: CredentialInfo + referentRevocationInterval: NonRevokedInterval | undefined + }> = [] + + //Retrieve information for referents and push to single array + for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedAttributes ?? {})) { + referentCredentials.push({ + referent, + credentialInfo: requestedCredential.credentialInfo, + type: RequestReferentType.Attribute, + referentRevocationInterval: proofRequest.requested_attributes[referent].non_revoked, + }) + } + for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedPredicates ?? {})) { + referentCredentials.push({ + referent, + credentialInfo: requestedCredential.credentialInfo, + type: RequestReferentType.Predicate, + referentRevocationInterval: proofRequest.requested_predicates[referent].non_revoked, + }) + } + + for (const { referent, credentialInfo, type, referentRevocationInterval } of referentCredentials) { + // Prefer referent-specific revocation interval over global revocation interval + const requestRevocationInterval = referentRevocationInterval ?? proofRequest.non_revoked + const credentialRevocationId = credentialInfo.credentialRevocationId + const revocationRegistryId = credentialInfo.revocationRegistryId + + // If revocation interval is present and the credential is revocable then create revocation state + if (requestRevocationInterval && credentialRevocationId && revocationRegistryId) { + agentContext.config.logger.trace( + `Presentation is requesting proof of non revocation for ${type} referent '${referent}', creating revocation state for credential`, + { + requestRevocationInterval, + credentialRevocationId, + revocationRegistryId, + } + ) + + this.assertRevocationInterval(requestRevocationInterval) + + const { definition, revocationLists, tailsFilePath } = revocationRegistries[revocationRegistryId] + // NOTE: we assume that the revocationLists have been added based on timestamps of the `to` query. On a higher level it means we'll find the + // most accurate revocation list for a given timestamp. It doesn't have to be that the revocationList is from the `to` timestamp however. + const revocationList = revocationLists[requestRevocationInterval.to] + + const tails = await this.indySdkUtilitiesService.createTailsReader(tailsFilePath) + + const revocationState = await this.indySdk.createRevocationState( + tails, + indySdkRevocationRegistryDefinitionFromAnonCreds(revocationRegistryId, definition), + indySdkRevocationDeltaFromAnonCreds(revocationList), + revocationList.timestamp, + credentialRevocationId + ) + const timestamp = revocationState.timestamp + + if (!indyRevocationStates[revocationRegistryId]) { + indyRevocationStates[revocationRegistryId] = {} + } + indyRevocationStates[revocationRegistryId][timestamp] = revocationState + } + } + + agentContext.config.logger.debug(`Created Revocation States for Proof Request`, { + indyRevocationStates, + }) + + return indyRevocationStates + } catch (error) { + agentContext.config.logger.error(`Error creating Indy Revocation State for Proof Request`, { + error, + proofRequest, + requestedCredentials, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + // TODO: Add Test + // TODO: we should do this verification on a higher level I think? + // Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints + private assertRevocationInterval( + revocationInterval: NonRevokedInterval + ): asserts revocationInterval is BestPracticeNonRevokedInterval { + if (!revocationInterval.to) { + throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) + } + + if ( + (revocationInterval.from || revocationInterval.from === 0) && + revocationInterval.to !== revocationInterval.from + ) { + throw new AriesFrameworkError( + `Presentation requests proof of non-revocation with an interval from: '${revocationInterval.from}' that does not match the interval to: '${revocationInterval.to}', as specified in Aries RFC 0441` + ) + } + } +} + +// This sets the `to` value to be required. We do this check in the `assertRevocationInterval` method, +// and it makes it easier to work with the object in TS +interface BestPracticeNonRevokedInterval { + from?: number + to: number +} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts new file mode 100644 index 0000000000..1ac0dec33e --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts @@ -0,0 +1,65 @@ +import type { BlobReaderHandle } from 'indy-sdk' + +import { + AriesFrameworkError, + FileSystem, + getDirFromFilePath, + IndySdkError, + InjectionSymbols, + Logger, +} from '@aries-framework/core' +import { inject, injectable } from 'tsyringe' + +import { isIndyError } from '../../error' +import { IndySdk, IndySdkSymbol } from '../../types' + +@injectable() +export class IndySdkUtilitiesService { + private indySdk: IndySdk + private logger: Logger + private fileSystem: FileSystem + + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, + @inject(IndySdkSymbol) indySdk: IndySdk + ) { + this.indySdk = indySdk + this.logger = logger + this.fileSystem = fileSystem + } + + /** + * Get a handler for the blob storage tails file reader. + * + * @param tailsFilePath The path of the tails file + * @returns The blob storage reader handle + */ + public async createTailsReader(tailsFilePath: string): Promise { + try { + this.logger.debug(`Opening tails reader at path ${tailsFilePath}`) + const tailsFileExists = await this.fileSystem.exists(tailsFilePath) + + // Extract directory from path (should also work with windows paths) + const dirname = getDirFromFilePath(tailsFilePath) + + if (!tailsFileExists) { + throw new AriesFrameworkError(`Tails file does not exist at path ${tailsFilePath}`) + } + + const tailsReaderConfig = { + base_dir: dirname, + } + + const tailsReader = await this.indySdk.openBlobStorageReader('default', tailsReaderConfig) + this.logger.debug(`Opened tails reader at path ${tailsFilePath}`) + return tailsReader + } catch (error) { + if (isIndyError(error)) { + throw new IndySdkError(error) + } + + throw error + } + } +} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts new file mode 100644 index 0000000000..d302e66c97 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -0,0 +1,86 @@ +import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' +import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs } from 'indy-sdk' + +import { inject } from '@aries-framework/core' + +import { IndySdkError, isIndyError } from '../../error' +import { IndySdk, IndySdkSymbol } from '../../types' +import { getIndySeqNoFromUnqualifiedCredentialDefinitionId } from '../utils/identifiers' +import { + indySdkCredentialDefinitionFromAnonCreds, + indySdkRevocationRegistryDefinitionFromAnonCreds, + indySdkRevocationRegistryFromAnonCreds, + indySdkSchemaFromAnonCreds, +} from '../utils/transform' + +export class IndySdkVerifierService implements AnonCredsVerifierService { + private indySdk: IndySdk + + public constructor(@inject(IndySdkSymbol) indySdk: IndySdk) { + this.indySdk = indySdk + } + + public async verifyProof(options: VerifyProofOptions): Promise { + try { + // The AnonCredsSchema doesn't contain the seqNo anymore. However, the indy credential definition id + // does contain the seqNo, so we can extract it from the credential definition id. + const seqNoMap: { [schemaId: string]: number } = {} + + // Convert AnonCreds credential definitions to Indy credential definitions + const indyCredentialDefinitions: CredentialDefs = {} + for (const credentialDefinitionId in options.credentialDefinitions) { + const credentialDefinition = options.credentialDefinitions[credentialDefinitionId] + + indyCredentialDefinitions[credentialDefinitionId] = indySdkCredentialDefinitionFromAnonCreds( + credentialDefinitionId, + credentialDefinition + ) + + // Get the seqNo for the schemas so we can use it when transforming the schemas + const schemaSeqNo = getIndySeqNoFromUnqualifiedCredentialDefinitionId(credentialDefinitionId) + seqNoMap[credentialDefinition.schemaId] = schemaSeqNo + } + + // Convert AnonCreds schemas to Indy schemas + const indySchemas: Schemas = {} + for (const schemaId in options.schemas) { + const schema = options.schemas[schemaId] + indySchemas[schemaId] = indySdkSchemaFromAnonCreds(schemaId, schema, seqNoMap[schemaId]) + } + + // Convert AnonCreds revocation definitions to Indy revocation definitions + const indyRevocationDefinitions: RevocRegDefs = {} + const indyRevocationRegistries: RevRegs = {} + + for (const revocationRegistryDefinitionId in options.revocationStates) { + const { definition, revocationLists } = options.revocationStates[revocationRegistryDefinitionId] + indyRevocationDefinitions[revocationRegistryDefinitionId] = indySdkRevocationRegistryDefinitionFromAnonCreds( + revocationRegistryDefinitionId, + definition + ) + + // Initialize empty object for this revocation registry + indyRevocationRegistries[revocationRegistryDefinitionId] = {} + + // Also transform the revocation lists for the specified timestamps into the revocation registry + // format Indy expects + for (const timestamp in revocationLists) { + const revocationList = revocationLists[timestamp] + indyRevocationRegistries[revocationRegistryDefinitionId][timestamp] = + indySdkRevocationRegistryFromAnonCreds(revocationList) + } + } + + return await this.indySdk.verifierVerifyProof( + options.proofRequest, + options.proof, + indySchemas, + indyCredentialDefinitions, + indyRevocationDefinitions, + indyRevocationRegistries + ) + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts new file mode 100644 index 0000000000..f85ec160b5 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -0,0 +1,48 @@ +import { + didFromSchemaId, + didFromCredentialDefinitionId, + didFromRevocationRegistryDefinitionId, + getIndySeqNoFromUnqualifiedCredentialDefinitionId, + getLegacyCredentialDefinitionId, + getLegacySchemaId, +} from '../identifiers' + +describe('identifiers', () => { + it('getLegacySchemaId should return a valid schema id given a did, name, and version', () => { + const did = '12345' + const name = 'backbench' + const version = '420' + + expect(getLegacySchemaId(did, name, version)).toEqual('12345:2:backbench:420') + }) + + it('getLegacyCredentialDefinitionId should return a valid credential definition id given a did, seqNo, and tag', () => { + const did = '12345' + const seqNo = 420 + const tag = 'someTag' + + expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') + }) + + it('getIndySeqNoFromUnqualifiedCredentialDefinitionId should return the seqNo from the credential definition id', () => { + expect(getIndySeqNoFromUnqualifiedCredentialDefinitionId('12345:3:CL:420:someTag')).toEqual(420) + }) + + it('didFromSchemaId should return the did from the schema id', () => { + const schemaId = '12345:2:backbench:420' + + expect(didFromSchemaId(schemaId)).toEqual('12345') + }) + + it('didFromCredentialDefinitionId should return the did from the credential definition id', () => { + const credentialDefinitionId = '12345:3:CL:420:someTag' + + expect(didFromCredentialDefinitionId(credentialDefinitionId)).toEqual('12345') + }) + + it('didFromRevocationRegistryDefinitionId should return the did from the revocation registry id', () => { + const revocationRegistryId = '12345:3:CL:420:someTag' + + expect(didFromRevocationRegistryDefinitionId(revocationRegistryId)).toEqual('12345') + }) +}) diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts new file mode 100644 index 0000000000..20b16fa0ff --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts @@ -0,0 +1,114 @@ +import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../../../../../anoncreds/src' +import type { CredDef, Schema } from 'indy-sdk' + +import { + anonCredsCredentialDefinitionFromIndySdk, + anonCredsSchemaFromIndySdk, + indySdkCredentialDefinitionFromAnonCreds, + indySdkSchemaFromAnonCreds, +} from '../transform' + +describe('transform', () => { + it('anonCredsSchemaFromIndySdk should return a valid anoncreds schema', () => { + const schema: Schema = { + attrNames: ['hello'], + id: '12345:2:Example Schema:1.0.0', + name: 'Example Schema', + seqNo: 150, + ver: '1.0', + version: '1.0.0', + } + + expect(anonCredsSchemaFromIndySdk(schema)).toEqual({ + attrNames: ['hello'], + issuerId: '12345', + name: 'Example Schema', + version: '1.0.0', + }) + }) + + it('indySdkSchemaFromAnonCreds should return a valid indy sdk schema', () => { + const schemaId = '12345:2:Example Schema:1.0.0' + const schema: AnonCredsSchema = { + attrNames: ['hello'], + issuerId: '12345', + name: 'Example Schema', + version: '1.0.0', + } + + expect(indySdkSchemaFromAnonCreds(schemaId, schema, 150)).toEqual({ + attrNames: ['hello'], + id: '12345:2:Example Schema:1.0.0', + name: 'Example Schema', + seqNo: 150, + ver: '1.0', + version: '1.0.0', + }) + }) + + it('anonCredsCredentialDefinitionFromIndySdk should return a valid anoncreds credential definition', () => { + const credDef: CredDef = { + id: '12345:3:CL:420:someTag', + schemaId: '8910:2:Example Schema:1.0.0', + tag: 'someTag', + type: 'CL', + value: { + primary: { + something: 'string', + }, + }, + ver: '1.0', + } + + expect(anonCredsCredentialDefinitionFromIndySdk(credDef)).toEqual({ + issuerId: '12345', + schemaId: '8910:2:Example Schema:1.0.0', + tag: 'someTag', + type: 'CL', + value: { + primary: { + something: 'string', + }, + }, + }) + }) + + it('indySdkCredentialDefinitionFromAnonCreds should return a valid indy sdk credential definition', () => { + const credentialDefinitionId = '12345:3:CL:420:someTag' + const credentialDefinition: AnonCredsCredentialDefinition = { + issuerId: '12345', + schemaId: '8910:2:Example Schema:1.0.0', + tag: 'someTag', + type: 'CL', + value: { + primary: { + something: 'string', + }, + }, + } + + expect(indySdkCredentialDefinitionFromAnonCreds(credentialDefinitionId, credentialDefinition)).toEqual({ + id: '12345:3:CL:420:someTag', + schemaId: '8910:2:Example Schema:1.0.0', + tag: 'someTag', + type: 'CL', + value: { + primary: { + something: 'string', + }, + }, + ver: '1.0', + }) + }) + + // TODO: add tests for these models once finalized in the anoncreds spec + test.todo( + 'anonCredsRevocationRegistryDefinitionFromIndySdk should return a valid anoncreds revocation registry definition' + ) + test.todo( + 'indySdkRevocationRegistryDefinitionFromAnonCreds should return a valid indy sdk revocation registry definition' + ) + test.todo('anonCredsRevocationListFromIndySdk should return a valid anoncreds revocation list') + test.todo('indySdkRevocationRegistryFromAnonCreds should return a valid indy sdk revocation registry') + test.todo('indySdkRevocationDeltaFromAnonCreds should return a valid indy sdk revocation delta') +}) diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts new file mode 100644 index 0000000000..bc59b5f8d4 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -0,0 +1,41 @@ +export function getIndySeqNoFromUnqualifiedCredentialDefinitionId(unqualifiedCredentialDefinitionId: string): number { + // 5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npbd + const [, , , seqNo] = unqualifiedCredentialDefinitionId.split(':') + + return Number(seqNo) +} + +export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { + return `${unqualifiedDid}:2:${name}:${version}` +} + +export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: number, tag: string) { + return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` +} + +/** + * Extract did from schema id + */ +export function didFromSchemaId(schemaId: string) { + const [did] = schemaId.split(':') + + return did +} + +/** + * Extract did from credential definition id + */ +export function didFromCredentialDefinitionId(credentialDefinitionId: string) { + const [did] = credentialDefinitionId.split(':') + + return did +} + +/** + * Extract did from revocation registry definition id + */ +export function didFromRevocationRegistryDefinitionId(revocationRegistryId: string) { + const [did] = revocationRegistryId.split(':') + + return did +} diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts new file mode 100644 index 0000000000..a5ad8afd60 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -0,0 +1,153 @@ +import type { + AnonCredsCredentialDefinition, + AnonCredsRevocationList, + AnonCredsRevocationRegistryDefinition, + AnonCredsSchema, +} from '@aries-framework/anoncreds' +import type { CredDef, RevocReg, RevocRegDef, RevocRegDelta, Schema } from 'indy-sdk' + +import { didFromCredentialDefinitionId, didFromRevocationRegistryDefinitionId, didFromSchemaId } from './identifiers' + +export function anonCredsSchemaFromIndySdk(schema: Schema): AnonCredsSchema { + const issuerId = didFromSchemaId(schema.id) + return { + issuerId, + name: schema.name, + version: schema.version, + attrNames: schema.attrNames, + } +} + +export function indySdkSchemaFromAnonCreds(schemaId: string, schema: AnonCredsSchema, indyLedgerSeqNo: number): Schema { + return { + id: schemaId, + attrNames: schema.attrNames, + name: schema.name, + version: schema.version, + ver: '1.0', + seqNo: indyLedgerSeqNo, + } +} + +export function anonCredsCredentialDefinitionFromIndySdk(credentialDefinition: CredDef): AnonCredsCredentialDefinition { + const issuerId = didFromCredentialDefinitionId(credentialDefinition.id) + + return { + issuerId, + schemaId: credentialDefinition.schemaId, + tag: credentialDefinition.tag, + type: 'CL', + value: credentialDefinition.value, + } +} + +export function indySdkCredentialDefinitionFromAnonCreds( + credentialDefinitionId: string, + credentialDefinition: AnonCredsCredentialDefinition +): CredDef { + return { + id: credentialDefinitionId, + schemaId: credentialDefinition.schemaId, + tag: credentialDefinition.tag, + type: credentialDefinition.type, + value: credentialDefinition.value, + ver: '1.0', + } +} + +export function anonCredsRevocationRegistryDefinitionFromIndySdk( + revocationRegistryDefinition: RevocRegDef +): AnonCredsRevocationRegistryDefinition { + const issuerId = didFromRevocationRegistryDefinitionId(revocationRegistryDefinition.id) + + return { + issuerId, + credDefId: revocationRegistryDefinition.credDefId, + maxCredNum: revocationRegistryDefinition.value.maxCredNum, + publicKeys: revocationRegistryDefinition.value.publicKeys, + tag: revocationRegistryDefinition.tag, + tailsHash: revocationRegistryDefinition.value.tailsHash, + tailsLocation: revocationRegistryDefinition.value.tailsLocation, + type: 'CL_ACCUM', + } +} + +export function indySdkRevocationRegistryDefinitionFromAnonCreds( + revocationRegistryDefinitionId: string, + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition +): RevocRegDef { + return { + id: revocationRegistryDefinitionId, + credDefId: revocationRegistryDefinition.credDefId, + revocDefType: revocationRegistryDefinition.type, + tag: revocationRegistryDefinition.tag, + value: { + issuanceType: 'ISSUANCE_BY_DEFAULT', // NOTE: we always use ISSUANCE_BY_DEFAULT when passing to the indy-sdk. It doesn't matter, as we have the revocation List with the full state + maxCredNum: revocationRegistryDefinition.maxCredNum, + publicKeys: revocationRegistryDefinition.publicKeys, + tailsHash: revocationRegistryDefinition.tailsHash, + tailsLocation: revocationRegistryDefinition.tailsLocation, + }, + ver: '1.0', + } +} + +export function anonCredsRevocationListFromIndySdk( + revocationRegistryDefinitionId: string, + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition, + delta: RevocRegDelta, + timestamp: number, + isIssuanceByDefault: boolean +): AnonCredsRevocationList { + // 0 means unrevoked, 1 means revoked + const defaultState = isIssuanceByDefault ? 0 : 1 + + // Fill with default value + const revocationList = new Array(revocationRegistryDefinition.maxCredNum).fill(defaultState) + + // Set all `issuer` indexes to 0 (not revoked) + for (const issued of delta.value.issued ?? []) { + revocationList[issued] = 0 + } + + // Set all `revoked` indexes to 1 (revoked) + for (const revoked of delta.value.revoked ?? []) { + revocationList[revoked] = 1 + } + + return { + issuerId: revocationRegistryDefinition.issuerId, + currentAccumulator: delta.value.accum, + revRegId: revocationRegistryDefinitionId, + revocationList, + timestamp, + } +} + +export function indySdkRevocationRegistryFromAnonCreds(revocationList: AnonCredsRevocationList): RevocReg { + return { + ver: '1.0', + value: { + accum: revocationList.currentAccumulator, + }, + } +} + +export function indySdkRevocationDeltaFromAnonCreds(revocationList: AnonCredsRevocationList): RevocRegDelta { + // Get all indices from the revocationList that are revoked (so have value '1') + const revokedIndices = revocationList.revocationList.reduce( + (revoked, current, index) => (current === 1 ? [...revoked, index] : revoked), + [] + ) + + return { + value: { + accum: revocationList.currentAccumulator, + issued: [], + revoked: revokedIndices, + // NOTE: I don't think this is used? + prevAccum: '', + }, + ver: '1.0', + } +} diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts new file mode 100644 index 0000000000..9f94c7326c --- /dev/null +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -0,0 +1,265 @@ +import type { IndySdkPool } from '../ledger' +import type { IndyEndpointAttrib } from './didSovUtil' +import type { + AgentContext, + DidRegistrar, + DidCreateOptions, + DidCreateResult, + DidDeactivateResult, + DidUpdateResult, + Key, +} from '@aries-framework/core' +import type { NymRole } from 'indy-sdk' + +import { inject, injectable, DidDocumentRole, DidRecord, DidRepository } from '@aries-framework/core' + +import { IndySdkError } from '../error' +import { isIndyError } from '../error/indyError' +import { IndySdkPoolService } from '../ledger' +import { IndySdk, IndySdkSymbol } from '../types' +import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' + +import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' + +@injectable() +export class IndySdkSovDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['sov'] + private didRepository: DidRepository + private indySdk: IndySdk + private indySdkPoolService: IndySdkPoolService + + public constructor( + didRepository: DidRepository, + indySdkPoolService: IndySdkPoolService, + @inject(IndySdkSymbol) indySdk: IndySdk + ) { + this.didRepository = didRepository + this.indySdk = indySdk + this.indySdkPoolService = indySdkPoolService + } + + public async create(agentContext: AgentContext, options: IndySdkSovDidCreateOptions): Promise { + const { alias, role, submitterDid, indyNamespace } = options.options + const seed = options.secret?.seed + + if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid seed provided', + }, + } + } + + if (!submitterDid.startsWith('did:sov:')) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Submitter did must be a valid did:sov did', + }, + } + } + + try { + // NOTE: we need to use the createAndStoreMyDid method from indy to create the did + // If we just create a key and handle the creating of the did ourselves, indy will throw a + // WalletItemNotFound when it needs to sign ledger transactions using this did. This means we need + // to rely directly on the indy SDK, as we don't want to expose a createDid method just for. + assertIndySdkWallet(agentContext.wallet) + const [unqualifiedIndyDid, verkey] = await this.indySdk.createAndStoreMyDid(agentContext.wallet.handle, { + seed, + }) + + const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` + const unqualifiedSubmitterDid = submitterDid.replace('did:sov:', '') + + // TODO: it should be possible to pass the pool used for writing to the indy ledger service. + // The easiest way to do this would be to make the submitterDid a fully qualified did, including the indy namespace. + const pool = this.indySdkPoolService.getPoolForNamespace(indyNamespace) + await this.registerPublicDid(agentContext, unqualifiedSubmitterDid, unqualifiedIndyDid, verkey, alias, pool, role) + + // Create did document + const didDocumentBuilder = sovDidDocumentFromDid(qualifiedSovDid, verkey) + + // Add services if endpoints object was passed. + if (options.options.endpoints) { + await this.setEndpointsForDid(agentContext, unqualifiedIndyDid, options.options.endpoints, pool) + addServicesFromEndpointsAttrib( + didDocumentBuilder, + qualifiedSovDid, + options.options.endpoints, + `${qualifiedSovDid}#key-agreement-1` + ) + } + + // Build did document. + const didDocument = didDocumentBuilder.build() + + const didIndyNamespace = pool.config.indyNamespace + const qualifiedIndyDid = `did:indy:${didIndyNamespace}:${unqualifiedIndyDid}` + + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + id: qualifiedSovDid, + did: qualifiedSovDid, + role: DidDocumentRole.Created, + tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), + qualifiedIndyDid, + }, + }) + await this.didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: { + qualifiedIndyDid, + }, + didRegistrationMetadata: { + didIndyNamespace, + }, + didState: { + state: 'finished', + did: qualifiedSovDid, + didDocument, + secret: { + // FIXME: the uni-registrar creates the seed in the registrar method + // if it doesn't exist so the seed can always be returned. Currently + // we can only return it if the seed was passed in by the user. Once + // we have a secure method for generating seeds we should use the same + // approach + seed: options.secret?.seed, + }, + }, + } + } catch (error) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async update(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:sov not implemented yet`, + }, + } + } + + public async deactivate(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:sov not implemented yet`, + }, + } + } + + public async registerPublicDid( + agentContext: AgentContext, + submitterDid: string, + targetDid: string, + verkey: string, + alias: string, + pool: IndySdkPool, + role?: NymRole + ) { + try { + agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`) + + const request = await this.indySdk.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) + + const response = await this.indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) + + agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`, { + response, + }) + + return targetDid + } catch (error) { + agentContext.config.logger.error( + `Error registering public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`, + { + error, + submitterDid, + targetDid, + verkey, + alias, + role, + pool: pool.didIndyNamespace, + } + ) + + throw error + } + } + + public async setEndpointsForDid( + agentContext: AgentContext, + did: string, + endpoints: IndyEndpointAttrib, + pool: IndySdkPool + ): Promise { + try { + agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, endpoints) + + const request = await this.indySdk.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) + + const response = await this.indySdkPoolService.submitWriteRequest(agentContext, pool, request, did) + agentContext.config.logger.debug( + `Successfully set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, + { + response, + endpoints, + } + ) + } catch (error) { + agentContext.config.logger.error( + `Error setting endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, + { + error, + did, + endpoints, + } + ) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} + +export interface IndySdkSovDidCreateOptions extends DidCreateOptions { + method: 'sov' + did?: undefined + // As did:sov is so limited, we require everything needed to construct the did document to be passed + // through the options object. Once we support did:indy we can allow the didDocument property. + didDocument?: never + options: { + alias: string + role?: NymRole + endpoints?: IndyEndpointAttrib + indyNamespace?: string + submitterDid: string + } + secret?: { + seed?: string + } +} + +// Update and Deactivate not supported for did:sov +export type IndySdkSovDidUpdateOptions = never +export type IndySdkSovDidDeactivateOptions = never diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts new file mode 100644 index 0000000000..c4d584568c --- /dev/null +++ b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts @@ -0,0 +1,95 @@ +import type { IndyEndpointAttrib } from './didSovUtil' +import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' + +import { inject, injectable } from '@aries-framework/core' + +import { isIndyError, IndySdkError } from '../error' +import { IndySdkPoolService } from '../ledger/IndySdkPoolService' +import { IndySdkSymbol, IndySdk } from '../types' + +import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' + +@injectable() +export class IndySdkSovDidResolver implements DidResolver { + private indySdk: IndySdk + private indySdkPoolService: IndySdkPoolService + + public constructor(indyPoolService: IndySdkPoolService, @inject(IndySdkSymbol) indySdk: IndySdk) { + this.indySdk = indySdk + this.indySdkPoolService = indyPoolService + } + + public readonly supportedMethods = ['sov'] + + public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { + const didDocumentMetadata = {} + + try { + const nym = await this.getPublicDid(agentContext, parsed.id) + const endpoints = await this.getEndpointsForDid(agentContext, parsed.id) + + const keyAgreementId = `${parsed.did}#key-agreement-1` + const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) + addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) + + return { + didDocument: builder.build(), + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } + + private async getPublicDid(agentContext: AgentContext, did: string) { + // Getting the pool for a did also retrieves the DID. We can just use that + const { did: didResponse } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + + return didResponse + } + + private async getEndpointsForDid(agentContext: AgentContext, did: string) { + const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + + try { + agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.didIndyNamespace}'`) + + const request = await this.indySdk.buildGetAttribRequest(null, did, 'endpoint', null, null) + + agentContext.config.logger.debug( + `Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.didIndyNamespace}'` + ) + const response = await this.indySdkPoolService.submitReadRequest(pool, request) + + if (!response.result.data) return {} + + const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib + agentContext.config.logger.debug( + `Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.didIndyNamespace}'`, + { + response, + endpoints, + } + ) + + return endpoints ?? {} + } catch (error) { + agentContext.config.logger.error( + `Error retrieving endpoints for did '${did}' from ledger '${pool.didIndyNamespace}'`, + { + error, + } + ) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} diff --git a/packages/indy-sdk/src/dids/didSovUtil.ts b/packages/indy-sdk/src/dids/didSovUtil.ts new file mode 100644 index 0000000000..b5af6ee3f0 --- /dev/null +++ b/packages/indy-sdk/src/dids/didSovUtil.ts @@ -0,0 +1,132 @@ +import { + TypedArrayEncoder, + DidDocumentService, + DidDocumentBuilder, + DidCommV1Service, + DidCommV2Service, + convertPublicKeyToX25519, +} from '@aries-framework/core' + +import { getFullVerkey } from '../utils/did' + +export interface IndyEndpointAttrib { + endpoint?: string + types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> + routingKeys?: string[] + [key: string]: unknown +} + +export function sovDidDocumentFromDid(fullDid: string, verkey: string) { + const verificationMethodId = `${fullDid}#key-1` + const keyAgreementId = `${fullDid}#key-agreement-1` + + const publicKeyBase58 = getFullVerkey(fullDid, verkey) + const publicKeyX25519 = TypedArrayEncoder.toBase58( + convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(publicKeyBase58)) + ) + + const builder = new DidDocumentBuilder(fullDid) + .addContext('https://w3id.org/security/suites/ed25519-2018/v1') + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: fullDid, + id: verificationMethodId, + publicKeyBase58: publicKeyBase58, + type: 'Ed25519VerificationKey2018', + }) + .addVerificationMethod({ + controller: fullDid, + id: keyAgreementId, + publicKeyBase58: publicKeyX25519, + type: 'X25519KeyAgreementKey2019', + }) + .addAuthentication(verificationMethodId) + .addAssertionMethod(verificationMethodId) + .addKeyAgreement(keyAgreementId) + + return builder +} + +// Process Indy Attrib Endpoint Types according to: https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html > Read (Resolve) > DID Service Endpoint +function processEndpointTypes(types?: string[]) { + const expectedTypes = ['endpoint', 'did-communication', 'DIDComm'] + const defaultTypes = ['endpoint', 'did-communication'] + + // Return default types if types "is NOT present [or] empty" + if (!types || types.length <= 0) { + return defaultTypes + } + + // Return default types if types "contain any other values" + for (const type of types) { + if (!expectedTypes.includes(type)) { + return defaultTypes + } + } + + // Return provided types + return types +} + +export function addServicesFromEndpointsAttrib( + builder: DidDocumentBuilder, + did: string, + endpoints: IndyEndpointAttrib, + keyAgreementId: string +) { + const { endpoint, routingKeys, types, ...otherEndpoints } = endpoints + + if (endpoint) { + const processedTypes = processEndpointTypes(types) + + // If 'endpoint' included in types, add id to the services array + if (processedTypes.includes('endpoint')) { + builder.addService( + new DidDocumentService({ + id: `${did}#endpoint`, + serviceEndpoint: endpoint, + type: 'endpoint', + }) + ) + } + + // If 'did-communication' included in types, add DIDComm v1 entry + if (processedTypes.includes('did-communication')) { + builder.addService( + new DidCommV1Service({ + id: `${did}#did-communication`, + serviceEndpoint: endpoint, + priority: 0, + routingKeys: routingKeys ?? [], + recipientKeys: [keyAgreementId], + accept: ['didcomm/aip2;env=rfc19'], + }) + ) + + // If 'DIDComm' included in types, add DIDComm v2 entry + if (processedTypes.includes('DIDComm')) { + builder + .addService( + new DidCommV2Service({ + id: `${did}#didcomm-1`, + serviceEndpoint: endpoint, + routingKeys: routingKeys ?? [], + accept: ['didcomm/v2'], + }) + ) + .addContext('https://didcomm.org/messaging/contexts/v2') + } + } + } + + // Add other endpoint types + for (const [type, endpoint] of Object.entries(otherEndpoints)) { + builder.addService( + new DidDocumentService({ + id: `${did}#${type}`, + serviceEndpoint: endpoint as string, + type, + }) + ) + } +} diff --git a/packages/indy-sdk/src/dids/index.ts b/packages/indy-sdk/src/dids/index.ts new file mode 100644 index 0000000000..68eabe204d --- /dev/null +++ b/packages/indy-sdk/src/dids/index.ts @@ -0,0 +1,7 @@ +export { + IndySdkSovDidRegistrar, + IndySdkSovDidCreateOptions, + IndySdkSovDidDeactivateOptions, + IndySdkSovDidUpdateOptions, +} from './IndySdkSovDidRegistrar' +export { IndySdkSovDidResolver } from './IndySdkSovDidResolver' diff --git a/packages/indy-sdk/src/error/IndySdkError.ts b/packages/indy-sdk/src/error/IndySdkError.ts new file mode 100644 index 0000000000..4b67802a9a --- /dev/null +++ b/packages/indy-sdk/src/error/IndySdkError.ts @@ -0,0 +1,11 @@ +import type { IndyError } from './indyError' + +import { AriesFrameworkError } from '@aries-framework/core' + +export class IndySdkError extends AriesFrameworkError { + public constructor(indyError: IndyError, message?: string) { + const base = `${indyError.name}(${indyError.indyName}): ${indyError.message}` + + super(message ? `${message}: ${base}` : base, { cause: indyError }) + } +} diff --git a/packages/indy-sdk/src/error/index.ts b/packages/indy-sdk/src/error/index.ts new file mode 100644 index 0000000000..5829a46d0a --- /dev/null +++ b/packages/indy-sdk/src/error/index.ts @@ -0,0 +1,2 @@ +export * from './IndySdkError' +export * from './indyError' diff --git a/packages/indy-sdk/src/error/indyError.ts b/packages/indy-sdk/src/error/indyError.ts new file mode 100644 index 0000000000..5d67cfdbf1 --- /dev/null +++ b/packages/indy-sdk/src/error/indyError.ts @@ -0,0 +1,100 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +export const indyErrors = { + 100: 'CommonInvalidParam1', + 101: 'CommonInvalidParam2', + 102: 'CommonInvalidParam3', + 103: 'CommonInvalidParam4', + 104: 'CommonInvalidParam5', + 105: 'CommonInvalidParam6', + 106: 'CommonInvalidParam7', + 107: 'CommonInvalidParam8', + 108: 'CommonInvalidParam9', + 109: 'CommonInvalidParam10', + 110: 'CommonInvalidParam11', + 111: 'CommonInvalidParam12', + 112: 'CommonInvalidState', + 113: 'CommonInvalidStructure', + 114: 'CommonIOError', + 115: 'CommonInvalidParam13', + 116: 'CommonInvalidParam14', + 200: 'WalletInvalidHandle', + 201: 'WalletUnknownTypeError', + 202: 'WalletTypeAlreadyRegisteredError', + 203: 'WalletAlreadyExistsError', + 204: 'WalletNotFoundError', + 205: 'WalletIncompatiblePoolError', + 206: 'WalletAlreadyOpenedError', + 207: 'WalletAccessFailed', + 208: 'WalletInputError', + 209: 'WalletDecodingError', + 210: 'WalletStorageError', + 211: 'WalletEncryptionError', + 212: 'WalletItemNotFound', + 213: 'WalletItemAlreadyExists', + 214: 'WalletQueryError', + 300: 'PoolLedgerNotCreatedError', + 301: 'PoolLedgerInvalidPoolHandle', + 302: 'PoolLedgerTerminated', + 303: 'LedgerNoConsensusError', + 304: 'LedgerInvalidTransaction', + 305: 'LedgerSecurityError', + 306: 'PoolLedgerConfigAlreadyExistsError', + 307: 'PoolLedgerTimeout', + 308: 'PoolIncompatibleProtocolVersion', + 309: 'LedgerNotFound', + 400: 'AnoncredsRevocationRegistryFullError', + 401: 'AnoncredsInvalidUserRevocId', + 404: 'AnoncredsMasterSecretDuplicateNameError', + 405: 'AnoncredsProofRejected', + 406: 'AnoncredsCredentialRevoked', + 407: 'AnoncredsCredDefAlreadyExistsError', + 500: 'UnknownCryptoTypeError', + 600: 'DidAlreadyExistsError', + 700: 'PaymentUnknownMethodError', + 701: 'PaymentIncompatibleMethodsError', + 702: 'PaymentInsufficientFundsError', + 703: 'PaymentSourceDoesNotExistError', + 704: 'PaymentOperationNotSupportedError', + 705: 'PaymentExtraFundsError', + 706: 'TransactionNotAllowedError', +} as const + +type IndyErrorValues = typeof indyErrors[keyof typeof indyErrors] + +export interface IndyError { + name: 'IndyError' + message: string + indyName?: string +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function isIndyError(error: any, errorName?: IndyErrorValues): error is IndyError { + if (typeof error !== 'object' || error === null) return false + + const indyError = error.name === 'IndyError' + + // if no specific indy error name is passed + // or the error is no indy error + // we can already return + if (!indyError || !errorName) return indyError + + // NodeJS Wrapper is missing some type names. When a type is missing it will + // only have the error code as string in the message field + // Until that is fixed we take that into account to make AFJ work with rn-indy-sdk + // See: https://github.com/AbsaOSS/rn-indy-sdk/pull/24 + // See: https://github.com/hyperledger/indy-sdk/pull/2283 + if (!error.indyName) { + const errorCode = Number(error.message) + if (!isNaN(errorCode) && Object.prototype.hasOwnProperty.call(indyErrors, errorCode)) { + // We already check if the property is set. We can safely ignore this typescript error + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return errorName === indyErrors[errorCode] + } + + throw new AriesFrameworkError(`Could not determine errorName of indyError ${error.message}`) + } + + return error.indyName === errorName +} diff --git a/packages/indy-sdk/src/index.ts b/packages/indy-sdk/src/index.ts new file mode 100644 index 0000000000..ea099b7cf4 --- /dev/null +++ b/packages/indy-sdk/src/index.ts @@ -0,0 +1,26 @@ +// Dids +export { + IndySdkSovDidRegistrar, + IndySdkSovDidCreateOptions, + IndySdkSovDidDeactivateOptions, + IndySdkSovDidUpdateOptions, + IndySdkSovDidResolver, +} from './dids' + +// Wallet +export { IndySdkWallet } from './wallet' + +// Storage +export { IndySdkStorageService } from './storage' + +// AnonCreds +export { + IndySdkAnonCredsRegistry, + IndySdkHolderService, + IndySdkIssuerService, + IndySdkVerifierService, +} from './anoncreds' + +// Module +export { IndySdkModule } from './IndySdkModule' +export { IndySdkModuleConfig } from './IndySdkModuleConfig' diff --git a/packages/indy-sdk/src/ledger/IndySdkPool.ts b/packages/indy-sdk/src/ledger/IndySdkPool.ts new file mode 100644 index 0000000000..a24a1c7ba5 --- /dev/null +++ b/packages/indy-sdk/src/ledger/IndySdkPool.ts @@ -0,0 +1,208 @@ +import type { IndySdk } from '../types' +import type { FileSystem, Logger } from '@aries-framework/core' +import type { LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' +import type { Subject } from 'rxjs' + +import { AriesFrameworkError } from '@aries-framework/core' + +import { isIndyError, IndySdkError } from '../error' + +import { IndySdkPoolError } from './error' +import { isLedgerRejectResponse, isLedgerReqnackResponse } from './util' + +export interface TransactionAuthorAgreement { + version: `${number}.${number}` | `${number}` + acceptanceMechanism: string +} + +export interface IndySdkPoolConfig { + genesisPath?: string + genesisTransactions?: string + id: string + isProduction: boolean + indyNamespace: string + transactionAuthorAgreement?: TransactionAuthorAgreement +} + +export class IndySdkPool { + private indySdk: IndySdk + private logger: Logger + private fileSystem: FileSystem + private poolConfig: IndySdkPoolConfig + private _poolHandle?: number + private poolConnected?: Promise + public authorAgreement?: AuthorAgreement | null + + public constructor( + poolConfig: IndySdkPoolConfig, + indySdk: IndySdk, + logger: Logger, + stop$: Subject, + fileSystem: FileSystem + ) { + this.indySdk = indySdk + this.fileSystem = fileSystem + this.poolConfig = poolConfig + this.logger = logger + + // Listen to stop$ (shutdown) and close pool + stop$.subscribe(async () => { + if (this._poolHandle) { + await this.close() + } + }) + } + + public get didIndyNamespace(): string { + return this.didIndyNamespace + } + + public get id() { + return this.poolConfig.id + } + + public get config() { + return this.poolConfig + } + + public async close() { + const poolHandle = this._poolHandle + + if (!poolHandle) { + return + } + + this._poolHandle = undefined + this.poolConnected = undefined + + await this.indySdk.closePoolLedger(poolHandle) + } + + public async delete() { + // Close the pool if currently open + if (this._poolHandle) { + await this.close() + } + + await this.indySdk.deletePoolLedgerConfig(this.poolConfig.id) + } + + public async connect() { + if (!this.poolConnected) { + // Save the promise of connectToLedger to determine if we are done connecting + this.poolConnected = this.connectToLedger() + this.poolConnected.catch((error) => { + // Set poolConnected to undefined so we can retry connection upon failure + this.poolConnected = undefined + this.logger.error('Connection to pool: ' + this.poolConfig.genesisPath + ' failed.', { error }) + }) + return this.poolConnected + } else { + throw new AriesFrameworkError('Cannot attempt connection to ledger, already connecting.') + } + } + + private async connectToLedger() { + const poolName = this.poolConfig.id + const genesisPath = await this.getGenesisPath() + + if (!genesisPath) { + throw new AriesFrameworkError('Cannot connect to ledger without genesis file') + } + + this.logger.debug(`Connecting to ledger pool '${poolName}'`, { genesisPath }) + await this.indySdk.setProtocolVersion(2) + + try { + this._poolHandle = await this.indySdk.openPoolLedger(poolName) + return this._poolHandle + } catch (error) { + if (!isIndyError(error, 'PoolLedgerNotCreatedError')) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + this.logger.debug(`Pool '${poolName}' does not exist yet, creating.`, { + indyError: 'PoolLedgerNotCreatedError', + }) + try { + await this.indySdk.createPoolLedgerConfig(poolName, { genesis_txn: genesisPath }) + this._poolHandle = await this.indySdk.openPoolLedger(poolName) + return this._poolHandle + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async submitRequest(request: LedgerRequest) { + return this.indySdk.submitRequest(await this.getPoolHandle(), request) + } + + public async submitReadRequest(request: LedgerRequest) { + const response = await this.submitRequest(request) + + if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { + throw new IndySdkPoolError(`Ledger '${this.id}' rejected read transaction request: ${response.reason}`) + } + + return response as LedgerReadReplyResponse + } + + public async submitWriteRequest(request: LedgerRequest) { + const response = await this.submitRequest(request) + + if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { + throw new IndySdkPoolError(`Ledger '${this.id}' rejected write transaction request: ${response.reason}`) + } + + return response as LedgerWriteReplyResponse + } + + private async getPoolHandle() { + if (this.poolConnected) { + // If we have tried to already connect to pool wait for it + try { + await this.poolConnected + } catch (error) { + this.logger.error('Connection to pool: ' + this.poolConfig.genesisPath + ' failed.', { error }) + } + } + + if (!this._poolHandle) { + return this.connect() + } + + return this._poolHandle + } + + private async getGenesisPath() { + // If the path is already provided return it + if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath + + // Determine the genesisPath + const genesisPath = this.fileSystem.basePath + `/afj/genesis-${this.poolConfig.id}.txn` + // Store genesis data if provided + if (this.poolConfig.genesisTransactions) { + await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) + this.poolConfig.genesisPath = genesisPath + return genesisPath + } + + // No genesisPath + return null + } +} + +export interface AuthorAgreement { + digest: string + version: string + text: string + ratification_ts: number + acceptanceMechanisms: AcceptanceMechanisms +} + +export interface AcceptanceMechanisms { + aml: Record + amlContext: string + version: string +} diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts new file mode 100644 index 0000000000..773b7db2cc --- /dev/null +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -0,0 +1,338 @@ +import type { AcceptanceMechanisms, AuthorAgreement, IndySdkPoolConfig } from './IndySdkPool' +import type { AgentContext } from '@aries-framework/core' +import type { GetNymResponse, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' + +import { + InjectionSymbols, + Logger, + injectable, + inject, + FileSystem, + CacheRepository, + PersistedLruCache, +} from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { IndySdkError, isIndyError } from '../error' +import { IndySdk } from '../types' +import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' +import { isSelfCertifiedDid } from '../utils/did' +import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' + +import { IndySdkPool } from './IndySdkPool' +import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from './error' + +export const INDY_SDK_DID_POOL_CACHE_ID = 'INDY_SDK_DID_POOL_CACHE' +export const INDY_SDK_DID_POOL_CACHE_LIMIT = 500 +export interface CachedDidResponse { + nymResponse: GetNymResponse + poolId: string +} + +@injectable() +export class IndySdkPoolService { + public pools: IndySdkPool[] = [] + private logger: Logger + private indySdk: IndySdk + private stop$: Subject + private fileSystem: FileSystem + private didCache: PersistedLruCache + + public constructor( + cacheRepository: CacheRepository, + indySdk: IndySdk, + @inject(InjectionSymbols.Logger) logger: Logger, + @inject(InjectionSymbols.Stop$) stop$: Subject, + @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem + ) { + this.logger = logger + this.indySdk = indySdk + this.fileSystem = fileSystem + this.stop$ = stop$ + + this.didCache = new PersistedLruCache(INDY_SDK_DID_POOL_CACHE_ID, INDY_SDK_DID_POOL_CACHE_LIMIT, cacheRepository) + } + + public setPools(poolConfigs: IndySdkPoolConfig[]) { + this.pools = poolConfigs.map( + (poolConfig) => new IndySdkPool(poolConfig, this.indySdk, this.logger, this.stop$, this.fileSystem) + ) + } + + /** + * Create connections to all ledger pools + */ + public async connectToPools() { + const handleArray: number[] = [] + // Sequentially connect to pools so we don't use up too many resources connecting in parallel + for (const pool of this.pools) { + this.logger.debug(`Connecting to pool: ${pool.id}`) + const poolHandle = await pool.connect() + this.logger.debug(`Finished connection to pool: ${pool.id}`) + handleArray.push(poolHandle) + } + return handleArray + } + + /** + * Get the most appropriate pool for the given did. The algorithm is based on the approach as described in this document: + * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit + */ + public async getPoolForDid( + agentContext: AgentContext, + did: string + ): Promise<{ pool: IndySdkPool; did: GetNymResponse }> { + const pools = this.pools + + if (pools.length === 0) { + throw new IndySdkPoolNotConfiguredError( + "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + ) + } + + const cachedNymResponse = await this.didCache.get(agentContext, did) + const pool = this.pools.find((pool) => pool.id === cachedNymResponse?.poolId) + + // If we have the nym response with associated pool in the cache, we'll use that + if (cachedNymResponse && pool) { + this.logger.trace(`Found ledger id '${pool.id}' for did '${did}' in cache`) + return { did: cachedNymResponse.nymResponse, pool } + } + + const { successful, rejected } = await this.getSettledDidResponsesFromPools(did, pools) + + if (successful.length === 0) { + const allNotFound = rejected.every((e) => e.reason instanceof IndySdkPoolNotFoundError) + const rejectedOtherThanNotFound = rejected.filter((e) => !(e.reason instanceof IndySdkPoolNotFoundError)) + + // All ledgers returned response that the did was not found + if (allNotFound) { + throw new IndySdkPoolNotFoundError(`Did '${did}' not found on any of the ledgers (total ${this.pools.length}).`) + } + + // one or more of the ledgers returned an unknown error + throw new IndySdkPoolError( + `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers`, + { cause: rejectedOtherThanNotFound[0].reason } + ) + } + + // If there are self certified DIDs we always prefer it over non self certified DIDs + // We take the first self certifying DID as we take the order in the + // indyLedgers config as the order of preference of ledgers + let value = successful.find((response) => + isSelfCertifiedDid(response.value.did.did, response.value.did.verkey) + )?.value + + if (!value) { + // Split between production and nonProduction ledgers. If there is at least one + // successful response from a production ledger, only keep production ledgers + // otherwise we only keep the non production ledgers. + const production = successful.filter((s) => s.value.pool.config.isProduction) + const nonProduction = successful.filter((s) => !s.value.pool.config.isProduction) + const productionOrNonProduction = production.length >= 1 ? production : nonProduction + + // We take the first value as we take the order in the indyLedgers config as + // the order of preference of ledgers + value = productionOrNonProduction[0].value + } + + await this.didCache.set(agentContext, did, { + nymResponse: value.did, + poolId: value.pool.id, + }) + return { pool: value.pool, did: value.did } + } + + private async getSettledDidResponsesFromPools(did: string, pools: IndySdkPool[]) { + this.logger.trace(`Retrieving did '${did}' from ${pools.length} ledgers`) + const didResponses = await allSettled(pools.map((pool) => this.getDidFromPool(did, pool))) + + const successful = onlyFulfilled(didResponses) + this.logger.trace(`Retrieved ${successful.length} responses from ledgers for did '${did}'`) + + const rejected = onlyRejected(didResponses) + + return { + rejected, + successful, + } + } + + /** + * Get the most appropriate pool for the given indyNamespace + */ + public getPoolForNamespace(indyNamespace?: string) { + if (this.pools.length === 0) { + throw new IndySdkPoolNotConfiguredError( + "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + ) + } + + if (!indyNamespace) { + this.logger.warn('Not passing the indyNamespace is deprecated and will be removed in the future version.') + return this.pools[0] + } + + const pool = this.pools.find((pool) => pool.didIndyNamespace === indyNamespace) + + if (!pool) { + throw new IndySdkPoolNotFoundError(`No ledgers found for IndyNamespace '${indyNamespace}'.`) + } + + return pool + } + + public async submitWriteRequest( + agentContext: AgentContext, + pool: IndySdkPool, + request: LedgerRequest, + signDid: string + ): Promise { + try { + const requestWithTaa = await this.appendTaa(pool, request) + const signedRequestWithTaa = await this.signRequest(agentContext, signDid, requestWithTaa) + + const response = await pool.submitWriteRequest(signedRequestWithTaa) + + return response + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async submitReadRequest(pool: IndySdkPool, request: LedgerRequest): Promise { + try { + const response = await pool.submitReadRequest(request) + + return response + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async signRequest(agentContext: AgentContext, did: string, request: LedgerRequest): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + return this.indySdk.signRequest(agentContext.wallet.handle, did, request) + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async appendTaa(pool: IndySdkPool, request: LedgerRequest) { + try { + const authorAgreement = await this.getTransactionAuthorAgreement(pool) + const taa = pool.config.transactionAuthorAgreement + + // If ledger does not have TAA, we can just send request + if (authorAgreement == null) { + return request + } + // Ledger has taa but user has not specified which one to use + if (!taa) { + throw new IndySdkPoolError( + `Please, specify a transaction author agreement with version and acceptance mechanism. ${JSON.stringify( + authorAgreement + )}` + ) + } + + // Throw an error if the pool doesn't have the specified version and acceptance mechanism + if ( + authorAgreement.version !== taa.version || + !(taa.acceptanceMechanism in authorAgreement.acceptanceMechanisms.aml) + ) { + // Throw an error with a helpful message + const errMessage = `Unable to satisfy matching TAA with mechanism ${JSON.stringify( + taa.acceptanceMechanism + )} and version ${JSON.stringify(taa.version)} in pool.\n Found ${JSON.stringify( + Object.keys(authorAgreement.acceptanceMechanisms.aml) + )} and version ${authorAgreement.version} in pool.` + throw new IndySdkPoolError(errMessage) + } + + const requestWithTaa = await this.indySdk.appendTxnAuthorAgreementAcceptanceToRequest( + request, + authorAgreement.text, + taa.version, + authorAgreement.digest, + taa.acceptanceMechanism, + // Current time since epoch + // We can't use ratification_ts, as it must be greater than 1499906902 + Math.floor(new Date().getTime() / 1000) + ) + + return requestWithTaa + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async getTransactionAuthorAgreement(pool: IndySdkPool): Promise { + try { + // TODO Replace this condition with memoization + if (pool.authorAgreement !== undefined) { + return pool.authorAgreement + } + + const taaRequest = await this.indySdk.buildGetTxnAuthorAgreementRequest(null) + const taaResponse = await this.submitReadRequest(pool, taaRequest) + const acceptanceMechanismRequest = await this.indySdk.buildGetAcceptanceMechanismsRequest(null) + const acceptanceMechanismResponse = await this.submitReadRequest(pool, acceptanceMechanismRequest) + + // TAA can be null + if (taaResponse.result.data == null) { + pool.authorAgreement = null + return null + } + + // If TAA is not null, we can be sure AcceptanceMechanisms is also not null + const authorAgreement = taaResponse.result.data as AuthorAgreement + const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms + pool.authorAgreement = { + ...authorAgreement, + acceptanceMechanisms, + } + return pool.authorAgreement + } catch (error) { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async getDidFromPool(did: string, pool: IndySdkPool): Promise { + try { + this.logger.trace(`Get public did '${did}' from ledger '${pool.id}'`) + const request = await this.indySdk.buildGetNymRequest(null, did) + + this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.id}'`) + const response = await pool.submitReadRequest(request) + + const result = await this.indySdk.parseGetNymResponse(response) + this.logger.trace(`Retrieved did '${did}' from ledger '${pool.id}'`, result) + + return { + did: result, + pool, + response, + } + } catch (error) { + this.logger.trace(`Error retrieving did '${did}' from ledger '${pool.id}'`, { + error, + did, + }) + if (isIndyError(error, 'LedgerNotFound')) { + throw new IndySdkPoolNotFoundError(`Did '${did}' not found on ledger ${pool.id}`) + } else { + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + } +} + +export interface PublicDidRequest { + did: GetNymResponse + pool: IndySdkPool + response: LedgerReadReplyResponse +} diff --git a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts new file mode 100644 index 0000000000..74debe2656 --- /dev/null +++ b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts @@ -0,0 +1,444 @@ +import type { IndySdkPoolConfig } from '../IndySdkPool' +import type { CachedDidResponse } from '../IndySdkPoolService' +import type { AgentContext } from '@aries-framework/core' + +import { SigningProviderRegistry, AriesFrameworkError } from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { CacheRecord } from '../../../../core/src/cache' +import { CacheRepository } from '../../../../core/src/cache/CacheRepository' +import { getDidResponsesForDid } from '../../../../core/src/modules/ledger/__tests__/didResponses' +import { agentDependencies, getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import { NodeFileSystem } from '../../../../node/src/NodeFileSystem' +import { IndySdkWallet } from '../../wallet/IndySdkWallet' +import { INDY_SDK_DID_POOL_CACHE_ID, IndySdkPoolService } from '../IndySdkPoolService' +import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from '../error' + +jest.mock('../../../../core/src/cache/CacheRepository') +const CacheRepositoryMock = CacheRepository as jest.Mock + +const pools: IndySdkPoolConfig[] = [ + { + id: 'sovrinMain', + indyNamespace: 'sovrin', + isProduction: true, + genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, + { + id: 'sovrinBuilder', + indyNamespace: 'sovrin:builder', + isProduction: false, + genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, + { + id: 'sovringStaging', + indyNamespace: 'sovrin:staging', + isProduction: false, + genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, + { + id: 'indicioMain', + indyNamespace: 'indicio', + isProduction: true, + genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, + { + id: 'bcovrinTest', + indyNamespace: 'bcovrin:test', + isProduction: false, + genesisTransactions: 'xxx', + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, +] + +describe('IndySdkPoolService', () => { + const config = getAgentConfig('IndySdkPoolServiceTest', { + indyLedgers: pools, + }) + let agentContext: AgentContext + let wallet: IndySdkWallet + let poolService: IndySdkPoolService + let cacheRepository: CacheRepository + + beforeAll(async () => { + wallet = new IndySdkWallet(config.agentDependencies.indy, config.logger, new SigningProviderRegistry([])) + agentContext = getAgentContext() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(config.walletConfig!) + }) + + afterAll(async () => { + await wallet.delete() + }) + + beforeEach(async () => { + cacheRepository = new CacheRepositoryMock() + mockFunction(cacheRepository.findById).mockResolvedValue(null) + + poolService = new IndySdkPoolService( + cacheRepository, + agentDependencies.indy, + config.logger, + new Subject(), + new NodeFileSystem() + ) + + poolService.setPools(pools) + }) + + describe('getPoolForDid', () => { + it('should throw a IndySdkPoolNotConfiguredError error if no pools are configured on the pool service', async () => { + poolService.setPools([]) + + expect(poolService.getPoolForDid(agentContext, 'some-did')).rejects.toThrow(IndySdkPoolNotConfiguredError) + }) + + it('should throw a IndySdkPoolError if all ledger requests throw an error other than NotFoundError', async () => { + const did = 'Y5bj4SjCiTM9PgeheKAiXx' + + poolService.pools.forEach((pool) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(() => Promise.reject(new AriesFrameworkError('Something went wrong'))) + }) + + expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(IndySdkPoolError) + }) + + it('should throw a IndySdkPoolNotFoundError if all pools did not find the did on the ledger', async () => { + const did = 'Y5bj4SjCiTM9PgeheKAiXx' + // Not found on any of the ledgers + const responses = getDidResponsesForDid(did, pools, {}) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(IndySdkPoolNotFoundError) + }) + + it('should return the pool if the did was only found on one ledger', async () => { + const did = 'TL1EaPFCZ8Si5aUrqScBDt' + // Only found on one ledger + const responses = getDidResponsesForDid(did, pools, { + sovrinMain: '~43X4NhAFqREffK7eWdKgFH', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe('sovrinMain') + }) + + it('should return the first pool with a self certifying DID if at least one did is self certifying ', async () => { + const did = 'did:sov:q7ATwTYbQDgiigVijUAej' + // Found on one production and one non production ledger + const responses = getDidResponsesForDid(did, pools, { + indicioMain: '~43X4NhAFqREffK7eWdKgFH', + bcovrinTest: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + sovrinBuilder: '~43X4NhAFqREffK7eWdKgFH', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe('sovrinBuilder') + }) + + it('should return the production pool if the did was found on one production and one non production ledger and both DIDs are not self certifying', async () => { + const did = 'V6ty6ttM3EjuCtosH6sGtW' + // Found on one production and one non production ledger + const responses = getDidResponsesForDid(did, pools, { + indicioMain: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + sovrinBuilder: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe('indicioMain') + }) + + it('should return the pool with the self certified did if the did was found on two production ledgers where one did is self certified', async () => { + const did = 'VsKV7grR1BUE29mG2Fm2kX' + // Found on two production ledgers. Sovrin is self certified + const responses = getDidResponsesForDid(did, pools, { + sovrinMain: '~43X4NhAFqREffK7eWdKgFH', + indicioMain: 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe('sovrinMain') + }) + + it('should return the first pool with a self certified did if the did was found on three non production ledgers where two DIDs are self certified', async () => { + const did = 'HEi9QViXNThGQaDsQ3ptcw' + // Found on two non production ledgers. Sovrin is self certified + const responses = getDidResponsesForDid(did, pools, { + sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', + sovrinStaging: '~M9kv2Ez61cur7X39DXWh8W', + bcovrinTest: '3SeuRm3uYuQDYmHeuMLu1xNHozNTtzS3kbZRFMMCWrX4', + }) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe('sovrinBuilder') + }) + + it('should return the pool from the cache if the did was found in the cache', async () => { + const did = 'HEi9QViXNThGQaDsQ3ptcw' + + const expectedPool = pools[3] + + const didResponse: CachedDidResponse = { + nymResponse: { + did, + role: 'ENDORSER', + verkey: '~M9kv2Ez61cur7X39DXWh8W', + }, + poolId: expectedPool.id, + } + + const cachedEntries = [ + { + key: did, + value: didResponse, + }, + ] + + mockFunction(cacheRepository.findById).mockResolvedValue( + new CacheRecord({ + id: INDY_SDK_DID_POOL_CACHE_ID, + entries: cachedEntries, + }) + ) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe(pool.id) + }) + + it('should set the poolId in the cache if the did was not found in the cache, but resolved later on', async () => { + const did = 'HEi9QViXNThGQaDsQ3ptcw' + // Found on one ledger + const responses = getDidResponsesForDid(did, pools, { + sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', + }) + + mockFunction(cacheRepository.findById).mockResolvedValue( + new CacheRecord({ + id: INDY_SDK_DID_POOL_CACHE_ID, + entries: [], + }) + ) + + const spy = mockFunction(cacheRepository.update).mockResolvedValue() + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'submitReadRequest') + spy.mockImplementationOnce(responses[index]) + }) + + const { pool } = await poolService.getPoolForDid(agentContext, did) + + expect(pool.config.id).toBe('sovrinBuilder') + expect(pool.config.indyNamespace).toBe('sovrin:builder') + + const cacheRecord = spy.mock.calls[0][1] + expect(cacheRecord.entries.length).toBe(1) + expect(cacheRecord.entries[0].key).toBe(did) + expect(cacheRecord.entries[0].value).toEqual({ + nymResponse: { + did, + verkey: '~M9kv2Ez61cur7X39DXWh8W', + role: '0', + }, + poolId: 'sovrinBuilder', + }) + }) + }) + + describe('getPoolForNamespace', () => { + it('should throw a IndySdkPoolNotConfiguredError error if no pools are configured on the pool service', async () => { + poolService.setPools([]) + + expect(() => poolService.getPoolForNamespace()).toThrow(IndySdkPoolNotConfiguredError) + }) + + it('should return the first pool if indyNamespace is not provided', async () => { + const expectedPool = pools[0] + + expect(poolService.getPoolForNamespace().id).toEqual(expectedPool.id) + }) + + it('should throw a IndySdkPoolNotFoundError error if any of the pools did not have the provided indyNamespace', async () => { + const indyNameSpace = 'test' + const responses = pools.map((pool) => pool.indyNamespace) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'didIndyNamespace', 'get') + spy.mockReturnValueOnce(responses[index]) + }) + + expect(() => poolService.getPoolForNamespace(indyNameSpace)).toThrow(IndySdkPoolNotFoundError) + }) + + it('should return the first pool that indyNamespace matches', async () => { + const expectedPool = pools[3] + const indyNameSpace = 'indicio' + const responses = pools.map((pool) => pool.indyNamespace) + + poolService.pools.forEach((pool, index) => { + const spy = jest.spyOn(pool, 'didIndyNamespace', 'get') + spy.mockReturnValueOnce(responses[index]) + }) + + const pool = poolService.getPoolForNamespace(indyNameSpace) + + expect(pool.id).toEqual(expectedPool.id) + }) + }) + + describe('submitWriteRequest', () => { + it('should throw an error if the config version does not match', async () => { + const pool = poolService.getPoolForNamespace() + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ + digest: 'abcde', + version: '2.0', + text: 'jhsdhbv', + ratification_ts: 12345678, + acceptanceMechanisms: { + aml: { accept: 'accept' }, + amlContext: 'accept', + version: '3', + }, + } as never) + await expect( + poolService.submitWriteRequest( + agentContext, + pool, + { + reqId: 1668174449192969000, + identifier: 'BBPoJqRKatdcfLEAFL7exC', + operation: { + type: '1', + dest: 'N8NQHLtCKfPmWMgCSdfa7h', + verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', + alias: 'Heinz57', + }, + protocolVersion: 2, + }, + 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + ) + ).rejects.toThrowError( + 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["accept"] and version 2.0 in pool.' + ) + }) + + it('should throw an error if the config acceptance mechanism does not match', async () => { + const pool = poolService.getPoolForNamespace() + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ + digest: 'abcde', + version: '1.0', + text: 'jhsdhbv', + ratification_ts: 12345678, + acceptanceMechanisms: { + aml: { decline: 'accept' }, + amlContext: 'accept', + version: '1', + }, + } as never) + await expect( + poolService.submitWriteRequest( + agentContext, + pool, + { + reqId: 1668174449192969000, + identifier: 'BBPoJqRKatdcfLEAFL7exC', + operation: { + type: '1', + dest: 'N8NQHLtCKfPmWMgCSdfa7h', + verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', + alias: 'Heinz57', + }, + protocolVersion: 2, + }, + 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + ) + ).rejects.toThrowError( + 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["decline"] and version 1.0 in pool.' + ) + }) + + it('should throw an error if no config is present', async () => { + const pool = poolService.getPoolForNamespace() + pool.authorAgreement = undefined + pool.config.transactionAuthorAgreement = undefined + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ + digest: 'abcde', + version: '1.0', + text: 'jhsdhbv', + ratification_ts: 12345678, + acceptanceMechanisms: { + aml: { accept: 'accept' }, + amlContext: 'accept', + version: '3', + }, + } as never) + await expect( + poolService.submitWriteRequest( + agentContext, + pool, + { + reqId: 1668174449192969000, + identifier: 'BBPoJqRKatdcfLEAFL7exC', + operation: { + type: '1', + dest: 'N8NQHLtCKfPmWMgCSdfa7h', + verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', + alias: 'Heinz57', + }, + protocolVersion: 2, + }, + 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + ) + ).rejects.toThrowError(/Please, specify a transaction author agreement with version and acceptance mechanism/) + }) + }) +}) diff --git a/packages/indy-sdk/src/ledger/__tests__/util.test.ts b/packages/indy-sdk/src/ledger/__tests__/util.test.ts new file mode 100644 index 0000000000..38976758ae --- /dev/null +++ b/packages/indy-sdk/src/ledger/__tests__/util.test.ts @@ -0,0 +1,45 @@ +import type { LedgerRejectResponse, LedgerReqnackResponse } from 'indy-sdk' + +import * as LedgerUtil from '../util' + +describe('LedgerUtils', () => { + // IsLedgerRejectResponse + it('Should return true if the response op is: REJECT', () => { + const ledgerResponse: LedgerRejectResponse = { + op: 'REJECT', + reqId: 1, + reason: 'Why not', + identifier: '123456', + } + expect(LedgerUtil.isLedgerRejectResponse(ledgerResponse)).toEqual(true) + }) + it('Should return false if the response op is not: REJECT', () => { + const ledgerResponse: LedgerReqnackResponse = { + op: 'REQNACK', + reqId: 1, + reason: 'Why not', + identifier: '123456', + } + expect(LedgerUtil.isLedgerRejectResponse(ledgerResponse)).toEqual(false) + }) + + // isLedgerReqnackResponse + it('Should return true if the response op is: REQNACK', () => { + const ledgerResponse: LedgerReqnackResponse = { + op: 'REQNACK', + reqId: 1, + reason: 'Why not', + identifier: '123456', + } + expect(LedgerUtil.isLedgerReqnackResponse(ledgerResponse)).toEqual(true) + }) + it('Should return false if the response op is NOT: REQNACK', () => { + const ledgerResponse: LedgerRejectResponse = { + op: 'REJECT', + reqId: 1, + reason: 'Why not', + identifier: '123456', + } + expect(LedgerUtil.isLedgerReqnackResponse(ledgerResponse)).toEqual(false) + }) +}) diff --git a/packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts b/packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts new file mode 100644 index 0000000000..fa6679d789 --- /dev/null +++ b/packages/indy-sdk/src/ledger/error/IndySdkPoolError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +export class IndySdkPoolError extends AriesFrameworkError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/indy-sdk/src/ledger/error/IndySdkPoolNotConfiguredError.ts b/packages/indy-sdk/src/ledger/error/IndySdkPoolNotConfiguredError.ts new file mode 100644 index 0000000000..91cd3c7199 --- /dev/null +++ b/packages/indy-sdk/src/ledger/error/IndySdkPoolNotConfiguredError.ts @@ -0,0 +1,7 @@ +import { IndySdkPoolError } from './IndySdkPoolError' + +export class IndySdkPoolNotConfiguredError extends IndySdkPoolError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/indy-sdk/src/ledger/error/IndySdkPoolNotFoundError.ts b/packages/indy-sdk/src/ledger/error/IndySdkPoolNotFoundError.ts new file mode 100644 index 0000000000..4977428cba --- /dev/null +++ b/packages/indy-sdk/src/ledger/error/IndySdkPoolNotFoundError.ts @@ -0,0 +1,7 @@ +import { IndySdkPoolError } from './IndySdkPoolError' + +export class IndySdkPoolNotFoundError extends IndySdkPoolError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/indy-sdk/src/ledger/error/index.ts b/packages/indy-sdk/src/ledger/error/index.ts new file mode 100644 index 0000000000..e2554abbdf --- /dev/null +++ b/packages/indy-sdk/src/ledger/error/index.ts @@ -0,0 +1,3 @@ +export * from './IndySdkPoolError' +export * from './IndySdkPoolNotConfiguredError' +export * from './IndySdkPoolNotFoundError' diff --git a/packages/indy-sdk/src/ledger/index.ts b/packages/indy-sdk/src/ledger/index.ts new file mode 100644 index 0000000000..fe016abcec --- /dev/null +++ b/packages/indy-sdk/src/ledger/index.ts @@ -0,0 +1,2 @@ +export * from './IndySdkPool' +export * from './IndySdkPoolService' diff --git a/packages/indy-sdk/src/ledger/util.ts b/packages/indy-sdk/src/ledger/util.ts new file mode 100644 index 0000000000..d7b5fc2076 --- /dev/null +++ b/packages/indy-sdk/src/ledger/util.ts @@ -0,0 +1,9 @@ +import type { LedgerResponse, LedgerRejectResponse, LedgerReqnackResponse } from 'indy-sdk' + +export function isLedgerRejectResponse(response: LedgerResponse): response is LedgerRejectResponse { + return response.op === 'REJECT' +} + +export function isLedgerReqnackResponse(response: LedgerResponse): response is LedgerReqnackResponse { + return response.op === 'REQNACK' +} diff --git a/packages/indy-sdk/src/storage/IndySdkStorageService.ts b/packages/indy-sdk/src/storage/IndySdkStorageService.ts new file mode 100644 index 0000000000..48f3022154 --- /dev/null +++ b/packages/indy-sdk/src/storage/IndySdkStorageService.ts @@ -0,0 +1,324 @@ +import type { IndySdkWallet } from '../wallet/IndySdkWallet' +import type { + BaseRecordConstructor, + AgentContext, + BaseRecord, + TagsBase, + Query, + StorageService, +} from '@aries-framework/core' +import type { WalletQuery, WalletRecord, WalletSearchOptions } from 'indy-sdk' + +import { RecordDuplicateError, RecordNotFoundError, injectable, inject, JsonTransformer } from '@aries-framework/core' + +import { isIndyError, IndySdkError } from '../error' +import { IndySdk, IndySdkSymbol } from '../types' +import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' + +@injectable() +export class IndySdkStorageService implements StorageService { + private indySdk: IndySdk + + private static DEFAULT_QUERY_OPTIONS = { + retrieveType: true, + retrieveTags: true, + } + + public constructor(@inject(IndySdkSymbol) indySdk: IndySdk) { + this.indySdk = indySdk + } + + private transformToRecordTagValues(tags: { [key: number]: string | undefined }): TagsBase { + const transformedTags: TagsBase = {} + + for (const [key, value] of Object.entries(tags)) { + // If the value is a boolean string ('1' or '0') + // use the boolean val + if (value === '1' && key?.includes(':')) { + const [tagName, tagValue] = key.split(':') + + const transformedValue = transformedTags[tagName] + + if (Array.isArray(transformedValue)) { + transformedTags[tagName] = [...transformedValue, tagValue] + } else { + transformedTags[tagName] = [tagValue] + } + } + // Transform '1' and '0' to boolean + else if (value === '1' || value === '0') { + transformedTags[key] = value === '1' + } + // If 1 or 0 is prefixed with 'n__' we need to remove it. This is to prevent + // casting the value to a boolean + else if (value === 'n__1' || value === 'n__0') { + transformedTags[key] = value === 'n__1' ? '1' : '0' + } + // Otherwise just use the value + else { + transformedTags[key] = value + } + } + + return transformedTags + } + + private transformFromRecordTagValues(tags: TagsBase): { [key: string]: string | undefined } { + const transformedTags: { [key: string]: string | undefined } = {} + + for (const [key, value] of Object.entries(tags)) { + // If the value is of type null we use the value undefined + // Indy doesn't support null as a value + if (value === null) { + transformedTags[key] = undefined + } + // If the value is a boolean use the indy + // '1' or '0' syntax + else if (typeof value === 'boolean') { + transformedTags[key] = value ? '1' : '0' + } + // If the value is 1 or 0, we need to add something to the value, otherwise + // the next time we deserialize the tag values it will be converted to boolean + else if (value === '1' || value === '0') { + transformedTags[key] = `n__${value}` + } + // If the value is an array we create a tag for each array + // item ("tagName:arrayItem" = "1") + else if (Array.isArray(value)) { + value.forEach((item) => { + const tagName = `${key}:${item}` + transformedTags[tagName] = '1' + }) + } + // Otherwise just use the value + else { + transformedTags[key] = value + } + } + + return transformedTags + } + + /** + * Transforms the search query into a wallet query compatible with indy WQL. + * + * The format used by AFJ is almost the same as the indy query, with the exception of + * the encoding of values, however this is handled by the {@link IndyStorageService.transformToRecordTagValues} + * method. + */ + private indyQueryFromSearchQuery(query: Query): Record { + // eslint-disable-next-line prefer-const + let { $and, $or, $not, ...tags } = query + + $and = ($and as Query[] | undefined)?.map((q) => this.indyQueryFromSearchQuery(q)) + $or = ($or as Query[] | undefined)?.map((q) => this.indyQueryFromSearchQuery(q)) + $not = $not ? this.indyQueryFromSearchQuery($not as Query) : undefined + + const indyQuery = { + ...this.transformFromRecordTagValues(tags as unknown as TagsBase), + $and, + $or, + $not, + } + + return indyQuery + } + + private recordToInstance(record: WalletRecord, recordClass: BaseRecordConstructor): T { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const instance = JsonTransformer.deserialize(record.value!, recordClass) + instance.id = record.id + + const tags = record.tags ? this.transformToRecordTagValues(record.tags) : {} + instance.replaceTags(tags) + + return instance + } + + /** @inheritDoc */ + public async save(agentContext: AgentContext, record: T) { + assertIndySdkWallet(agentContext.wallet) + + const value = JsonTransformer.serialize(record) + const tags = this.transformFromRecordTagValues(record.getTags()) as Record + + try { + await this.indySdk.addWalletRecord(agentContext.wallet.handle, record.type, record.id, value, tags) + } catch (error) { + // Record already exists + if (isIndyError(error, 'WalletItemAlreadyExists')) { + throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) + } + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + /** @inheritDoc */ + public async update(agentContext: AgentContext, record: T): Promise { + assertIndySdkWallet(agentContext.wallet) + + const value = JsonTransformer.serialize(record) + const tags = this.transformFromRecordTagValues(record.getTags()) as Record + + try { + await this.indySdk.updateWalletRecordValue(agentContext.wallet.handle, record.type, record.id, value) + await this.indySdk.updateWalletRecordTags(agentContext.wallet.handle, record.type, record.id, tags) + } catch (error) { + // Record does not exist + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`record with id ${record.id} not found.`, { + recordType: record.type, + cause: error, + }) + } + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + /** @inheritDoc */ + public async delete(agentContext: AgentContext, record: T) { + assertIndySdkWallet(agentContext.wallet) + + try { + await this.indySdk.deleteWalletRecord(agentContext.wallet.handle, record.type, record.id) + } catch (error) { + // Record does not exist + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`record with id ${record.id} not found.`, { + recordType: record.type, + cause: error, + }) + } + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + /** @inheritDoc */ + public async deleteById( + agentContext: AgentContext, + recordClass: BaseRecordConstructor, + id: string + ): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + await this.indySdk.deleteWalletRecord(agentContext.wallet.handle, recordClass.type, id) + } catch (error) { + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + cause: error, + }) + } + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + /** @inheritDoc */ + public async getById(agentContext: AgentContext, recordClass: BaseRecordConstructor, id: string): Promise { + assertIndySdkWallet(agentContext.wallet) + + try { + const record = await this.indySdk.getWalletRecord( + agentContext.wallet.handle, + recordClass.type, + id, + IndySdkStorageService.DEFAULT_QUERY_OPTIONS + ) + return this.recordToInstance(record, recordClass) + } catch (error) { + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + cause: error, + }) + } + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + /** @inheritDoc */ + public async getAll(agentContext: AgentContext, recordClass: BaseRecordConstructor): Promise { + assertIndySdkWallet(agentContext.wallet) + + const recordIterator = this.search( + agentContext.wallet, + recordClass.type, + {}, + IndySdkStorageService.DEFAULT_QUERY_OPTIONS + ) + const records = [] + for await (const record of recordIterator) { + records.push(this.recordToInstance(record, recordClass)) + } + return records + } + + /** @inheritDoc */ + public async findByQuery( + agentContext: AgentContext, + recordClass: BaseRecordConstructor, + query: Query + ): Promise { + assertIndySdkWallet(agentContext.wallet) + + const indyQuery = this.indyQueryFromSearchQuery(query) + + const recordIterator = this.search( + agentContext.wallet, + recordClass.type, + indyQuery, + IndySdkStorageService.DEFAULT_QUERY_OPTIONS + ) + const records = [] + for await (const record of recordIterator) { + records.push(this.recordToInstance(record, recordClass)) + } + return records + } + + private async *search( + wallet: IndySdkWallet, + type: string, + query: WalletQuery, + { limit = Infinity, ...options }: WalletSearchOptions & { limit?: number } + ) { + try { + const searchHandle = await this.indySdk.openWalletSearch(wallet.handle, type, query, options) + + let records: WalletRecord[] = [] + + // Allow max of 256 per fetch operation + const chunk = limit ? Math.min(256, limit) : 256 + + // Loop while limit not reached (or no limit specified) + while (!limit || records.length < limit) { + // Retrieve records + const recordsJson = await this.indySdk.fetchWalletSearchNextRecords(wallet.handle, searchHandle, chunk) + + if (recordsJson.records) { + records = [...records, ...recordsJson.records] + + for (const record of recordsJson.records) { + yield record + } + } + + // If the number of records returned is less than chunk + // It means we reached the end of the iterator (no more records) + if (!records.length || !recordsJson.records || recordsJson.records.length < chunk) { + await this.indySdk.closeWalletSearch(searchHandle) + + return + } + } + } catch (error) { + throw new IndySdkError(error, `Searching '${type}' records for query '${JSON.stringify(query)}' failed`) + } + } +} diff --git a/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts b/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts new file mode 100644 index 0000000000..7a8855c9d5 --- /dev/null +++ b/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts @@ -0,0 +1,297 @@ +import type { IndySdk } from '../../types' +import type { AgentContext, TagsBase } from '@aries-framework/core' + +import { SigningProviderRegistry, RecordDuplicateError, RecordNotFoundError } from '@aries-framework/core' + +import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { IndySdkWallet } from '../../wallet/IndySdkWallet' +import { IndySdkStorageService } from '../IndySdkStorageService' + +describe('IndySdkStorageService', () => { + let wallet: IndySdkWallet + let indy: IndySdk + let storageService: IndySdkStorageService + let agentContext: AgentContext + + beforeEach(async () => { + indy = agentDependencies.indy + const agentConfig = getAgentConfig('IndySdkStorageServiceTest') + wallet = new IndySdkWallet(indy, agentConfig.logger, new SigningProviderRegistry([])) + agentContext = getAgentContext({ + wallet, + agentConfig, + }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(agentConfig.walletConfig!) + storageService = new IndySdkStorageService(indy) + }) + + afterEach(async () => { + await wallet.delete() + }) + + const insertRecord = async ({ id, tags }: { id?: string; tags?: TagsBase }) => { + const props = { + id, + foo: 'bar', + tags: tags ?? { myTag: 'foobar' }, + } + const record = new TestRecord(props) + await storageService.save(agentContext, record) + return record + } + + describe('tag transformation', () => { + it('should correctly transform tag values to string before storing', async () => { + const record = await insertRecord({ + id: 'test-id', + tags: { + someBoolean: true, + someOtherBoolean: false, + someStringValue: 'string', + anArrayValue: ['foo', 'bar'], + // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' + someStringNumberValue: '1', + anotherStringNumberValue: '0', + }, + }) + + const retrieveRecord = await indy.getWalletRecord(wallet.handle, record.type, record.id, { + retrieveType: true, + retrieveTags: true, + }) + + expect(retrieveRecord.tags).toEqual({ + someBoolean: '1', + someOtherBoolean: '0', + someStringValue: 'string', + 'anArrayValue:foo': '1', + 'anArrayValue:bar': '1', + someStringNumberValue: 'n__1', + anotherStringNumberValue: 'n__0', + }) + }) + + it('should correctly transform tag values from string after retrieving', async () => { + await indy.addWalletRecord(wallet.handle, TestRecord.type, 'some-id', '{}', { + someBoolean: '1', + someOtherBoolean: '0', + someStringValue: 'string', + 'anArrayValue:foo': '1', + 'anArrayValue:bar': '1', + // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' + someStringNumberValue: 'n__1', + anotherStringNumberValue: 'n__0', + }) + + const record = await storageService.getById(agentContext, TestRecord, 'some-id') + + expect(record.getTags()).toEqual({ + someBoolean: true, + someOtherBoolean: false, + someStringValue: 'string', + anArrayValue: expect.arrayContaining(['bar', 'foo']), + someStringNumberValue: '1', + anotherStringNumberValue: '0', + }) + }) + }) + + describe('save()', () => { + it('should throw RecordDuplicateError if a record with the id already exists', async () => { + const record = await insertRecord({ id: 'test-id' }) + + return expect(() => storageService.save(agentContext, record)).rejects.toThrowError(RecordDuplicateError) + }) + + it('should save the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + const found = await storageService.getById(agentContext, TestRecord, 'test-id') + + expect(record).toEqual(found) + }) + }) + + describe('getById()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + return expect(() => storageService.getById(agentContext, TestRecord, 'does-not-exist')).rejects.toThrowError( + RecordNotFoundError + ) + }) + + it('should return the record by id', async () => { + const record = await insertRecord({ id: 'test-id' }) + const found = await storageService.getById(agentContext, TestRecord, 'test-id') + + expect(found).toEqual(record) + }) + }) + + describe('update()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + const record = new TestRecord({ + id: 'test-id', + foo: 'test', + tags: { some: 'tag' }, + }) + + return expect(() => storageService.update(agentContext, record)).rejects.toThrowError(RecordNotFoundError) + }) + + it('should update the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + + record.replaceTags({ ...record.getTags(), foo: 'bar' }) + record.foo = 'foobaz' + await storageService.update(agentContext, record) + + const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) + expect(retrievedRecord).toEqual(record) + }) + }) + + describe('delete()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + const record = new TestRecord({ + id: 'test-id', + foo: 'test', + tags: { some: 'tag' }, + }) + + return expect(() => storageService.delete(agentContext, record)).rejects.toThrowError(RecordNotFoundError) + }) + + it('should delete the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + await storageService.delete(agentContext, record) + + return expect(() => storageService.getById(agentContext, TestRecord, record.id)).rejects.toThrowError( + RecordNotFoundError + ) + }) + }) + + describe('getAll()', () => { + it('should retrieve all records', async () => { + const createdRecords = await Promise.all( + Array(5) + .fill(undefined) + .map((_, index) => insertRecord({ id: `record-${index}` })) + ) + + const records = await storageService.getAll(agentContext, TestRecord) + + expect(records).toEqual(expect.arrayContaining(createdRecords)) + }) + }) + + describe('findByQuery()', () => { + it('should retrieve all records that match the query', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foobar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { myTag: 'foobar' }) + + expect(records.length).toBe(1) + expect(records[0]).toEqual(expectedRecord) + }) + + it('finds records using $and statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo', anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { + $and: [{ myTag: 'foo' }, { anotherTag: 'bar' }], + }) + + expect(records.length).toBe(1) + expect(records[0]).toEqual(expectedRecord) + }) + + it('finds records using $or statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) + const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { + $or: [{ myTag: 'foo' }, { anotherTag: 'bar' }], + }) + + expect(records.length).toBe(2) + expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) + }) + + it('finds records using $not statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) + const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { + $not: { myTag: 'notfoobar' }, + }) + + expect(records.length).toBe(2) + expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) + }) + + it('correctly transforms an advanced query into a valid WQL query', async () => { + const indySpy = jest.fn() + const storageServiceWithoutIndy = new IndySdkStorageService({ + openWalletSearch: indySpy, + fetchWalletSearchNextRecords: jest.fn(() => ({ records: undefined })), + closeWalletSearch: jest.fn(), + } as unknown as IndySdk) + + await storageServiceWithoutIndy.findByQuery(agentContext, TestRecord, { + $and: [ + { + $or: [{ myTag: true }, { myTag: false }], + }, + { + $and: [{ theNumber: '0' }, { theNumber: '1' }], + }, + ], + $or: [ + { + aValue: ['foo', 'bar'], + }, + ], + $not: { myTag: 'notfoobar' }, + }) + + const expectedQuery = { + $and: [ + { + $and: undefined, + $not: undefined, + $or: [ + { myTag: '1', $and: undefined, $or: undefined, $not: undefined }, + { myTag: '0', $and: undefined, $or: undefined, $not: undefined }, + ], + }, + { + $or: undefined, + $not: undefined, + $and: [ + { theNumber: 'n__0', $and: undefined, $or: undefined, $not: undefined }, + { theNumber: 'n__1', $and: undefined, $or: undefined, $not: undefined }, + ], + }, + ], + $or: [ + { + 'aValue:foo': '1', + 'aValue:bar': '1', + $and: undefined, + $or: undefined, + $not: undefined, + }, + ], + $not: { myTag: 'notfoobar', $and: undefined, $or: undefined, $not: undefined }, + } + + expect(indySpy).toBeCalledWith(expect.anything(), expect.anything(), expectedQuery, expect.anything()) + }) + }) +}) diff --git a/packages/indy-sdk/src/storage/index.ts b/packages/indy-sdk/src/storage/index.ts new file mode 100644 index 0000000000..ff59756cfa --- /dev/null +++ b/packages/indy-sdk/src/storage/index.ts @@ -0,0 +1 @@ +export * from './IndySdkStorageService' diff --git a/packages/indy-sdk/src/types.ts b/packages/indy-sdk/src/types.ts new file mode 100644 index 0000000000..f6ac41c161 --- /dev/null +++ b/packages/indy-sdk/src/types.ts @@ -0,0 +1,6 @@ +import type { default as _IndySdk } from 'indy-sdk' + +type IndySdk = typeof _IndySdk + +export const IndySdkSymbol = Symbol('IndySdk') +export type { IndySdk } diff --git a/packages/indy-sdk/src/utils/__tests__/did.test.ts b/packages/indy-sdk/src/utils/__tests__/did.test.ts new file mode 100644 index 0000000000..45344136d9 --- /dev/null +++ b/packages/indy-sdk/src/utils/__tests__/did.test.ts @@ -0,0 +1,73 @@ +import { isAbbreviatedVerkey, isFullVerkey, isSelfCertifiedDid } from '../did' + +const validAbbreviatedVerkeys = [ + '~PKAYz8Ev4yoQgr2LaMAWFx', + '~Soy1augaQrQYtNZRRHsikB', + '~BUF7uxYTxZ6qYdZ4G9e1Gi', + '~DbZ4gkBqhFRVsT5P7BJqyZ', + '~4zmNTdG78iYyMAQdEQLrf8', +] + +const invalidAbbreviatedVerkeys = [ + '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvt', + '8jG2Bim1HNSybCTdKBRppP4PCQSSijx1pBnreqsdo8JG', + 'ABUF7uxYTxZ6qYdZ4G9e1Gi', + '~Db3IgkBqhFRVsT5P7BJqyZ', + '~4zmNTlG78iYyMAQdEQLrf8', +] + +const validFullVerkeys = [ + '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvt', + '8jG2Bim1HNSybCTdKBRppP4PCQSSijx1pBnreqsdo8JG', + '9wMLhw9SSxtTUyosrndMbvWY4TtDbVvRnMtzG2NysniP', + '6m2XT39vivJ7tLSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', + 'CAgL85iEecPNQMmxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', + 'MqXmB7cTsTXqyxDPBbrgu5EPqw61kouK1qjMvnoPa96', +] + +const invalidFullVerkeys = [ + '~PKAYz8Ev4yoQgr2LaMAWFx', + '~Soy1augaQrQYtNZRRHsikB', + '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvta', + '6m2XT39vIvJ7tLSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', + 'CAgL85iEecPNQMlxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', +] + +describe('Utils | Did', () => { + describe('isSelfCertifiedDid()', () => { + test('returns true if the verkey is abbreviated', () => { + expect(isSelfCertifiedDid('PW8ZHpNupeWXbmpPWog6Ki', '~QQ5jiH1dgXPAnvHdJvazn9')).toBe(true) + }) + + test('returns true if the verkey is not abbreviated and the did is generated from the verkey', () => { + expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'HyEoPRNvC7q4jj5joUo8AWYtxbNccbEnTAeuMYkpmNS2')).toBe(true) + }) + + test('returns false if the verkey is not abbreviated and the did is not generated from the verkey', () => { + expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'AcU7DnRqoXGYATD6VqsRq4eHuz55gdM3uzFBEhFd6rGh')).toBe(false) + }) + }) + + describe('isAbbreviatedVerkey()', () => { + test.each(validAbbreviatedVerkeys)('returns true when valid abbreviated verkey "%s" is passed in', (verkey) => { + expect(isAbbreviatedVerkey(verkey)).toBe(true) + }) + + test.each(invalidAbbreviatedVerkeys)( + 'returns false when invalid abbreviated verkey "%s" is passed in', + (verkey) => { + expect(isAbbreviatedVerkey(verkey)).toBe(false) + } + ) + }) + + describe('isFullVerkey()', () => { + test.each(validFullVerkeys)('returns true when valid full verkey "%s" is passed in', (verkey) => { + expect(isFullVerkey(verkey)).toBe(true) + }) + + test.each(invalidFullVerkeys)('returns false when invalid full verkey "%s" is passed in', (verkey) => { + expect(isFullVerkey(verkey)).toBe(false) + }) + }) +}) diff --git a/packages/indy-sdk/src/utils/assertIndySdkWallet.ts b/packages/indy-sdk/src/utils/assertIndySdkWallet.ts new file mode 100644 index 0000000000..0b1914555f --- /dev/null +++ b/packages/indy-sdk/src/utils/assertIndySdkWallet.ts @@ -0,0 +1,13 @@ +import type { Wallet } from '@aries-framework/core' + +import { AriesFrameworkError } from '@aries-framework/core' + +import { IndySdkWallet } from '../wallet/IndySdkWallet' + +export function assertIndySdkWallet(wallet: Wallet): asserts wallet is IndySdkWallet { + if (!(wallet instanceof IndySdkWallet)) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const walletClassName = (wallet as any).constructor?.name ?? 'unknown' + throw new AriesFrameworkError(`Expected wallet to be instance of IndySdkWallet, found ${walletClassName}`) + } +} diff --git a/packages/indy-sdk/src/utils/did.ts b/packages/indy-sdk/src/utils/did.ts new file mode 100644 index 0000000000..90b465a0f6 --- /dev/null +++ b/packages/indy-sdk/src/utils/did.ts @@ -0,0 +1,89 @@ +/** + * Based on DidUtils implementation in Aries Framework .NET + * @see: https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Utils/DidUtils.cs + * + * Some context about full verkeys versus abbreviated verkeys: + * A standard verkey is 32 bytes, and by default in Indy the DID is chosen as the first 16 bytes of that key, before base58 encoding. + * An abbreviated verkey replaces the first 16 bytes of the verkey with ~ when it matches the DID. + * + * When a full verkey is used to register on the ledger, this is stored as a full verkey on the ledger and also returned from the ledger as a full verkey. + * The same applies to an abbreviated verkey. If an abbreviated verkey is used to register on the ledger, this is stored as an abbreviated verkey on the ledger and also returned from the ledger as an abbreviated verkey. + * + * For this reason we need some methods to check whether verkeys are full or abbreviated, so we can align this with `indy.abbreviateVerkey` + * + * Aries Framework .NET also abbreviates verkey before sending to ledger: + * https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Ledger/DefaultLedgerService.cs#L139-L147 + */ + +import { Buffer, TypedArrayEncoder } from '@aries-framework/core' + +export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ +export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ + +/** + * Check whether the did is a self certifying did. If the verkey is abbreviated this method + * will always return true. Make sure that the verkey you pass in this method belongs to the + * did passed in + * + * @return Boolean indicating whether the did is self certifying + */ +export function isSelfCertifiedDid(did: string, verkey: string): boolean { + // If the verkey is Abbreviated, it means the full verkey + // is the did + the verkey + if (isAbbreviatedVerkey(verkey)) { + return true + } + + const didFromVerkey = indyDidFromPublicKeyBase58(verkey) + + if (didFromVerkey === did) { + return true + } + + return false +} + +export function indyDidFromPublicKeyBase58(publicKeyBase58: string): string { + const buffer = TypedArrayEncoder.fromBase58(publicKeyBase58) + + const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + + return did +} + +export function getFullVerkey(did: string, verkey: string) { + if (isFullVerkey(verkey)) return verkey + + // Did could have did:xxx prefix, only take the last item after : + const id = did.split(':').pop() ?? did + // Verkey is prefixed with ~ if abbreviated + const verkeyWithoutTilde = verkey.slice(1) + + // Create base58 encoded public key (32 bytes) + return TypedArrayEncoder.toBase58( + Buffer.concat([ + // Take did identifier (16 bytes) + TypedArrayEncoder.fromBase58(id), + // Concat the abbreviated verkey (16 bytes) + TypedArrayEncoder.fromBase58(verkeyWithoutTilde), + ]) + ) +} + +/** + * Check a base58 encoded string against a regex expression to determine if it is a full valid verkey + * @param verkey Base58 encoded string representation of a verkey + * @return Boolean indicating if the string is a valid verkey + */ +export function isFullVerkey(verkey: string): boolean { + return FULL_VERKEY_REGEX.test(verkey) +} + +/** + * Check a base58 encoded string against a regex expression to determine if it is a valid abbreviated verkey + * @param verkey Base58 encoded string representation of an abbreviated verkey + * @returns Boolean indicating if the string is a valid abbreviated verkey + */ +export function isAbbreviatedVerkey(verkey: string): boolean { + return ABBREVIATED_VERKEY_REGEX.test(verkey) +} diff --git a/packages/indy-sdk/src/utils/promises.ts b/packages/indy-sdk/src/utils/promises.ts new file mode 100644 index 0000000000..0e843d73b5 --- /dev/null +++ b/packages/indy-sdk/src/utils/promises.ts @@ -0,0 +1,44 @@ +// This file polyfills the allSettled method introduced in ESNext + +export type AllSettledFulfilled = { + status: 'fulfilled' + value: T +} + +export type AllSettledRejected = { + status: 'rejected' + // eslint-disable-next-line @typescript-eslint/no-explicit-any + reason: any +} + +export function allSettled(promises: Promise[]) { + return Promise.all( + promises.map((p) => + p + .then( + (value) => + ({ + status: 'fulfilled', + value, + } as AllSettledFulfilled) + ) + .catch( + (reason) => + ({ + status: 'rejected', + reason, + } as AllSettledRejected) + ) + ) + ) +} + +export function onlyFulfilled(entries: Array | AllSettledRejected>) { + // We filter for only the rejected values, so we can safely cast the type + return entries.filter((e) => e.status === 'fulfilled') as AllSettledFulfilled[] +} + +export function onlyRejected(entries: Array | AllSettledRejected>) { + // We filter for only the rejected values, so we can safely cast the type + return entries.filter((e) => e.status === 'rejected') as AllSettledRejected[] +} diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts new file mode 100644 index 0000000000..9230ed5f28 --- /dev/null +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -0,0 +1,686 @@ +import type { + EncryptedMessage, + KeyDerivationMethod, + WalletConfig, + Buffer, + WalletCreateKeyOptions, + DidConfig, + DidInfo, + WalletSignOptions, + UnpackedMessageContext, + WalletVerifyOptions, + Wallet, + KeyPair, + WalletExportImportConfig, + WalletConfigRekey, +} from '@aries-framework/core' +import type { WalletStorageConfig, WalletConfig as IndySdkWalletConfig, OpenWalletCredentials } from 'indy-sdk' + +const isError = (error: unknown): error is Error => error instanceof Error + +import { + AriesFrameworkError, + RecordDuplicateError, + RecordNotFoundError, + Logger, + JsonEncoder, + WalletDuplicateError, + WalletError, + WalletNotFoundError, + WalletInvalidKeyError, + InjectionSymbols, + KeyType, + Key, + SigningProviderRegistry, + TypedArrayEncoder, +} from '@aries-framework/core' +import { inject, injectable } from 'tsyringe' + +import { isIndyError, IndySdkError } from '../error' +import { IndySdk, IndySdkSymbol } from '../types' + +@injectable() +export class IndySdkWallet implements Wallet { + private walletConfig?: WalletConfig + private walletHandle?: number + + private logger: Logger + private signingKeyProviderRegistry: SigningProviderRegistry + private publicDidInfo: DidInfo | undefined + private indySdk: IndySdk + + public constructor( + @inject(IndySdkSymbol) indySdk: IndySdk, + @inject(InjectionSymbols.Logger) logger: Logger, + signingKeyProviderRegistry: SigningProviderRegistry + ) { + this.logger = logger + this.signingKeyProviderRegistry = signingKeyProviderRegistry + this.indySdk = indySdk + } + + public get isProvisioned() { + return this.walletConfig !== undefined + } + + public get isInitialized() { + return this.walletHandle !== undefined + } + + public get publicDid() { + return this.publicDidInfo + } + + public get handle() { + if (!this.walletHandle) { + throw new AriesFrameworkError( + 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' + ) + } + + return this.walletHandle + } + + public get masterSecretId() { + if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) { + throw new AriesFrameworkError( + 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' + ) + } + + return this.walletConfig?.masterSecretId ?? this.walletConfig.id + } + + /** + * Dispose method is called when an agent context is disposed. + */ + public async dispose() { + if (this.isInitialized) { + await this.close() + } + } + + private walletStorageConfig(walletConfig: WalletConfig): IndySdkWalletConfig { + const walletStorageConfig: IndySdkWalletConfig = { + id: walletConfig.id, + storage_type: walletConfig.storage?.type, + } + + if (walletConfig.storage?.config) { + walletStorageConfig.storage_config = walletConfig.storage?.config as WalletStorageConfig + } + + return walletStorageConfig + } + + private walletCredentials( + walletConfig: WalletConfig, + rekey?: string, + rekeyDerivation?: KeyDerivationMethod + ): OpenWalletCredentials { + const walletCredentials: OpenWalletCredentials = { + key: walletConfig.key, + key_derivation_method: walletConfig.keyDerivationMethod, + } + if (rekey) { + walletCredentials.rekey = rekey + } + if (rekeyDerivation) { + walletCredentials.rekey_derivation_method = rekeyDerivation + } + if (walletConfig.storage?.credentials) { + walletCredentials.storage_credentials = walletConfig.storage?.credentials as Record + } + + return walletCredentials + } + + /** + * @throws {WalletDuplicateError} if the wallet already exists + * @throws {WalletError} if another error occurs + */ + public async create(walletConfig: WalletConfig): Promise { + await this.createAndOpen(walletConfig) + await this.close() + } + + /** + * @throws {WalletDuplicateError} if the wallet already exists + * @throws {WalletError} if another error occurs + */ + public async createAndOpen(walletConfig: WalletConfig): Promise { + this.logger.debug(`Creating wallet '${walletConfig.id}' using SQLite storage`) + + try { + await this.indySdk.createWallet(this.walletStorageConfig(walletConfig), this.walletCredentials(walletConfig)) + this.walletConfig = walletConfig + + // We usually want to create master secret only once, therefore, we can to do so when creating a wallet. + await this.open(walletConfig) + + // We need to open wallet before creating master secret because we need wallet handle here. + await this.createMasterSecret(this.handle, this.masterSecretId) + } catch (error) { + // If an error ocurred while creating the master secret, we should close the wallet + if (this.isInitialized) await this.close() + + if (isIndyError(error, 'WalletAlreadyExistsError')) { + const errorMessage = `Wallet '${walletConfig.id}' already exists` + this.logger.debug(errorMessage) + + throw new WalletDuplicateError(errorMessage, { + walletType: 'IndySdkWallet', + cause: error, + }) + } else { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + const errorMessage = `Error creating wallet '${walletConfig.id}'` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + this.logger.debug(`Successfully created wallet '${walletConfig.id}'`) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async open(walletConfig: WalletConfig): Promise { + await this._open(walletConfig) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async rotateKey(walletConfig: WalletConfigRekey): Promise { + if (!walletConfig.rekey) { + throw new WalletError('Wallet rekey undefined!. Please specify the new wallet key') + } + await this._open( + { + id: walletConfig.id, + key: walletConfig.key, + keyDerivationMethod: walletConfig.keyDerivationMethod, + }, + walletConfig.rekey, + walletConfig.rekeyDerivationMethod + ) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + private async _open( + walletConfig: WalletConfig, + rekey?: string, + rekeyDerivation?: KeyDerivationMethod + ): Promise { + if (this.walletHandle) { + throw new WalletError( + 'Wallet instance already opened. Close the currently opened wallet before re-opening the wallet' + ) + } + + try { + this.walletHandle = await this.indySdk.openWallet( + this.walletStorageConfig(walletConfig), + this.walletCredentials(walletConfig, rekey, rekeyDerivation) + ) + if (rekey) { + this.walletConfig = { ...walletConfig, key: rekey, keyDerivationMethod: rekeyDerivation } + } else { + this.walletConfig = walletConfig + } + } catch (error) { + if (isIndyError(error, 'WalletNotFoundError')) { + const errorMessage = `Wallet '${walletConfig.id}' not found` + this.logger.debug(errorMessage) + + throw new WalletNotFoundError(errorMessage, { + walletType: 'IndySdkWallet', + cause: error, + }) + } else if (isIndyError(error, 'WalletAccessFailed')) { + const errorMessage = `Incorrect key for wallet '${walletConfig.id}'` + this.logger.debug(errorMessage) + throw new WalletInvalidKeyError(errorMessage, { + walletType: 'IndySdkWallet', + cause: error, + }) + } else { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + const errorMessage = `Error opening wallet '${walletConfig.id}': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + this.logger.debug(`Wallet '${walletConfig.id}' opened with handle '${this.handle}'`) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async delete(): Promise { + if (!this.walletConfig) { + throw new WalletError( + 'Can not delete wallet that does not have wallet config set. Make sure to call create wallet before deleting the wallet' + ) + } + + this.logger.info(`Deleting wallet '${this.walletConfig.id}'`) + + if (this.walletHandle) { + await this.close() + } + + try { + await this.indySdk.deleteWallet( + this.walletStorageConfig(this.walletConfig), + this.walletCredentials(this.walletConfig) + ) + } catch (error) { + if (isIndyError(error, 'WalletNotFoundError')) { + const errorMessage = `Error deleting wallet: wallet '${this.walletConfig.id}' not found` + this.logger.debug(errorMessage) + + throw new WalletNotFoundError(errorMessage, { + walletType: 'IndySdkWallet', + cause: error, + }) + } else { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + const errorMessage = `Error deleting wallet '${this.walletConfig.id}': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + } + + public async export(exportConfig: WalletExportImportConfig) { + try { + this.logger.debug(`Exporting wallet ${this.walletConfig?.id} to path ${exportConfig.path}`) + await this.indySdk.exportWallet(this.handle, exportConfig) + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + const errorMessage = `Error exporting wallet: ${error.message}` + this.logger.error(errorMessage, { + error, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig) { + try { + this.logger.debug(`Importing wallet ${walletConfig.id} from path ${importConfig.path}`) + await this.indySdk.importWallet( + { id: walletConfig.id }, + { key: walletConfig.key, key_derivation_method: walletConfig.keyDerivationMethod }, + importConfig + ) + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + const errorMessage = `Error importing wallet': ${error.message}` + this.logger.error(errorMessage, { + error, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + /** + * @throws {WalletError} if the wallet is already closed or another error occurs + */ + public async close(): Promise { + this.logger.debug(`Closing wallet ${this.walletConfig?.id}`) + if (!this.walletHandle) { + throw new WalletError('Wallet is in invalid state, you are trying to close wallet that has no `walletHandle`.') + } + + try { + await this.indySdk.closeWallet(this.walletHandle) + this.walletHandle = undefined + this.publicDidInfo = undefined + } catch (error) { + if (isIndyError(error, 'WalletInvalidHandle')) { + const errorMessage = `Error closing wallet: wallet already closed` + this.logger.debug(errorMessage) + + throw new WalletError(errorMessage, { + cause: error, + }) + } else { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + const errorMessage = `Error closing wallet': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + } + + /** + * Create master secret with specified id in currently opened wallet. + * + * If a master secret by this id already exists in the current wallet, the method + * will return without doing anything. + * + * @throws {WalletError} if an error occurs + */ + private async createMasterSecret(walletHandle: number, masterSecretId: string): Promise { + this.logger.debug(`Creating master secret with id '${masterSecretId}' in wallet with handle '${walletHandle}'`) + + try { + await this.indySdk.proverCreateMasterSecret(walletHandle, masterSecretId) + + return masterSecretId + } catch (error) { + if (isIndyError(error, 'AnoncredsMasterSecretDuplicateNameError')) { + // master secret id is the same as the master secret id passed in the create function + // so if it already exists we can just assign it. + this.logger.debug( + `Master secret with id '${masterSecretId}' already exists in wallet with handle '${walletHandle}'`, + { + indyError: 'AnoncredsMasterSecretDuplicateNameError', + } + ) + + return masterSecretId + } else { + if (!isIndyError(error)) { + throw new AriesFrameworkError('Attempted to throw Indy error, but it was not an Indy error') + } + + this.logger.error(`Error creating master secret with id ${masterSecretId}`, { + indyError: error.indyName, + error, + }) + + throw new WalletError( + `Error creating master secret with id ${masterSecretId} in wallet with handle '${walletHandle}'`, + { cause: error } + ) + } + } + } + + public async initPublicDid(didConfig: DidConfig) { + const { did, verkey } = await this.createDid(didConfig) + this.publicDidInfo = { + did, + verkey, + } + } + + public async createDid(didConfig?: DidConfig): Promise { + try { + const [did, verkey] = await this.indySdk.createAndStoreMyDid(this.handle, didConfig || {}) + + return { did, verkey } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError('Error creating Did', { cause: error }) + } + } + + /** + * Create a key with an optional seed and keyType. + * The keypair is also automatically stored in the wallet afterwards + * + * Bls12381g1g2 and X25519 are not supported. + * + * @param seed string The seed for creating a key + * @param keyType KeyType the type of key that should be created + * + * @returns a Key instance with a publicKeyBase58 + * + * @throws {WalletError} When an unsupported keytype is requested + * @throws {WalletError} When the key could not be created + */ + public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { + try { + // Ed25519 is supported natively in Indy wallet + if (keyType === KeyType.Ed25519) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + const verkey = await this.indySdk.createKey(this.handle, { seed, crypto_type: 'ed25519' }) + return Key.fromPublicKeyBase58(verkey, keyType) + } + + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) + + const keyPair = await signingKeyProvider.createKeyPair({ seed }) + await this.storeKeyPair(keyPair) + return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) + } + + throw new WalletError(`Unsupported key type: '${keyType}' for wallet IndySdkWallet`) + } + + /** + * sign a Buffer with an instance of a Key class + * + * Bls12381g1g2, Bls12381g1 and X25519 are not supported. + * + * @param data Buffer The data that needs to be signed + * @param key Key The key that is used to sign the data + * + * @returns A signature for the data + */ + public async sign({ data, key }: WalletSignOptions): Promise { + try { + // Ed25519 is supported natively in Indy wallet + if (key.keyType === KeyType.Ed25519) { + // Checks to see if it is an not an Array of messages, but just a single one + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`${KeyType.Ed25519} does not support multiple singing of multiple messages`) + } + return await this.indySdk.cryptoSign(this.handle, key.publicKeyBase58, data as Buffer) + } + + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + + const keyPair = await this.retrieveKeyPair(key.publicKeyBase58) + const signed = await signingKeyProvider.sign({ + data, + privateKeyBase58: keyPair.privateKeyBase58, + publicKeyBase58: key.publicKeyBase58, + }) + + return signed + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}`, { cause: error }) + } + throw new WalletError(`Unsupported keyType: ${key.keyType}`) + } + + /** + * Verify the signature with the data and the used key + * + * Bls12381g1g2, Bls12381g1 and X25519 are not supported. + * + * @param data Buffer The data that has to be confirmed to be signed + * @param key Key The key that was used in the signing process + * @param signature Buffer The signature that was created by the signing process + * + * @returns A boolean whether the signature was created with the supplied data and key + * + * @throws {WalletError} When it could not do the verification + * @throws {WalletError} When an unsupported keytype is used + */ + public async verify({ data, key, signature }: WalletVerifyOptions): Promise { + try { + // Ed25519 is supported natively in Indy wallet + if (key.keyType === KeyType.Ed25519) { + // Checks to see if it is an not an Array of messages, but just a single one + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`${KeyType.Ed25519} does not support multiple singing of multiple messages`) + } + return await this.indySdk.cryptoVerify(key.publicKeyBase58, data as Buffer, signature) + } + + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + + const signed = await signingKeyProvider.verify({ + data, + signature, + publicKeyBase58: key.publicKeyBase58, + }) + + return signed + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error verifying signature of data signed with verkey ${key.publicKeyBase58}`, { + cause: error, + }) + } + throw new WalletError(`Unsupported keyType: ${key.keyType}`) + } + + public async pack( + payload: Record, + recipientKeys: string[], + senderVerkey?: string + ): Promise { + try { + const messageRaw = JsonEncoder.toBuffer(payload) + const packedMessage = await this.indySdk.packMessage(this.handle, messageRaw, recipientKeys, senderVerkey ?? null) + return JsonEncoder.fromBuffer(packedMessage) + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError('Error packing message', { cause: error }) + } + } + + public async unpack(messagePackage: EncryptedMessage): Promise { + try { + const unpackedMessageBuffer = await this.indySdk.unpackMessage(this.handle, JsonEncoder.toBuffer(messagePackage)) + const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer) + return { + senderKey: unpackedMessage.sender_verkey, + recipientKey: unpackedMessage.recipient_verkey, + plaintextMessage: JsonEncoder.fromString(unpackedMessage.message), + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError('Error unpacking message', { cause: error }) + } + } + + public async generateNonce(): Promise { + try { + return await this.indySdk.generateNonce() + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError('Error generating nonce', { cause: error }) + } + } + + private async retrieveKeyPair(publicKeyBase58: string): Promise { + try { + const { value } = await this.indySdk.getWalletRecord(this.handle, 'KeyPairRecord', `key-${publicKeyBase58}`, {}) + if (value) { + return JsonEncoder.fromString(value) as KeyPair + } else { + throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) + } + } catch (error) { + if (isIndyError(error, 'WalletItemNotFound')) { + throw new RecordNotFoundError(`KeyPairRecord not found for public key: ${publicKeyBase58}.`, { + recordType: 'KeyPairRecord', + cause: error, + }) + } + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + private async storeKeyPair(keyPair: KeyPair): Promise { + try { + await this.indySdk.addWalletRecord( + this.handle, + 'KeyPairRecord', + `key-${keyPair.publicKeyBase58}`, + JSON.stringify(keyPair), + { + keyType: keyPair.keyType, + } + ) + } catch (error) { + if (isIndyError(error, 'WalletItemAlreadyExists')) { + throw new RecordDuplicateError(`Record already exists`, { recordType: 'KeyPairRecord' }) + } + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + + public async generateWalletKey() { + try { + return await this.indySdk.generateWalletKey() + } catch (error) { + throw new WalletError('Error generating wallet key', { cause: error }) + } + } +} diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts new file mode 100644 index 0000000000..4b7f822f0e --- /dev/null +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -0,0 +1,125 @@ +import type { WalletConfig } from '@aries-framework/core' + +import { + KeyType, + WalletError, + SigningProviderRegistry, + TypedArrayEncoder, + KeyDerivationMethod, +} from '@aries-framework/core' + +import testLogger from '../../../../core/tests/logger' +import { agentDependencies } from '../../../../node/src' +import { IndySdkWallet } from '../IndySdkWallet' + +// use raw key derivation method to speed up wallet creating / opening / closing between tests +const walletConfig: WalletConfig = { + id: 'Wallet: IndySdkWalletTest', + // generated using indy.generateWalletKey + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, +} + +const walletConfigWithMasterSecretId: WalletConfig = { + id: 'Wallet: WalletTestWithMasterSecretId', + // generated using indy.generateWalletKey + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, + masterSecretId: 'customMasterSecretId', +} + +describe('IndySdkWallet', () => { + let indySdkWallet: IndySdkWallet + + const seed = 'sample-seed' + const message = TypedArrayEncoder.fromString('sample-message') + + beforeEach(async () => { + indySdkWallet = new IndySdkWallet(agentDependencies.indy, testLogger, new SigningProviderRegistry([])) + await indySdkWallet.createAndOpen(walletConfig) + }) + + afterEach(async () => { + await indySdkWallet.delete() + }) + + test('Get the public DID', async () => { + await indySdkWallet.initPublicDid({ seed: '000000000000000000000000Trustee9' }) + expect(indySdkWallet.publicDid).toMatchObject({ + did: expect.any(String), + verkey: expect.any(String), + }) + }) + + test('Get the Master Secret', () => { + expect(indySdkWallet.masterSecretId).toEqual('Wallet: IndySdkWalletTest') + }) + + test('Get the wallet handle', () => { + expect(indySdkWallet.handle).toEqual(expect.any(Number)) + }) + + test('Initializes a public did', async () => { + await indySdkWallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) + + expect(indySdkWallet.publicDid).toEqual({ + did: 'DtWRdd6C5dN5vpcN6XRAvu', + verkey: '82RBSn3heLgXzZd74UsMC8Q8YRfEEhQoAM7LUqE6bevJ', + }) + }) + + test('Generate Nonce', async () => { + await expect(indySdkWallet.generateNonce()).resolves.toEqual(expect.any(String)) + }) + + test('Create ed25519 keypair', async () => { + await expect( + indySdkWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) + ).resolves.toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Fail to create x25519 keypair', async () => { + await expect(indySdkWallet.createKey({ seed, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) + }) + + test('Create a signature with a ed25519 keypair', async () => { + const ed25519Key = await indySdkWallet.createKey({ keyType: KeyType.Ed25519 }) + const signature = await indySdkWallet.sign({ + data: message, + key: ed25519Key, + }) + expect(signature.length).toStrictEqual(64) + }) + + test('Verify a signed message with a ed25519 publicKey', async () => { + const ed25519Key = await indySdkWallet.createKey({ keyType: KeyType.Ed25519 }) + const signature = await indySdkWallet.sign({ + data: message, + key: ed25519Key, + }) + await expect(indySdkWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) + }) + + test('masterSecretId is equal to wallet ID by default', async () => { + expect(indySdkWallet.masterSecretId).toEqual(walletConfig.id) + }) +}) + +describe('IndySdkWallet with custom Master Secret Id', () => { + let indySdkWallet: IndySdkWallet + + beforeEach(async () => { + indySdkWallet = new IndySdkWallet(agentDependencies.indy, testLogger, new SigningProviderRegistry([])) + await indySdkWallet.createAndOpen(walletConfigWithMasterSecretId) + }) + + afterEach(async () => { + await indySdkWallet.delete() + }) + + test('masterSecretId is set by config', async () => { + expect(indySdkWallet.masterSecretId).toEqual(walletConfigWithMasterSecretId.masterSecretId) + }) +}) diff --git a/packages/indy-sdk/src/wallet/index.ts b/packages/indy-sdk/src/wallet/index.ts new file mode 100644 index 0000000000..b327ed63bf --- /dev/null +++ b/packages/indy-sdk/src/wallet/index.ts @@ -0,0 +1 @@ +export { IndySdkWallet } from './IndySdkWallet' diff --git a/packages/indy-sdk/tsconfig.build.json b/packages/indy-sdk/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/indy-sdk/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/indy-sdk/tsconfig.json b/packages/indy-sdk/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/indy-sdk/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 8be53f94cc..01a769bea8 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -26,10 +26,8 @@ "dependencies": { "class-transformer": "0.5.1", "class-validator": "0.13.1", - "rxjs": "^7.2.0" - }, - "peerDependencies": { - "@aries-framework/core": "0.2.5" + "rxjs": "^7.2.0", + "@aries-framework/core": "0.3.2" }, "devDependencies": { "@aries-framework/node": "0.3.2", diff --git a/packages/react-native/package.json b/packages/react-native/package.json index dfef41e5d2..dab91dd4ec 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -29,7 +29,7 @@ "events": "^3.3.0" }, "devDependencies": { - "@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.21", + "@types/indy-sdk-react-native": "npm:@types/indy-sdk@1.16.24", "@types/react-native": "^0.64.10", "indy-sdk-react-native": "^0.3.0", "react": "17.0.1", diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index 7a7adfaa8e..16868df737 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -11,7 +11,7 @@ export class SubjectOutboundTransport implements OutboundTransport { private agent!: Agent private stop$!: Subject - public supportedSchemes = ['rxjs'] + public supportedSchemes = ['rxjs', 'wss'] public constructor(subjectMap: { [key: string]: Subject | undefined }) { this.subjectMap = subjectMap diff --git a/yarn.lock b/yarn.lock index d28283327d..eb7826abc9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,38 +29,38 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.1.tgz#72d647b4ff6a4f82878d184613353af1dd0290f9" - integrity sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.0", "@babel/compat-data@^7.20.1": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.1.tgz#f2e6ef7790d8c8dbf03d379502dcc246dcce0b30" + integrity sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.1.tgz#c8fa615c5e88e272564ace3d42fbc8b17bfeb22b" - integrity sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw== + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.2.tgz#8dc9b1620a673f92d3624bd926dc49a52cf25b92" + integrity sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.19.0" - "@babel/helper-compilation-targets" "^7.19.1" - "@babel/helper-module-transforms" "^7.19.0" - "@babel/helpers" "^7.19.0" - "@babel/parser" "^7.19.1" + "@babel/generator" "^7.20.2" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-module-transforms" "^7.20.2" + "@babel/helpers" "^7.20.1" + "@babel/parser" "^7.20.2" "@babel/template" "^7.18.10" - "@babel/traverse" "^7.19.1" - "@babel/types" "^7.19.0" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.2" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.19.0", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.0.tgz#785596c06425e59334df2ccee63ab166b738419a" - integrity sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg== +"@babel/generator@^7.20.1", "@babel/generator@^7.20.2", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.20.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.4.tgz#4d9f8f0c30be75fd90a0562099a26e5839602ab8" + integrity sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA== dependencies: - "@babel/types" "^7.19.0" + "@babel/types" "^7.20.2" "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" @@ -79,27 +79,27 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0", "@babel/helper-compilation-targets@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz#7f630911d83b408b76fe584831c98e5395d7a17c" - integrity sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" + integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== dependencies: - "@babel/compat-data" "^7.19.1" + "@babel/compat-data" "^7.20.0" "@babel/helper-validator-option" "^7.18.6" browserslist "^4.21.3" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz#bfd6904620df4e46470bae4850d66be1054c404b" - integrity sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.2.tgz#3c08a5b5417c7f07b5cf3dfb6dc79cbec682e8c2" + integrity sha512-k22GoYRAHPYr9I+Gvy2ZQlAe5mGy8BqWst2wRt8cwIufWTxrsVshhIBvYNqC80N0GSFWTsqRVexOtfzlgOEDvA== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" "@babel/helper-member-expression-to-functions" "^7.18.9" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-replace-supers" "^7.19.1" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-create-regexp-features-plugin@^7.18.6": @@ -163,19 +163,19 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30" - integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ== +"@babel/helper-module-transforms@^7.19.6", "@babel/helper-module-transforms@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" + integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" "@babel/template" "^7.18.10" - "@babel/traverse" "^7.19.0" - "@babel/types" "^7.19.0" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.2" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -184,12 +184,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.8.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf" - integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78" integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw== @@ -200,19 +200,19 @@ "@babel/traverse" "^7.19.1" "@babel/types" "^7.19.0" -"@babel/helper-simple-access@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" - integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== +"@babel/helper-simple-access@^7.19.4", "@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.20.2" "@babel/helper-skip-transparent-expression-wrappers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" - integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" + integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== dependencies: - "@babel/types" "^7.18.9" + "@babel/types" "^7.20.0" "@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" @@ -221,12 +221,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-string-parser@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" - integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== -"@babel/helper-validator-identifier@^7.18.6": +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== @@ -236,14 +236,14 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== -"@babel/helpers@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.0.tgz#f30534657faf246ae96551d88dd31e9d1fa1fc18" - integrity sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg== +"@babel/helpers@^7.20.1": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.1.tgz#2ab7a0fcb0a03b5bf76629196ed63c2d7311f4c9" + integrity sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg== dependencies: "@babel/template" "^7.18.10" - "@babel/traverse" "^7.19.0" - "@babel/types" "^7.19.0" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" @@ -254,10 +254,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.1.tgz#6f6d6c2e621aad19a92544cc217ed13f1aac5b4c" - integrity sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.20.1", "@babel/parser@^7.20.2": + version "7.20.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" + integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": version "7.18.6" @@ -284,15 +284,15 @@ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz#f9434f6beb2c8cae9dfcf97d2a5941bbbf9ad4e7" - integrity sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q== + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz#a556f59d555f06961df1e572bb5eca864c84022d" + integrity sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ== dependencies: - "@babel/compat-data" "^7.18.8" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/compat-data" "^7.20.1" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.18.8" + "@babel/plugin-transform-parameters" "^7.20.1" "@babel/plugin-proposal-optional-catch-binding@^7.0.0": version "7.18.6" @@ -423,12 +423,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.18.6", "@babel/plugin-syntax-typescript@^7.7.2": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285" - integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA== +"@babel/plugin-syntax-typescript@^7.20.0", "@babel/plugin-syntax-typescript@^7.7.2": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7" + integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-arrow-functions@^7.0.0": version "7.18.6" @@ -445,24 +445,24 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz#f9b7e018ac3f373c81452d6ada8bd5a18928926d" - integrity sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw== + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.2.tgz#f59b1767e6385c663fd0bce655db6ca9c8b236ed" + integrity sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-classes@^7.0.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz#0e61ec257fba409c41372175e7c1e606dc79bb20" - integrity sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A== + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz#c0033cf1916ccf78202d04be4281d161f6709bb2" + integrity sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.19.0" + "@babel/helper-compilation-targets" "^7.20.0" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.19.1" "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" @@ -474,11 +474,11 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.18.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.13.tgz#9e03bc4a94475d62b7f4114938e6c5c33372cbf5" - integrity sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow== + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz#c23741cfa44ddd35f5e53896e88c75331b8b2792" + integrity sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-exponentiation-operator@^7.0.0": version "7.18.6" @@ -527,14 +527,13 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz#afd243afba166cca69892e24a8fd8c9f2ca87883" - integrity sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q== + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz#25b32feef24df8038fc1ec56038917eacb0b730c" + integrity sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ== dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-simple-access" "^7.18.6" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-simple-access" "^7.19.4" "@babel/plugin-transform-object-assign@^7.0.0": version "7.18.6" @@ -551,12 +550,12 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a" - integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg== +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.1": + version "7.20.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.3.tgz#7b3468d70c3c5b62e46be0a47b6045d8590fb748" + integrity sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-property-literals@^7.0.0": version "7.18.6" @@ -580,11 +579,11 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-jsx-source@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.18.6.tgz#06e9ae8a14d2bc19ce6e3c447d842032a50598fc" - integrity sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw== + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz#88578ae8331e5887e8ce28e4c9dc83fb29da0b86" + integrity sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-react-jsx@^7.0.0": version "7.19.0" @@ -606,9 +605,9 @@ regenerator-transform "^0.15.0" "@babel/plugin-transform-runtime@^7.0.0": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.1.tgz#a3df2d7312eea624c7889a2dcd37fd1dfd25b2c6" - integrity sha512-2nJjTUFIzBMP/f/miLxEK9vxwW/KUXsdvN4sR//TmuDhe6yU2h57WmIOE12Gng3MDP/xpjUV/ToZRdcf8Yj4fA== + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz#9d2a9dbf4e12644d6f46e5e75bfbf02b5d6e9194" + integrity sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw== dependencies: "@babel/helper-module-imports" "^7.18.6" "@babel/helper-plugin-utils" "^7.19.0" @@ -647,13 +646,13 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typescript@^7.18.6", "@babel/plugin-transform-typescript@^7.5.0": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.1.tgz#adcf180a041dcbd29257ad31b0c65d4de531ce8d" - integrity sha512-+ILcOU+6mWLlvCwnL920m2Ow3wWx3Wo8n2t5aROQmV55GZt+hOiLvBaa3DNzRjSEHa1aauRs4/YLmkCfFkhhRQ== + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.2.tgz#91515527b376fc122ba83b13d70b01af8fe98f3f" + integrity sha512-jvS+ngBfrnTUBfOQq8NfGnSbF9BrqlR6hjJ2yVxMkmO5nL/cdifNbI30EfjRlN4g5wYWNnMPyj5Sa6R1pbLeag== dependencies: - "@babel/helper-create-class-features-plugin" "^7.19.0" - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/plugin-syntax-typescript" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.20.2" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-typescript" "^7.20.0" "@babel/plugin-transform-unicode-regex@^7.0.0": version "7.18.6" @@ -693,11 +692,11 @@ source-map-support "^0.5.16" "@babel/runtime@^7.8.4": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" - integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" + integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg== dependencies: - regenerator-runtime "^0.13.4" + regenerator-runtime "^0.13.10" "@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.3.3": version "7.18.10" @@ -708,29 +707,29 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.7.2": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.1.tgz#0fafe100a8c2a603b4718b1d9bf2568d1d193347" - integrity sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1", "@babel/traverse@^7.7.2": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.1.tgz#9b15ccbf882f6d107eeeecf263fbcdd208777ec8" + integrity sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.19.0" + "@babel/generator" "^7.20.1" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.19.1" - "@babel/types" "^7.19.0" + "@babel/parser" "^7.20.1" + "@babel/types" "^7.20.0" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.0.tgz#75f21d73d73dc0351f3368d28db73465f4814600" - integrity sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA== +"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842" + integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog== dependencies: - "@babel/helper-string-parser" "^7.18.10" - "@babel/helper-validator-identifier" "^7.18.6" + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -1075,7 +1074,7 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@^3.0.3": +"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== @@ -1085,7 +1084,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== @@ -1099,12 +1098,12 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping@^0.3.9": - version "0.3.15" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" - integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" "@lerna/add@4.0.0": version "4.0.0" @@ -2032,10 +2031,10 @@ dependencies: "@octokit/openapi-types" "^12.11.0" -"@peculiar/asn1-schema@^2.1.6": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.0.tgz#5368416eb336138770c692ffc2bab119ee3ae917" - integrity sha512-DtNLAG4vmDrdSJFPe7rypkcj597chNQL7u+2dBtYo5mh7VW2+im6ke+O0NVr8W1f4re4C3F71LhoMb0Yxqa48Q== +"@peculiar/asn1-schema@^2.1.6", "@peculiar/asn1-schema@^2.3.0": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.3.tgz#21418e1f3819e0b353ceff0c2dad8ccb61acd777" + integrity sha512-6GptMYDMyWBHTUKndHaDsRZUO/XMSgIns2krxcm2L7SEExRHwawFvSwNBhqNPR9HJwv3MruAiF1bhN0we6j6GQ== dependencies: asn1js "^3.0.5" pvtsutils "^1.3.2" @@ -2049,14 +2048,14 @@ tslib "^2.0.0" "@peculiar/webcrypto@^1.0.22": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.0.tgz#f941bd95285a0f8a3d2af39ccda5197b80cd32bf" - integrity sha512-U58N44b2m3OuTgpmKgf0LPDOmP3bhwNz01vAnj1mBwxBASRhptWYK+M3zG+HBkDqGQM+bFsoIihTW8MdmPXEqg== + version "1.4.1" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.1.tgz#821493bd5ad0f05939bd5f53b28536f68158360a" + integrity sha512-eK4C6WTNYxoI7JOabMoZICiyqRRtJB220bh0Mbj5RwRycleZf9BPyZoxsTvpP0FpmVS2aS13NKOuh5/tN3sIRw== dependencies: - "@peculiar/asn1-schema" "^2.1.6" + "@peculiar/asn1-schema" "^2.3.0" "@peculiar/json-schema" "^1.1.12" pvtsutils "^1.3.2" - tslib "^2.4.0" + tslib "^2.4.1" webcrypto-core "^1.7.4" "@react-native-community/cli-debugger-ui@^5.0.1": @@ -2216,9 +2215,9 @@ integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== "@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== + version "1.8.5" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.5.tgz#e280c94c95f206dcfd5aca00a43f2156b758c764" + integrity sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA== dependencies: type-detect "4.0.8" @@ -2325,9 +2324,9 @@ integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.1.19" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" - integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw== + version "7.1.20" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.20.tgz#e168cdd612c92a2d335029ed62ac94c95b362359" + integrity sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -2442,17 +2441,17 @@ dependencies: "@types/node" "*" -"@types/indy-sdk-react-native@npm:@types/indy-sdk@^1.16.21", "@types/indy-sdk@^1.16.21": - version "1.16.21" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.21.tgz#bb6178e2a515115b1bf225fb78506a3017d08aa8" - integrity sha512-SIu1iOa77lkxkGlW09OinFwebe7U5oDYwI70NnPoe9nbDr63i0FozITWEyIdC1BloKvZRXne6nM4i9zy6E3n6g== +"@types/indy-sdk-react-native@npm:@types/indy-sdk@1.16.24", "@types/indy-sdk@1.16.24": + version "1.16.24" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.24.tgz#1b8e33e8fd2a095a29cb06b76146ed14d1477cdd" + integrity sha512-5YliU8lqahihz46MPpiu1ZWNkG2c/lm9SI+Fp3DUV2HrGbuAPxI8dYg2CP6avuD5kfCYr6Y5+TaqeOH/aID0FQ== dependencies: buffer "^6.0.0" "@types/inquirer@^8.1.3": - version "8.2.3" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.3.tgz#985515d04879a0d0c1f5f49ec375767410ba9dab" - integrity sha512-ZlBqD+8WIVNy3KIVkl+Qne6bGLW2erwN0GJXY9Ri/9EMbyupee3xw3H0Mmv5kJoLyNpfd/oHlwKxO0DUDH7yWA== + version "8.2.5" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.5.tgz#c508423bcc11126db278170ab07347783ac2300c" + integrity sha512-QXlzybid60YtAwfgG3cpykptRYUx2KomzNutMlWsQC64J/WG/gQSl+P4w7A21sGN0VIxRVava4rgnT7FQmFCdg== dependencies: "@types/through" "*" @@ -2562,16 +2561,16 @@ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/react-native@^0.64.10": - version "0.64.27" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.27.tgz#f16e0b713c733c2476e7b16c92bf767f0675c560" - integrity sha512-vOEGMQGKNp6B1UfofKvCit2AxwByI6cbSa71E2uuxuvFr7FATVlykDbUS/Yht1HJhnbP5qlNOYw4ocUvDGjwbA== + version "0.64.29" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.29.tgz#521544f12f01192e38cdaa376817eceb0c4104db" + integrity sha512-nCa4rcAlilTWL7wEUwTnxo6HjxQvFjVeDPK9taglDvId06pw/eOUu2NozfpwY91o8K7UdZn8VUoDRaGt2i8LBA== dependencies: "@types/react" "^17" "@types/react@^17": - version "17.0.50" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.50.tgz#39abb4f7098f546cfcd6b51207c90c4295ee81fc" - integrity sha512-ZCBHzpDb5skMnc1zFXAXnL3l1FAdi+xZvwxK+PkglMmBrwjpp9nKaWuEvrGnSifCJmBFGxZOOFuwC6KH/s0NuA== + version "17.0.52" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.52.tgz#10d8b907b5c563ac014a541f289ae8eaa9bf2e9b" + integrity sha512-vwk8QqVODi0VaZZpDXQCmEmiOuyjEFPY7Ttaw5vjM112LOq37yz1CDJGrRJwA1fYEq4Iitd5rnjd1yWAc/bT+A== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2621,15 +2620,15 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== -"@types/validator@^13.1.3": - version "13.7.7" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.7.tgz#e87cf34dd08522d21acf30130fd8941f433b81b5" - integrity sha512-jiEw2kTUJ8Jsh4A1K4b5Pkjj9Xz6FktLLOQ36ZVLRkmxFbpTvAV2VRoKMojz8UlZxNg/2dZqzpigH4JYn1bkQg== +"@types/validator@^13.1.3", "@types/validator@^13.7.10": + version "13.7.10" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.10.tgz#f9763dc0933f8324920afa9c0790308eedf55ca7" + integrity sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ== "@types/varint@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@types/varint/-/varint-6.0.0.tgz#4ad73c23cbc9b7e44379a7729ace7ed9c8bc9854" - integrity sha512-2jBazyxGl4644tvu3VAez8UA/AtrcEetT9HOeAbqZ/vAcRVL/ZDFQjSS7rkWusU5cyONQVUz+nwwrNZdMva4ow== + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/varint/-/varint-6.0.1.tgz#018d424627c7951d370d73816e97e143dc99523b" + integrity sha512-fQdOiZpDMBvaEdl12P1x7xlTPRAtd7qUUtVaWgkCy8DC//wCv19nqFFtrnR3y/ac6VFY0UUvYuQqfKzZTSE26w== dependencies: "@types/node" "*" @@ -2811,9 +2810,9 @@ acorn@^7.1.1, acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4, acorn@^8.4.1: - version "8.8.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" - integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + version "8.8.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" + integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== add-stream@^1.0.0: version "1.0.0" @@ -2855,9 +2854,9 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.11.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" - integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== + version "8.11.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.2.tgz#aecb20b50607acf2569b6382167b65a96008bb78" + integrity sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2933,9 +2932,9 @@ anymatch@^2.0.0: normalize-path "^2.1.1" anymatch@^3.0.3: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -3037,20 +3036,20 @@ array-ify@^1.0.0: integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== array-includes@^3.1.4: - version "3.1.5" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" - integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" - get-intrinsic "^1.1.1" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" is-string "^1.0.7" array-map@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" - integrity sha512-123XMszMB01QKVptpDQ7x1m1pP5NmJIG1kbl0JSPPRezvwQChxAN0Gvzo7rvR1IZ2tOL2tmiy7kY/KKgnpVVpg== + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.1.tgz#d1bf3cc8813a7daaa335e5c8eb21d9d06230c1a7" + integrity sha512-sxHIeJTGEsRC8/hYkZzdJNNPZ41EXHVys7pqMw1iwE/Kx8/hto0UbDuGQsSJ0ujPovj9qUZl6EOY/EiZ2g3d9Q== array-reduce@~0.0.0: version "0.0.0" @@ -3068,23 +3067,23 @@ array-unique@^0.3.2: integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== array.prototype.flat@^1.2.5: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" - integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -array.prototype.reduce@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f" - integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw== +array.prototype.reduce@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" + integrity sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" es-array-method-boxes-properly "^1.0.0" is-string "^1.0.7" @@ -3228,13 +3227,6 @@ babel-jest@^27.5.1: graceful-fs "^4.2.9" slash "^3.0.0" -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" @@ -3387,9 +3379,9 @@ bcrypt-pbkdf@^1.0.0: tweetnacl "^0.14.3" before-after-hook@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" - integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== + version "2.2.3" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" + integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== big-integer@1.6.x: version "1.6.51" @@ -3413,10 +3405,10 @@ bn.js@^5.2.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -body-parser@1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" - integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== dependencies: bytes "3.1.2" content-type "~1.0.4" @@ -3426,7 +3418,7 @@ body-parser@1.20.0: http-errors "2.0.0" iconv-lite "0.4.24" on-finished "2.4.1" - qs "6.10.3" + qs "6.11.0" raw-body "2.5.1" type-is "~1.6.18" unpipe "1.0.0" @@ -3654,9 +3646,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001400: - version "1.0.30001412" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001412.tgz#30f67d55a865da43e0aeec003f073ea8764d5d7c" - integrity sha512-+TeEIee1gS5bYOiuf+PS/kp2mrXic37Hl66VY6EAfxasIk5fELTktK2oOezYed12H8w7jt3s512PpulQidPjwA== + version "1.0.30001434" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz#ec1ec1cfb0a93a34a0600d37903853030520a4e5" + integrity sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA== canonicalize@^1.0.1: version "1.0.8" @@ -3726,16 +3718,16 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.4.0.tgz#b28484fd436cbc267900364f096c9dc185efb251" - integrity sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug== + version "3.7.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.0.tgz#6d01b3696c59915b6ce057e4aa4adfc2fa25f5ef" + integrity sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog== cjs-module-lexer@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -class-transformer@0.5.1: +class-transformer@0.5.1, class-transformer@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== @@ -3759,6 +3751,15 @@ class-validator@0.13.1: libphonenumber-js "^1.9.7" validator "^13.5.2" +class-validator@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.0.tgz#40ed0ecf3c83b2a8a6a320f4edb607be0f0df159" + integrity sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A== + dependencies: + "@types/validator" "^13.7.10" + libphonenumber-js "^1.10.14" + validator "^13.7.0" + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -4146,11 +4147,9 @@ conventional-recommended-bump@^6.1.0: q "^1.5.1" convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== cookie-signature@1.0.6: version "1.0.6" @@ -4168,9 +4167,9 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.25.1: - version "3.25.3" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.3.tgz#d6a442a03f4eade4555d4e640e6a06151dd95d38" - integrity sha512-xVtYpJQ5grszDHEUU9O7XbjjcZ0ccX3LgQsyqSvTnjX97ZqEgn9F5srmrwwwMtbKzDllyFPL+O+2OFMl1lU4TQ== + version "3.26.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.26.1.tgz#0e710b09ebf689d719545ac36e49041850f943df" + integrity sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A== dependencies: browserslist "^4.21.4" @@ -4203,9 +4202,9 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: parse-json "^4.0.0" cosmiconfig@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" - integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== dependencies: "@types/parse-json" "^4.0.0" import-fresh "^3.2.1" @@ -4304,9 +4303,9 @@ dateformat@^3.0.0: integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== dayjs@^1.8.15: - version "1.11.5" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.5.tgz#00e8cc627f231f9499c19b38af49f56dc0ac5e93" - integrity sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA== + version "1.11.6" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.6.tgz#2e79a226314ec3ec904e3ee1dd5a4f5e5b1c7afb" + integrity sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ== debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" @@ -4335,9 +4334,9 @@ debuglog@^1.0.1: integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== decamelize-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" - integrity sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg== + version "1.1.1" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" + integrity sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg== dependencies: decamelize "^1.1.0" map-obj "^1.0.0" @@ -4348,9 +4347,9 @@ decamelize@^1.1.0, decamelize@^1.2.0: integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== decimal.js@^10.2.1: - version "10.4.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.1.tgz#be75eeac4a2281aace80c1a8753587c27ef053e7" - integrity sha512-F29o+vci4DodHYT9UrR5IEbfBw9pE5eSapIJdTqXK5+6hq+t8VRxwQyKlW2i+KDKFkkJQRvFyI/QXD83h8LyQw== + version "10.4.2" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.2.tgz#0341651d1d997d86065a2ce3a441fbd0d8e8b98e" + integrity sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA== decode-uri-component@^0.2.0: version "0.2.2" @@ -4383,9 +4382,9 @@ deepmerge@^4.2.2: integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA== + version "1.0.4" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== dependencies: clone "^1.0.2" @@ -4493,9 +4492,9 @@ did-resolver@^3.1.3: integrity sha512-Eeo2F524VM5N3W4GwglZrnul2y6TLTwMQP3In62JdG34NZoqihYyOZLk+5wUW8sSgvIYIcJM8Dlt3xsdKZZ3tg== did-resolver@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.0.0.tgz#fc8f657b4cd7f44c2921051fb046599fbe7d4b31" - integrity sha512-/roxrDr9EnAmLs+s9T+8+gcpilMo+IkeytcsGO7dcxvTmVJ+0Rt60HtV8o0UXHhGBo0Q+paMH/0ffXz1rqGFYg== + version "4.0.1" + resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.0.1.tgz#11bb3f19ed1c8f53f4af4702912fa9f7852fc305" + integrity sha512-eHs2VLKhcANmh08S87PKvOauIAmSOd7nb7AlhNxcvOyDAIGQY1UfbiqI1VOW5IDKvOO6aEWY+5edOt1qrCp1Eg== diff-sequences@^26.6.2: version "26.6.2" @@ -4578,9 +4577,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.4.251: - version "1.4.262" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.262.tgz#25715dfbae4c2e0640517cba184715241ecd8e63" - integrity sha512-Ckn5haqmGh/xS8IbcgK3dnwAVnhDyo/WQnklWn6yaMucYTq7NNxwlGE8ElzEOnonzRLzUCo2Ot3vUb2GYUF2Hw== + version "1.4.284" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" + integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== emittery@^0.8.1: version "0.8.1" @@ -4655,10 +4654,10 @@ errorhandler@^1.5.0: accepts "~1.3.7" escape-html "~1.0.3" -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.1: - version "1.20.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.3.tgz#90b143ff7aedc8b3d189bcfac7f1e3e3f81e9da1" - integrity sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw== +es-abstract@^1.19.0, es-abstract@^1.20.4: + version "1.20.4" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" + integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" @@ -4670,7 +4669,7 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19 has-property-descriptors "^1.0.0" has-symbols "^1.0.3" internal-slot "^1.0.3" - is-callable "^1.2.6" + is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" @@ -5014,20 +5013,20 @@ expo-modules-autolinking@^0.0.3: fs-extra "^9.1.0" expo-random@*: - version "12.3.0" - resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-12.3.0.tgz#4a45bcb14e285a4a9161e4a5dc82ff6c3fc2ac0c" - integrity sha512-q+AsTfGNT+Q+fb2sRrYtRkI3g5tV4H0kuYXM186aueILGO/vLn/YYFa7xFZj1IZ8LJZg2h96JDPDpsqHfRG2mQ== + version "13.0.0" + resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-13.0.0.tgz#fc9c1496ac9f7555563d86de0db25966739c028f" + integrity sha512-aGb0vtUmFFuW0TF1rdOgsz89zEVD/RXUPUnnZy5+i3jJeQ2PerJ4uo72/EuWqHpCBNto8/qT+aCzFinmQDeTAA== dependencies: base64-js "^1.3.0" express@^4.17.1: - version "4.18.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" - integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.0" + body-parser "1.20.1" content-disposition "0.5.4" content-type "~1.0.4" cookie "0.5.0" @@ -5046,7 +5045,7 @@ express@^4.17.1: parseurl "~1.3.3" path-to-regexp "0.1.7" proxy-addr "~2.0.7" - qs "6.10.3" + qs "6.11.0" range-parser "~1.2.1" safe-buffer "5.2.1" send "0.18.0" @@ -5314,9 +5313,9 @@ flatted@^3.1.0: integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.187.1" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.187.1.tgz#52b2c7ebd7544b75bda0676380138bc5b3de3177" - integrity sha512-ZvlTeakTTMmYGukt4EIQtLEp4ie45W+jK325uukGgiqFg2Rl7TdpOJQbOLUN2xMeGS+WvXaK0uIJ3coPGDXFGQ== + version "0.193.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.193.0.tgz#8d705fc2d6b378a24bae189014f6f0320d040c4f" + integrity sha512-x7ZoArE1UO3Nk2rkq/KK/Tkp714QDMVzEsxIyK2+p7Alx+88LY7KgqmeQZuiAG8TCHucmYuHefbk3KsVFVjouA== flow-parser@^0.121.0: version "0.121.0" @@ -5652,9 +5651,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.17.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" - integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== + version "13.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.18.0.tgz#fb224daeeb2bb7d254cd2c640f003528b8d0c1dc" + integrity sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A== dependencies: type-fest "^0.20.2" @@ -6103,7 +6102,7 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.2.6: +is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== @@ -6116,9 +6115,9 @@ is-ci@^2.0.0: ci-info "^2.0.0" is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" - integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== dependencies: has "^1.0.3" @@ -6404,9 +6403,9 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz#31d18bdd127f825dd02ea7bfdfd906f8ab840e9f" - integrity sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A== + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== dependencies: "@babel/core" "^7.12.3" "@babel/parser" "^7.14.7" @@ -6701,9 +6700,9 @@ jest-mock@^27.5.1: "@types/node" "*" jest-pnp-resolver@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" - integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== jest-regex-util@^26.0.0: version "26.0.0" @@ -6933,9 +6932,9 @@ jetifier@^1.6.2: integrity sha512-3Zi16h6L5tXDRQJTb221cnRoVG9/9OvreLdLU2/ZjRv/GILL+2Cemt0IKvkowwkDpvouAU1DQPOJ7qaiHeIdrw== joi@^17.2.1: - version "17.6.1" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.1.tgz#e77422f277091711599634ac39a409e599d7bdaa" - integrity sha512-Hl7/iBklIX345OCM1TiFSCZRVaAOLDGlWCp0Df2vWYgBgjkezaR7Kvm3joBciBHQjZj5sxXs859r6eqsRSlG8w== + version "17.7.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3" + integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -7112,9 +7111,9 @@ jsonfile@^6.0.1: graceful-fs "^4.1.6" jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - integrity sha512-trvBk1ki43VZptdBI5rIlG4YOzyeH/WefQt5rj1grasPn4iiZWKet8nkgc4GlsAylaztn0qZfUYOiTsASJFdNA== + version "0.0.1" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" + integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" @@ -7246,10 +7245,15 @@ libnpmpublish@^4.0.0: semver "^7.1.3" ssri "^8.0.1" +libphonenumber-js@^1.10.14: + version "1.10.17" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.17.tgz#7efcfa3068fc076bc59a43a08a723ccd95308474" + integrity sha512-UQrNzsusSn5qaojdpWqporWRdpx6AGeb+egj64NrpYuyKHvnSH9jMp/1Dy3b/WnMyJA5zgV1yw//jC6J0dCXkw== + libphonenumber-js@^1.9.7: - version "1.10.13" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.13.tgz#0b5833c7fdbf671140530d83531c6753f7e0ea3c" - integrity sha512-b74iyWmwb4GprAUPjPkJ11GTC7KX4Pd3onpJfKxYyY8y9Rbb4ERY47LvCMEDM09WD3thiLDMXtkfDK/AX+zT7Q== + version "1.10.14" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.14.tgz#e29da7f539751f724ac54017a098e3c7ca23de94" + integrity sha512-McGS7GV/WjJ2KjfOGhJU1oJn29RYeo7Q+RpANRbUNMQ9gj5XArpbjurSuyYPTejFwbaUojstQ4XyWCrAzGOUXw== lines-and-columns@^1.1.6: version "1.2.4" @@ -7948,9 +7952,9 @@ minipass@^2.6.0, minipass@^2.9.0: yallist "^3.0.0" minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: - version "3.3.4" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.4.tgz#ca99f95dd77c43c7a76bf51e6d200025eee0ffae" - integrity sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw== + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== dependencies: yallist "^4.0.0" @@ -8040,9 +8044,9 @@ mute-stream@0.0.8, mute-stream@~0.0.4: integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.11.1: - version "2.16.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" - integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== + version "2.17.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" + integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== nanomatch@^1.2.9: version "1.2.13" @@ -8496,7 +8500,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0, object.assign@^4.1.4: +object.assign@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== @@ -8507,14 +8511,14 @@ object.assign@^4.1.0, object.assign@^4.1.4: object-keys "^1.1.1" object.getownpropertydescriptors@^2.0.3: - version "2.1.4" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37" - integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ== + version "2.1.5" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz#db5a9002489b64eef903df81d6623c07e5b4b4d3" + integrity sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw== dependencies: - array.prototype.reduce "^1.0.4" + array.prototype.reduce "^1.0.5" call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.20.1" + es-abstract "^1.20.4" object.pick@^1.3.0: version "1.3.0" @@ -8524,13 +8528,13 @@ object.pick@^1.3.0: isobject "^3.0.1" object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" on-finished@2.4.1: version "2.4.1" @@ -8963,9 +8967,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" - integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + version "2.8.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.0.tgz#c7df58393c9ba77d6fba3921ae01faf994fb9dc9" + integrity sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA== pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" @@ -9010,9 +9014,9 @@ promise-retry@^2.0.1: retry "^0.12.0" promise@^8.0.3: - version "8.2.0" - resolved "https://registry.yarnpkg.com/promise/-/promise-8.2.0.tgz#a1f6280ab67457fbfc8aad2b198c9497e9e5c806" - integrity sha512-+CMAlLHqwRYwBMXKCP+o8ns7DN+xHDUiI+0nArsiJ9y+kJVPLFxEaSw6Ha9s9H0tftxg2Yzl25wqj9G7m5wLZg== + version "8.3.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" + integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== dependencies: asap "~2.0.6" @@ -9098,14 +9102,7 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== -qs@6.10.3: - version "6.10.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" - integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== - dependencies: - side-channel "^1.0.4" - -qs@^6.9.4: +qs@6.11.0, qs@^6.9.4: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== @@ -9178,9 +9175,9 @@ rc@^1.2.8: strip-json-comments "~2.0.1" react-devtools-core@^4.6.0: - version "4.26.0" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.26.0.tgz#d3d0f59d62ccf1ac03017a7e92f0fe71455019cc" - integrity sha512-OO0Q+vXtHYCXvRQ6elLiOUph3MjsCpuYktGTLnBpizYm46f8tAPuJKihGkwsceitHSJNpzNIjJaYHgX96CyTUQ== + version "4.26.1" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.26.1.tgz#2893fea58089be64c5356d5bd0eebda8d1bbf317" + integrity sha512-r1csa5n9nABVpSdAadwTG7K+SfgRJPc/Hdx89BkV5IlA1mEGgGi3ir630ST5D/xYlJQaY3VE75YGADgpNW7HIw== dependencies: shell-quote "^1.6.1" ws "^7" @@ -9468,15 +9465,15 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== +regenerator-runtime@^0.13.10, regenerator-runtime@^0.13.2: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== regenerator-transform@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" - integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== + version "0.15.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" + integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== dependencies: "@babel/runtime" "^7.8.4" @@ -9503,16 +9500,16 @@ regexpp@^3.1.0: integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== regexpu-core@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.1.tgz#a69c26f324c1e962e9ffd0b88b055caba8089139" - integrity sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ== + version "5.2.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.2.tgz#3e4e5d12103b64748711c3aad69934d7718e75fc" + integrity sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw== dependencies: regenerate "^1.4.2" regenerate-unicode-properties "^10.1.0" regjsgen "^0.7.1" regjsparser "^0.9.1" unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" regjsgen@^0.7.1: version "0.7.1" @@ -9793,9 +9790,9 @@ scheduler@^0.20.1: integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" @@ -9907,9 +9904,9 @@ shell-quote@1.6.1: jsonify "~0.0.0" shell-quote@^1.6.1: - version "1.7.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" - integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== + version "1.7.4" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.4.tgz#33fe15dee71ab2a81fcbd3a52106c5cfb9fb75d8" + integrity sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw== shelljs@^0.8.4: version "0.8.5" @@ -10030,9 +10027,9 @@ socks-proxy-agent@^6.0.0: socks "^2.6.2" socks@^2.3.3, socks@^2.6.2: - version "2.7.0" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.0.tgz#f9225acdb841e874dca25f870e9130990f3913d0" - integrity sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA== + version "2.7.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" + integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== dependencies: ip "^2.0.0" smart-buffer "^4.2.0" @@ -10170,9 +10167,9 @@ ssri@^8.0.0, ssri@^8.0.1: minipass "^3.1.1" stack-utils@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" - integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" @@ -10248,22 +10245,22 @@ string-width@^1.0.1: strip-ansi "^6.0.1" string.prototype.trimend@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" - integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" + es-abstract "^1.20.4" string.prototype.trimstart@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" - integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" + es-abstract "^1.20.4" string_decoder@^1.1.1: version "1.3.0" @@ -10401,9 +10398,9 @@ table-layout@^1.0.2: wordwrapjs "^4.0.0" table@^6.0.9: - version "6.8.0" - resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" - integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== + version "6.8.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" + integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA== dependencies: ajv "^8.0.1" lodash.truncate "^4.4.2" @@ -10425,9 +10422,9 @@ tar@^4.4.12, tar@^4.4.13: yallist "^3.1.1" tar@^6.0.2, tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: - version "6.1.11" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" - integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + version "6.1.12" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.12.tgz#3b742fb05669b55671fb769ab67a7791ea1a62e6" + integrity sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" @@ -10671,10 +10668,10 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== tslog@^3.2.0: version "3.3.4" @@ -10807,9 +10804,9 @@ uglify-es@^3.1.9: source-map "~0.6.1" uglify-js@^3.1.4: - version "3.17.2" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.2.tgz#f55f668b9a64b213977ae688703b6bbb7ca861c6" - integrity sha512-bbxglRjsGQMchfvXZNusUcYgiB9Hx2K4AHYXQy2DITZ9Rd+JzhX7+hoocE5Winr7z2oHvPsekkBwXtigvxevXg== + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== uid-number@0.0.6: version "0.0.6" @@ -10849,10 +10846,10 @@ unicode-match-property-ecmascript@^2.0.0: unicode-canonical-property-names-ecmascript "^2.0.0" unicode-property-aliases-ecmascript "^2.0.0" -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== unicode-property-aliases-ecmascript@^2.0.0: version "2.1.0" @@ -10922,9 +10919,9 @@ upath@^2.0.1: integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== update-browserslist-db@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18" - integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg== + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -11032,7 +11029,7 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" -validator@^13.5.2: +validator@^13.5.2, validator@^13.7.0: version "13.7.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== @@ -11090,9 +11087,9 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: defaults "^1.0.3" web-did-resolver@^2.0.8: - version "2.0.20" - resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.20.tgz#22e053b0f8bc1f4ab03da05989ce934852b7623f" - integrity sha512-qGcrm01B+ytCZUYhxH0mGOk0Ldf67kXUXLsNth6F3sx3fhUKNSIE8D+MnMFRugQm7j87mDHqUTDLmW9c90g3nw== + version "2.0.21" + resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.21.tgz#065797dee3e37cd9f19261d04a90144fe576e5df" + integrity sha512-vKYz0s9spYfYrKhrF88F44lkofS1yj6TCF40+i077a7boru2BNROl5VZEIVL9jJRUDsNzvmVSKkq3kS8kZnB2Q== dependencies: cross-fetch "^3.1.5" did-resolver "^4.0.0" From c34a3df580114a87a7d276213594f0d2339ba243 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Thu, 12 Jan 2023 05:26:34 +0100 Subject: [PATCH 007/139] docs: update readme packages (#1206) * docs: update readme packages Signed-off-by: Karim Stekelenburg Co-authored-by: Ariel Gentile --- README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 074c7a0127..314db5a4d6 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] - ✅ Smart Auto Acceptance of Connections, Credentials and Proofs - 🚧 Receiving and Verifying revocable Indy Credentials - 🚧 W3C Linked Data VCs, BBS+ Signatures -- 🚧 Multi Tenancy +- ✅ Multi Tenancy - ❌ Browser ### Packages @@ -101,6 +101,29 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap] + + @aries-framework/action-menu + + + @aries-framework/action-menu version + + + + @aries-framework/question-answer + + + @aries-framework/question-answer version + + + + + @aries-framework/tenants + + + @aries-framework/tenants version + + + ## Getting Started From 8a04d1b8e84f2198e1663aa9232ae59f53459ea5 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 12 Jan 2023 18:01:19 +0800 Subject: [PATCH 008/139] chore: remove husky pre-push hook (#1209) Signed-off-by: Timo Glastra Signed-off-by: Timo Glastra --- .husky/pre-push | 1 - package.json | 2 -- yarn.lock | 5 ----- 3 files changed, 8 deletions(-) delete mode 100755 .husky/pre-push diff --git a/.husky/pre-push b/.husky/pre-push deleted file mode 100755 index b969aaaf8d..0000000000 --- a/.husky/pre-push +++ /dev/null @@ -1 +0,0 @@ -yarn validate \ No newline at end of file diff --git a/package.json b/package.json index 139ef64ae7..636ee9f284 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "test": "jest", "lint": "eslint --ignore-path .gitignore .", "validate": "yarn lint && yarn check-types && yarn check-format", - "prepare": "husky install", "run-mediator": "ts-node ./samples/mediator.ts", "next-version-bump": "ts-node ./scripts/get-next-bump.ts" }, @@ -48,7 +47,6 @@ "eslint-plugin-import": "^2.23.4", "eslint-plugin-prettier": "^3.4.0", "express": "^4.17.1", - "husky": "^7.0.1", "indy-sdk": "^1.16.0-dev-1636", "jest": "^27.0.4", "lerna": "^4.0.0", diff --git a/yarn.lock b/yarn.lock index eb7826abc9..dfd89ae3dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5871,11 +5871,6 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -husky@^7.0.1: - version "7.0.4" - resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" - integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== - iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" From 86647e7f55c9a362f6ab500538c4de2112e42206 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 12 Jan 2023 19:05:19 +0800 Subject: [PATCH 009/139] feat(anoncreds): add anoncreds registry service (#1204) Signed-off-by: Timo Glastra --- packages/anoncreds/src/AnonCredsModule.ts | 23 +++++ .../anoncreds/src/AnonCredsModuleConfig.ts | 28 ++++++ .../src/__tests__/AnonCredsModule.test.ts | 28 ++++++ .../__tests__/AnonCredsModuleConfig.test.ts | 15 ++++ .../anoncreds/src/error/AnonCredsError.ts | 7 ++ packages/anoncreds/src/error/index.ts | 1 + packages/anoncreds/src/index.ts | 3 + .../services/registry/AnonCredsRegistry.ts | 6 +- .../registry/AnonCredsRegistryService.ts | 28 ++++++ .../AnonCredsRegistryService.test.ts | 38 ++++++++ packages/indy-sdk/src/IndySdkModuleConfig.ts | 2 +- .../services/IndySdkAnonCredsRegistry.ts | 86 +++++++++++-------- .../services/IndySdkHolderService.ts | 1 - .../utils/__tests__/identifiers.test.ts | 17 ++++ .../src/anoncreds/utils/identifiers.ts | 11 +++ 15 files changed, 254 insertions(+), 40 deletions(-) create mode 100644 packages/anoncreds/src/AnonCredsModule.ts create mode 100644 packages/anoncreds/src/AnonCredsModuleConfig.ts create mode 100644 packages/anoncreds/src/__tests__/AnonCredsModule.test.ts create mode 100644 packages/anoncreds/src/__tests__/AnonCredsModuleConfig.test.ts create mode 100644 packages/anoncreds/src/error/AnonCredsError.ts create mode 100644 packages/anoncreds/src/error/index.ts create mode 100644 packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts create mode 100644 packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts diff --git a/packages/anoncreds/src/AnonCredsModule.ts b/packages/anoncreds/src/AnonCredsModule.ts new file mode 100644 index 0000000000..0da6e242f7 --- /dev/null +++ b/packages/anoncreds/src/AnonCredsModule.ts @@ -0,0 +1,23 @@ +import type { AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' +import type { DependencyManager, Module } from '@aries-framework/core' + +import { AnonCredsModuleConfig } from './AnonCredsModuleConfig' +import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' + +/** + * @public + */ +export class AnonCredsModule implements Module { + public readonly config: AnonCredsModuleConfig + + public constructor(config: AnonCredsModuleConfigOptions) { + this.config = new AnonCredsModuleConfig(config) + } + + public register(dependencyManager: DependencyManager) { + // Config + dependencyManager.registerInstance(AnonCredsModuleConfig, this.config) + + dependencyManager.registerSingleton(AnonCredsRegistryService) + } +} diff --git a/packages/anoncreds/src/AnonCredsModuleConfig.ts b/packages/anoncreds/src/AnonCredsModuleConfig.ts new file mode 100644 index 0000000000..9f7b971aab --- /dev/null +++ b/packages/anoncreds/src/AnonCredsModuleConfig.ts @@ -0,0 +1,28 @@ +import type { AnonCredsRegistry } from './services' + +/** + * @public + * AnonCredsModuleConfigOptions defines the interface for the options of the AnonCredsModuleConfig class. + */ +export interface AnonCredsModuleConfigOptions { + /** + * A list of AnonCreds registries to make available to the AnonCreds module. + */ + registries: [AnonCredsRegistry, ...AnonCredsRegistry[]] +} + +/** + * @public + */ +export class AnonCredsModuleConfig { + private options: AnonCredsModuleConfigOptions + + public constructor(options: AnonCredsModuleConfigOptions) { + this.options = options + } + + /** See {@link AnonCredsModuleConfigOptions.registries} */ + public get registries() { + return this.options.registries + } +} diff --git a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts new file mode 100644 index 0000000000..90aa51ce66 --- /dev/null +++ b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts @@ -0,0 +1,28 @@ +import type { AnonCredsRegistry } from '../services' +import type { DependencyManager } from '@aries-framework/core' + +import { AnonCredsModule } from '../AnonCredsModule' +import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' +import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' + +const dependencyManager = { + registerInstance: jest.fn(), + registerSingleton: jest.fn(), +} as unknown as DependencyManager + +const registry = {} as AnonCredsRegistry + +describe('AnonCredsModule', () => { + test('registers dependencies on the dependency manager', () => { + const anonCredsModule = new AnonCredsModule({ + registries: [registry], + }) + anonCredsModule.register(dependencyManager) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsRegistryService) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(AnonCredsModuleConfig, anonCredsModule.config) + }) +}) diff --git a/packages/anoncreds/src/__tests__/AnonCredsModuleConfig.test.ts b/packages/anoncreds/src/__tests__/AnonCredsModuleConfig.test.ts new file mode 100644 index 0000000000..beaca8bf53 --- /dev/null +++ b/packages/anoncreds/src/__tests__/AnonCredsModuleConfig.test.ts @@ -0,0 +1,15 @@ +import type { AnonCredsRegistry } from '../services' + +import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' + +describe('AnonCredsModuleConfig', () => { + test('sets values', () => { + const registry = {} as AnonCredsRegistry + + const config = new AnonCredsModuleConfig({ + registries: [registry], + }) + + expect(config.registries).toEqual([registry]) + }) +}) diff --git a/packages/anoncreds/src/error/AnonCredsError.ts b/packages/anoncreds/src/error/AnonCredsError.ts new file mode 100644 index 0000000000..eb6d250a4a --- /dev/null +++ b/packages/anoncreds/src/error/AnonCredsError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +export class AnonCredsError extends AriesFrameworkError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/anoncreds/src/error/index.ts b/packages/anoncreds/src/error/index.ts new file mode 100644 index 0000000000..d9786950bf --- /dev/null +++ b/packages/anoncreds/src/error/index.ts @@ -0,0 +1 @@ +export * from './AnonCredsError' diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 83fdeb7877..759e343c2c 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -1,2 +1,5 @@ export * from './models' export * from './services' +export * from './error' +export { AnonCredsModule } from './AnonCredsModule' +export { AnonCredsModuleConfig, AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts index 966a1afb95..e3061043dd 100644 --- a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts @@ -8,8 +8,12 @@ import type { GetRevocationRegistryDefinitionReturn } from './RevocationRegistry import type { GetSchemaReturn, RegisterSchemaOptions, RegisterSchemaReturn } from './SchemaOptions' import type { AgentContext } from '@aries-framework/core' -// This service can be registered multiple times in a single AFJ instance. +/** + * @public + */ export interface AnonCredsRegistry { + supportedIdentifier: RegExp + getSchema(agentContext: AgentContext, schemaId: string): Promise registerSchema(agentContext: AgentContext, options: RegisterSchemaOptions): Promise diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts new file mode 100644 index 0000000000..8ee8eb4b50 --- /dev/null +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts @@ -0,0 +1,28 @@ +import type { AnonCredsRegistry } from '.' +import type { AgentContext } from '@aries-framework/core' + +import { injectable } from '@aries-framework/core' + +import { AnonCredsModuleConfig } from '../../AnonCredsModuleConfig' +import { AnonCredsError } from '../../error' + +/** + * @internal + * The AnonCreds registry service manages multiple {@link AnonCredsRegistry} instances + * and returns the correct registry based on a given identifier + */ +@injectable() +export class AnonCredsRegistryService { + public async getRegistryForIdentifier(agentContext: AgentContext, identifier: string): Promise { + const registries = agentContext.dependencyManager.resolve(AnonCredsModuleConfig).registries + + // TODO: should we check if multiple are registered? + const registry = registries.find((registry) => registry.supportedIdentifier.test(identifier)) + + if (!registry) { + throw new AnonCredsError(`No AnonCredsRegistry registered for identifier '${registry}'`) + } + + return registry + } +} diff --git a/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts new file mode 100644 index 0000000000..096626f805 --- /dev/null +++ b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts @@ -0,0 +1,38 @@ +import type { AnonCredsRegistry } from '../AnonCredsRegistry' + +import { getAgentContext } from '../../../../../core/tests/helpers' +import { AnonCredsModuleConfig } from '../../../AnonCredsModuleConfig' +import { AnonCredsError } from '../../../error' +import { AnonCredsRegistryService } from '../AnonCredsRegistryService' + +const registryOne = { + supportedIdentifier: /a/, +} as AnonCredsRegistry + +const registryTwo = { + supportedIdentifier: /b/, +} as AnonCredsRegistry + +const agentContext = getAgentContext({ + registerInstances: [ + [ + AnonCredsModuleConfig, + new AnonCredsModuleConfig({ + registries: [registryOne, registryTwo], + }), + ], + ], +}) + +const anonCredsRegistryService = new AnonCredsRegistryService() + +describe('AnonCredsRegistryService', () => { + test('returns the registry for an identifier based on the supportedMethods regex', async () => { + await expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'a')).resolves.toEqual(registryOne) + await expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'b')).resolves.toEqual(registryTwo) + }) + + test('throws AnonCredsError if no registry is found for the given identifier', async () => { + await expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'c')).rejects.toThrow(AnonCredsError) + }) +}) diff --git a/packages/indy-sdk/src/IndySdkModuleConfig.ts b/packages/indy-sdk/src/IndySdkModuleConfig.ts index a01bf813b3..e5b16142ee 100644 --- a/packages/indy-sdk/src/IndySdkModuleConfig.ts +++ b/packages/indy-sdk/src/IndySdkModuleConfig.ts @@ -38,7 +38,7 @@ export class IndySdkModuleConfig { this.options = options } - /** See {@link IndySdkModuleConfigOptions.resolvers} */ + /** See {@link IndySdkModuleConfigOptions.indySdk} */ public get indySdk() { return this.options.indySdk } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 3b5c6a08ce..95b08fa88b 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -1,3 +1,4 @@ +import type { IndySdk } from '../../types' import type { AnonCredsRegistry, GetCredentialDefinitionReturn, @@ -12,17 +13,16 @@ import type { import type { AgentContext } from '@aries-framework/core' import type { Schema as IndySdkSchema } from 'indy-sdk' -import { inject } from '@aries-framework/core' - import { IndySdkError, isIndyError } from '../../error' import { IndySdkPoolService } from '../../ledger' -import { IndySdk, IndySdkSymbol } from '../../types' +import { IndySdkSymbol } from '../../types' import { didFromCredentialDefinitionId, didFromRevocationRegistryDefinitionId, didFromSchemaId, getLegacyCredentialDefinitionId, getLegacySchemaId, + indySdkAnonCredsRegistryIdentifierRegex, } from '../utils/identifiers' import { anonCredsRevocationListFromIndySdk, @@ -33,32 +33,34 @@ import { * TODO: validation of the identifiers. The Indy SDK classes only support the legacy (unqualified) identifiers. */ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { - private indySdk: IndySdk - private indySdkPoolService: IndySdkPoolService - - public constructor(@inject(IndySdkSymbol) indySdk: IndySdk, indySdkPoolService: IndySdkPoolService) { - this.indySdk = indySdk - this.indySdkPoolService = indySdkPoolService - } + /** + * This class only supports resolving and registering objects with legacy indy identifiers. + * It needs to include support for the schema, credential definition, revocation registry as well + * as the issuer id (which is needed when registering objects). + */ + public readonly supportedIdentifier = indySdkAnonCredsRegistryIdentifierRegex public async getSchema(agentContext: AgentContext, schemaId: string): Promise { try { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const did = didFromSchemaId(schemaId) - const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`) - const request = await this.indySdk.buildGetSchemaRequest(null, schemaId) + const request = await indySdk.buildGetSchemaRequest(null, schemaId) agentContext.config.logger.trace( `Submitting get schema request for schema '${schemaId}' to ledger '${pool.didIndyNamespace}'` ) - const response = await this.indySdkPoolService.submitReadRequest(pool, request) + const response = await indySdkPoolService.submitReadRequest(pool, request) agentContext.config.logger.trace(`Got un-parsed schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`, { response, }) - const [, schema] = await this.indySdk.parseGetSchemaResponse(response) + const [, schema] = await indySdk.parseGetSchemaResponse(response) agentContext.config.logger.debug(`Got schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`, { schema, }) @@ -117,7 +119,10 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } try { - const pool = this.indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + + const pool = indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) agentContext.config.logger.debug( `Register schema on ledger '${pool.didIndyNamespace}' with did '${options.schema.issuerId}'`, options.schema @@ -133,14 +138,9 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { // buildSchemaRequest (seqNo is not yet known) } as IndySdkSchema - const request = await this.indySdk.buildSchemaRequest(options.schema.issuerId, schema) + const request = await indySdk.buildSchemaRequest(options.schema.issuerId, schema) - const response = await this.indySdkPoolService.submitWriteRequest( - agentContext, - pool, - request, - options.schema.issuerId - ) + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, options.schema.issuerId) agentContext.config.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.didIndyNamespace}'`, { response, schema, @@ -189,19 +189,22 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { credentialDefinitionId: string ): Promise { try { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const did = didFromCredentialDefinitionId(credentialDefinitionId) - const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve credential definition '${credentialDefinitionId}'` ) - const request = await this.indySdk.buildGetCredDefRequest(null, credentialDefinitionId) + const request = await indySdk.buildGetCredDefRequest(null, credentialDefinitionId) agentContext.config.logger.trace( `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.didIndyNamespace}'` ) - const response = await this.indySdkPoolService.submitReadRequest(pool, request) + const response = await indySdkPoolService.submitReadRequest(pool, request) agentContext.config.logger.trace( `Got un-parsed credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, { @@ -209,7 +212,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } ) - const [, credentialDefinition] = await this.indySdk.parseGetCredDefResponse(response) + const [, credentialDefinition] = await indySdk.parseGetCredDefResponse(response) agentContext.config.logger.debug( `Got credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, { @@ -267,7 +270,10 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } try { - const pool = this.indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + + const pool = indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) agentContext.config.logger.debug( `Registering credential definition on ledger '${pool.didIndyNamespace}' with did '${options.credentialDefinition.issuerId}'`, options.credentialDefinition @@ -296,7 +302,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { options.credentialDefinition.tag ) - const request = await this.indySdk.buildCredDefRequest(options.credentialDefinition.issuerId, { + const request = await indySdk.buildCredDefRequest(options.credentialDefinition.issuerId, { id: credentialDefinitionId, schemaId: options.credentialDefinition.schemaId, tag: options.credentialDefinition.tag, @@ -305,7 +311,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ver: '1.0', }) - const response = await this.indySdkPoolService.submitWriteRequest( + const response = await indySdkPoolService.submitWriteRequest( agentContext, pool, request, @@ -350,18 +356,21 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { revocationRegistryDefinitionId: string ): Promise { try { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) - const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` ) - const request = await this.indySdk.buildGetRevocRegDefRequest(null, revocationRegistryDefinitionId) + const request = await indySdk.buildGetRevocRegDefRequest(null, revocationRegistryDefinitionId) agentContext.config.logger.trace( `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` ) - const response = await this.indySdkPoolService.submitReadRequest(pool, request) + const response = await indySdkPoolService.submitReadRequest(pool, request) agentContext.config.logger.trace( `Got un-parsed revocation registry definition '${revocationRegistryDefinitionId}' from ledger '${pool.didIndyNamespace}'`, { @@ -369,7 +378,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } ) - const [, revocationRegistryDefinition] = await this.indySdk.parseGetRevocRegDefResponse(response) + const [, revocationRegistryDefinition] = await indySdk.parseGetRevocRegDefResponse(response) agentContext.config.logger.debug( `Got revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, @@ -415,21 +424,24 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { timestamp: number ): Promise { try { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const did = didFromRevocationRegistryDefinitionId(revocationRegistryId) - const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.id}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` ) // TODO: implement caching for returned deltas - const request = await this.indySdk.buildGetRevocRegDeltaRequest(null, revocationRegistryId, 0, timestamp) + const request = await indySdk.buildGetRevocRegDeltaRequest(null, revocationRegistryId, 0, timestamp) agentContext.config.logger.trace( `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` ) - const response = await this.indySdkPoolService.submitReadRequest(pool, request) + const response = await indySdkPoolService.submitReadRequest(pool, request) agentContext.config.logger.trace( `Got revocation registry delta unparsed-response '${revocationRegistryId}' from ledger`, { @@ -437,7 +449,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } ) - const [, revocationRegistryDelta, deltaTimestamp] = await this.indySdk.parseGetRevocRegDeltaResponse(response) + const [, revocationRegistryDelta, deltaTimestamp] = await indySdk.parseGetRevocRegDeltaResponse(response) agentContext.config.logger.debug( `Got revocation registry deltas '${revocationRegistryId}' until timestamp ${timestamp} from ledger`, diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 88179381a9..49b619332d 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -13,7 +13,6 @@ import type { } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { - Cred, CredentialDefs, IndyRequestedCredentials, RevStates, diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts index f85ec160b5..76454b615a 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -5,9 +5,26 @@ import { getIndySeqNoFromUnqualifiedCredentialDefinitionId, getLegacyCredentialDefinitionId, getLegacySchemaId, + indySdkAnonCredsRegistryIdentifierRegex, } from '../identifiers' describe('identifiers', () => { + it('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { + const did = '7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' + const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' + const revocationRegistryId = + 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + + const anotherId = 'some:id' + + expect(indySdkAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + it('getLegacySchemaId should return a valid schema id given a did, name, and version', () => { const did = '12345' const name = 'backbench' diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts index bc59b5f8d4..62d2650602 100644 --- a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -1,3 +1,14 @@ +export const legacyIndyIssuerIdRegex = /^[a-zA-Z0-9]{21,22}$/ +export const legacyIndySchemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ +export const legacyIndyCredentialDefinitionIdRegex = + /^[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ +export const legacyIndyRevocationRegistryIdRegex = + /^[a-zA-Z0-9]{21,22}:4:[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)/ + +export const indySdkAnonCredsRegistryIdentifierRegex = new RegExp( + `${legacyIndyIssuerIdRegex.source}|${legacyIndySchemaIdRegex.source}|${legacyIndyCredentialDefinitionIdRegex.source}|${legacyIndyRevocationRegistryIdRegex.source}` +) + export function getIndySeqNoFromUnqualifiedCredentialDefinitionId(unqualifiedCredentialDefinitionId: string): number { // 5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npbd const [, , , seqNo] = unqualifiedCredentialDefinitionId.split(':') From b6f89f943dc4417626f868ac9f43a3d890ab62c6 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Thu, 12 Jan 2023 14:59:39 +0100 Subject: [PATCH 010/139] feat: add minimal oidc-client package (#1197) Work funded by the Government of Ontario. * feat: add openid4vc-client package Signed-off-by: Karim Stekelenburg Co-authored-by: Timo Glastra --- packages/openid4vc-client/README.md | 37 ++++++++++++++++++ packages/openid4vc-client/jest.config.ts | 14 +++++++ packages/openid4vc-client/package.json | 39 +++++++++++++++++++ .../src/OpenId4VcClientApi.ts | 17 ++++++++ .../src/OpenId4VcClientApiOptions.ts | 0 .../src/OpenId4VcClientModule.ts | 22 +++++++++++ .../src/OpenId4VcClientService.ts | 10 +++++ .../__tests__/OpenId4VcClientModule.test.ts | 24 ++++++++++++ packages/openid4vc-client/src/index.ts | 3 ++ packages/openid4vc-client/tests/setup.ts | 3 ++ packages/openid4vc-client/tsconfig.build.json | 7 ++++ packages/openid4vc-client/tsconfig.json | 6 +++ yarn.lock | 34 ++++++++++++++++ 13 files changed, 216 insertions(+) create mode 100644 packages/openid4vc-client/README.md create mode 100644 packages/openid4vc-client/jest.config.ts create mode 100644 packages/openid4vc-client/package.json create mode 100644 packages/openid4vc-client/src/OpenId4VcClientApi.ts create mode 100644 packages/openid4vc-client/src/OpenId4VcClientApiOptions.ts create mode 100644 packages/openid4vc-client/src/OpenId4VcClientModule.ts create mode 100644 packages/openid4vc-client/src/OpenId4VcClientService.ts create mode 100644 packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts create mode 100644 packages/openid4vc-client/src/index.ts create mode 100644 packages/openid4vc-client/tests/setup.ts create mode 100644 packages/openid4vc-client/tsconfig.build.json create mode 100644 packages/openid4vc-client/tsconfig.json diff --git a/packages/openid4vc-client/README.md b/packages/openid4vc-client/README.md new file mode 100644 index 0000000000..e89f6cab7a --- /dev/null +++ b/packages/openid4vc-client/README.md @@ -0,0 +1,37 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript Open ID Connect For Verifiable Credentials Client Module

+

+ License + typescript + @aries-framework/openid4vc-client version + +

+
+ +Open ID Connect For Verifiable Credentials Client Module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript). + +### Installation + +### Quick start + +### Example of usage diff --git a/packages/openid4vc-client/jest.config.ts b/packages/openid4vc-client/jest.config.ts new file mode 100644 index 0000000000..55c67d70a6 --- /dev/null +++ b/packages/openid4vc-client/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json new file mode 100644 index 0000000000..ee4b727356 --- /dev/null +++ b/packages/openid4vc-client/package.json @@ -0,0 +1,39 @@ +{ + "name": "@aries-framework/openid4vc-client", + "main": "build/index", + "types": "build/index", + "version": "0.3.2", + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/openid4vc-client", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/openid4vc-client" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf -rf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "0.3.2", + "@sphereon/openid4vci-client": "^0.3.6", + "class-transformer": "0.5.1", + "class-validator": "0.13.1" + }, + "peerDependencies": {}, + "devDependencies": { + "@aries-framework/node": "0.3.2", + "reflect-metadata": "^0.1.13", + "rimraf": "~3.0.2", + "typescript": "~4.3.0" + } +} diff --git a/packages/openid4vc-client/src/OpenId4VcClientApi.ts b/packages/openid4vc-client/src/OpenId4VcClientApi.ts new file mode 100644 index 0000000000..ccf2cb84f3 --- /dev/null +++ b/packages/openid4vc-client/src/OpenId4VcClientApi.ts @@ -0,0 +1,17 @@ +import { AgentContext, injectable } from '@aries-framework/core' + +import { OpenId4VcClientService } from './OpenId4VcClientService' + +/** + * @public + */ +@injectable() +export class OpenId4VcClientApi { + private agentContext: AgentContext + private openId4VcClientService: OpenId4VcClientService + + public constructor(agentContext: AgentContext, openId4VcClientService: OpenId4VcClientService) { + this.agentContext = agentContext + this.openId4VcClientService = openId4VcClientService + } +} diff --git a/packages/openid4vc-client/src/OpenId4VcClientApiOptions.ts b/packages/openid4vc-client/src/OpenId4VcClientApiOptions.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/openid4vc-client/src/OpenId4VcClientModule.ts b/packages/openid4vc-client/src/OpenId4VcClientModule.ts new file mode 100644 index 0000000000..fe941949cf --- /dev/null +++ b/packages/openid4vc-client/src/OpenId4VcClientModule.ts @@ -0,0 +1,22 @@ +import type { DependencyManager, Module } from '@aries-framework/core' + +import { OpenId4VcClientApi } from './OpenId4VcClientApi' +import { OpenId4VcClientService } from './OpenId4VcClientService' + +/** + * @public + */ +export class OpenId4VcClientModule implements Module { + public readonly api = OpenId4VcClientApi + + /** + * Registers the dependencies of the question answer module on the dependency manager. + */ + public register(dependencyManager: DependencyManager) { + // Api + dependencyManager.registerContextScoped(OpenId4VcClientApi) + + // Services + dependencyManager.registerSingleton(OpenId4VcClientService) + } +} diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts new file mode 100644 index 0000000000..9c54e9b81c --- /dev/null +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -0,0 +1,10 @@ +import { injectable, W3cCredentialService } from '@aries-framework/core' + +@injectable() +export class OpenId4VcClientService { + private w3cCredentialService: W3cCredentialService + + public constructor(w3cCredentialService: W3cCredentialService) { + this.w3cCredentialService = w3cCredentialService + } +} diff --git a/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts b/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts new file mode 100644 index 0000000000..b02fa08ffc --- /dev/null +++ b/packages/openid4vc-client/src/__tests__/OpenId4VcClientModule.test.ts @@ -0,0 +1,24 @@ +import type { DependencyManager } from '@aries-framework/core' + +import { OpenId4VcClientApi } from '../OpenId4VcClientApi' +import { OpenId4VcClientModule } from '../OpenId4VcClientModule' +import { OpenId4VcClientService } from '../OpenId4VcClientService' + +const dependencyManager = { + registerInstance: jest.fn(), + registerSingleton: jest.fn(), + registerContextScoped: jest.fn(), +} as unknown as DependencyManager + +describe('OpenId4VcClientModule', () => { + test('registers dependencies on the dependency manager', () => { + const openId4VcClientModule = new OpenId4VcClientModule() + openId4VcClientModule.register(dependencyManager) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(OpenId4VcClientApi) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcClientService) + }) +}) diff --git a/packages/openid4vc-client/src/index.ts b/packages/openid4vc-client/src/index.ts new file mode 100644 index 0000000000..3200b8fa6d --- /dev/null +++ b/packages/openid4vc-client/src/index.ts @@ -0,0 +1,3 @@ +export * from './OpenId4VcClientModule' +export * from './OpenId4VcClientApi' +export * from './OpenId4VcClientService' diff --git a/packages/openid4vc-client/tests/setup.ts b/packages/openid4vc-client/tests/setup.ts new file mode 100644 index 0000000000..4955aeb601 --- /dev/null +++ b/packages/openid4vc-client/tests/setup.ts @@ -0,0 +1,3 @@ +import 'reflect-metadata' + +jest.setTimeout(20000) diff --git a/packages/openid4vc-client/tsconfig.build.json b/packages/openid4vc-client/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/openid4vc-client/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/openid4vc-client/tsconfig.json b/packages/openid4vc-client/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/openid4vc-client/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/yarn.lock b/yarn.lock index dfd89ae3dc..5fb0819bd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2233,6 +2233,23 @@ resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== +"@sphereon/openid4vci-client@^0.3.6": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@sphereon/openid4vci-client/-/openid4vci-client-0.3.6.tgz#70bd76bb888b458274007170e2bb7e784849cbbe" + integrity sha512-C336F3VPMu1LSQAv1rn1KkXOVC3JxbrUzMzOBFlJfSKfnSxfqTqGSK0NTJLnAwGNOEAQ1M4Q/2KIXbDIaTU4Ww== + dependencies: + "@sphereon/ssi-types" "^0.8.1-next.123" + cross-fetch "^3.1.5" + debug "^4.3.4" + uint8arrays "^3.1.1" + +"@sphereon/ssi-types@^0.8.1-next.123": + version "0.8.1-unstable.145" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.8.1-unstable.145.tgz#418cf00ebb077ccb9644e652bc4e7fb0eb23b645" + integrity sha512-ixT8z5bwDWKJaMQTsUeRs7vMg5fz68BRJhxn10Tkeg68nJUEUHck44QJOhog0MmjNJKw2k6U/IqIS0oOdxTSHQ== + dependencies: + jwt-decode "^3.1.2" + "@stablelib/binary@^1.0.0", "@stablelib/binary@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" @@ -7125,6 +7142,11 @@ jsprim@^1.2.2: json-schema "0.4.0" verror "1.10.0" +jwt-decode@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" + integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -8022,6 +8044,11 @@ msrcrypto@^1.5.6: resolved "https://registry.yarnpkg.com/msrcrypto/-/msrcrypto-1.5.8.tgz#be419be4945bf134d8af52e9d43be7fa261f4a1c" integrity sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q== +multiformats@^9.4.2: + version "9.9.0" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" + integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== + multimatch@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" @@ -10808,6 +10835,13 @@ uid-number@0.0.6: resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" integrity sha512-c461FXIljswCuscZn67xq9PpszkPT6RjheWFQTgCyabJrTUozElanb0YEqv2UGgk247YpcJkFBuSGNvBlpXM9w== +uint8arrays@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" + integrity sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg== + dependencies: + multiformats "^9.4.2" + ultron@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" From c697716bf1837b9fef307f60ff97f01d3d926728 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Thu, 12 Jan 2023 16:13:02 +0100 Subject: [PATCH 011/139] fix(openid4vc-client): set package to private (#1210) Signed-off-by: Karim Stekelenburg --- packages/openid4vc-client/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index ee4b727356..cac010d054 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -6,6 +6,7 @@ "files": [ "build" ], + "private": true, "license": "Apache-2.0", "publishConfig": { "access": "public" From 409d36c7e3623845f4718802b884bb40867806e1 Mon Sep 17 00:00:00 2001 From: Grammatopoulos Athanasios Vasileios Date: Tue, 17 Jan 2023 15:33:01 +0200 Subject: [PATCH 012/139] docs: corrected some mistakes on demo documentation (#1215) Signed-off-by: Grammatopoulos Athanasios Vasileios --- demo/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/demo/README.md b/demo/README.md index 7f4d71df0d..93f14ee99f 100644 --- a/demo/README.md +++ b/demo/README.md @@ -17,7 +17,7 @@ Alice, a former student of Faber College, connects with the College, is issued a In order to use Aries Framework JavaScript some platform specific dependencies and setup is required. See our guides below to quickly set up you project with Aries Framework JavaScript for NodeJS, React Native and Electron. -- [NodeJS](https:/aries.js.org/guides/getting-started/prerequisites/nodejs) +- [NodeJS](https://aries.js.org/guides/getting-started/installation/nodejs) ### Run the demo @@ -57,8 +57,8 @@ yarn faber To set up a connection: -- Select 'setup connection' in both Agents -- Alice will print a invitation link which you then copy and paste to Faber +- Select 'receive connection invitation' in Alice and 'create connection invitation' in Faber +- Faber will print a invitation link which you then copy and paste to Alice - You have now set up a connection! To offer a credential: @@ -66,7 +66,7 @@ To offer a credential: - Select 'offer credential' in Faber - Faber will start with registering a schema and the credential definition accordingly - You have now send a credential offer to Alice! -- Go to Alice to accept the incoming credential offer +- Go to Alice to accept the incoming credential offer by selecting 'yes'. To request a proof: From 087980f1adf3ee0bc434ca9782243a62c6124444 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 17 Jan 2023 23:48:40 +0800 Subject: [PATCH 013/139] fix: fix typing issues with typescript 4.9 (#1214) Fixes https://github.com/hyperledger/aries-framework-javascript/issues/1205 Signed-off-by: Timo Glastra --- package.json | 14 +- packages/action-menu/package.json | 6 +- .../src/services/ActionMenuService.ts | 4 +- .../action-menu/tests/action-menu.e2e.test.ts | 4 +- packages/anoncreds/package.json | 6 +- .../src/services/AnonCredsHolderService.ts | 4 +- .../src/services/AnonCredsIssuerService.ts | 4 +- .../registry/CredentialDefinitionOptions.ts | 2 +- .../registry/RevocationListOptions.ts | 2 +- .../RevocationRegistryDefinitionOptions.ts | 2 +- .../src/services/registry/SchemaOptions.ts | 2 +- packages/bbs-signatures/package.json | 6 +- packages/core/package.json | 6 +- packages/core/src/agent/Agent.ts | 6 +- packages/core/src/agent/AgentConfig.ts | 2 +- packages/core/src/agent/AgentModules.ts | 2 +- packages/core/src/agent/BaseAgent.ts | 6 +- packages/core/src/agent/EnvelopeService.ts | 2 +- packages/core/src/agent/Events.ts | 2 +- .../core/src/agent/MessageHandlerRegistry.ts | 2 +- packages/core/src/agent/MessageReceiver.ts | 6 +- packages/core/src/agent/MessageSender.ts | 10 +- packages/core/src/agent/TransportService.ts | 4 +- .../core/src/agent/context/AgentContext.ts | 2 +- packages/core/src/cache/PersistedLruCache.ts | 2 +- packages/core/src/crypto/JwsService.ts | 2 +- packages/core/src/crypto/WalletKeyPair.ts | 2 +- .../SigningProviderRegistry.ts | 2 +- .../service/ServiceDecoratorExtension.ts | 2 +- .../basic-messages/BasicMessageEvents.ts | 2 +- .../basic-messages/BasicMessagesApi.ts | 2 +- .../modules/connections/ConnectionEvents.ts | 2 +- .../src/modules/connections/ConnectionsApi.ts | 4 +- .../modules/connections/ConnectionsModule.ts | 2 +- .../connections/DidExchangeProtocol.ts | 4 +- .../connections/DidExchangeStateMachine.ts | 2 +- .../modules/connections/TrustPingEvents.ts | 2 +- .../errors/ConnectionProblemReportError.ts | 2 +- .../errors/DidExchangeProblemReportError.ts | 2 +- .../modules/connections/models/did/DidDoc.ts | 2 +- .../did/authentication/Authentication.ts | 2 +- .../repository/ConnectionRecord.ts | 2 +- .../modules/credentials/CredentialEvents.ts | 2 +- .../credentials/CredentialProtocolOptions.ts | 37 +- .../src/modules/credentials/CredentialsApi.ts | 30 +- .../credentials/CredentialsApiOptions.ts | 16 +- .../modules/credentials/CredentialsModule.ts | 4 +- .../errors/CredentialProblemReportError.ts | 2 +- .../formats/CredentialFormatService.ts | 4 +- .../formats/CredentialFormatServiceOptions.ts | 4 +- .../formats/indy/IndyCredentialFormat.ts | 2 +- .../indy/IndyCredentialFormatService.ts | 2 +- .../jsonld/JsonLdCredentialFormatService.ts | 12 +- .../protocol/BaseCredentialProtocol.ts | 65 +- ...ldproof.connectionless-credentials.test.ts | 4 +- .../repository/CredentialExchangeRecord.ts | 2 +- packages/core/src/modules/dids/DidsModule.ts | 2 +- .../dids/__tests__/dids-registrar.e2e.test.ts | 3 +- .../dids/domain/key-type/bls12381g1.ts | 2 +- .../dids/domain/key-type/bls12381g2.ts | 2 +- .../modules/dids/domain/key-type/ed25519.ts | 2 +- .../modules/dids/domain/key-type/x25519.ts | 2 +- .../src/modules/dids/repository/DidRecord.ts | 2 +- .../discover-features/DiscoverFeaturesApi.ts | 4 +- .../DiscoverFeaturesApiOptions.ts | 2 +- .../DiscoverFeaturesModule.ts | 2 +- .../services/DiscoverFeaturesService.ts | 10 +- .../generic-records/GenericRecordsApi.ts | 2 +- .../indy/services/IndyRevocationService.ts | 2 +- .../core/src/modules/ledger/LedgerModule.ts | 2 +- packages/core/src/modules/oob/OutOfBandApi.ts | 2 +- .../core/src/modules/oob/OutOfBandService.ts | 4 +- .../src/modules/oob/domain/OutOfBandEvents.ts | 2 +- .../core/src/modules/proofs/ProofEvents.ts | 2 +- .../proofs/ProofResponseCoordinator.ts | 2 +- .../core/src/modules/proofs/ProofService.ts | 50 +- packages/core/src/modules/proofs/ProofsApi.ts | 12 +- .../core/src/modules/proofs/ProofsModule.ts | 2 +- .../proofs/__tests__/V1ProofService.test.ts | 2 +- .../proofs/formats/ProofFormatService.ts | 42 +- .../formats/ProofFormatServiceOptions.ts | 4 +- .../proofs/formats/indy/IndyProofFormat.ts | 6 +- .../formats/indy/IndyProofFormatService.ts | 4 +- .../indy/IndyProofFormatsServiceOptions.ts | 6 +- .../models/ProofFormatServiceOptions.ts | 2 +- .../proofs/models/ProofServiceOptions.ts | 4 +- .../modules/proofs/models/SharedOptions.ts | 2 +- .../proofs/protocol/v2/V2ProofService.ts | 2 +- .../core/src/modules/routing/MediatorApi.ts | 2 +- .../src/modules/routing/MediatorModule.ts | 2 +- .../core/src/modules/routing/RecipientApi.ts | 4 +- .../src/modules/routing/RecipientModule.ts | 2 +- .../core/src/modules/routing/RoutingEvents.ts | 4 +- .../pickup/v1/MessagePickupService.ts | 2 +- .../pickup/v2/V2MessagePickupService.ts | 2 +- .../services/MediationRecipientService.ts | 2 +- .../src/modules/vc/W3cCredentialService.ts | 6 +- packages/core/src/modules/vc/W3cVcModule.ts | 2 +- .../vc/__tests__/W3cCredentialService.test.ts | 2 +- packages/core/src/modules/vc/jsonldUtil.ts | 2 +- .../modules/vc/libraries/documentLoader.ts | 2 +- .../vc/models/W3cCredentialServiceOptions.ts | 6 +- .../vc/models/credential/W3cCredential.ts | 2 +- .../credential/W3cVerifiableCredential.ts | 2 +- .../credential/W3cVerifyCredentialResult.ts | 2 +- .../presentation/W3cVerifiablePresentation.ts | 2 +- packages/core/src/plugins/Module.ts | 2 +- .../src/storage/InMemoryMessageRepository.ts | 2 +- .../core/src/storage/IndyStorageService.ts | 7 +- packages/core/src/storage/Repository.ts | 4 +- packages/core/src/storage/RepositoryEvents.ts | 2 +- packages/core/src/storage/StorageService.ts | 2 +- .../storage/didcomm/DidCommMessageRecord.ts | 2 +- .../didcomm/DidCommMessageRepository.ts | 2 +- .../storage/migration/StorageUpdateService.ts | 2 +- .../src/storage/migration/UpdateAssistant.ts | 2 +- .../core/src/storage/migration/updates.ts | 4 +- .../migration/updates/0.1-0.2/mediation.ts | 2 +- .../src/transport/HttpOutboundTransport.ts | 2 +- .../core/src/transport/WsOutboundTransport.ts | 4 +- packages/core/src/types.ts | 1 - packages/core/src/utils/attachment.ts | 2 +- packages/core/src/utils/indyError.ts | 2 +- packages/core/src/utils/messageType.ts | 2 +- packages/core/src/wallet/IndyWallet.ts | 18 +- packages/core/src/wallet/WalletApi.ts | 2 +- packages/core/tests/oob.test.ts | 21 +- packages/indy-sdk/package.json | 6 +- .../src/dids/IndySdkSovDidRegistrar.ts | 2 +- packages/indy-sdk/src/error/indyError.ts | 2 +- packages/node/package.json | 10 +- packages/openid4vc-client/package.json | 6 +- packages/question-answer/package.json | 6 +- .../tests/question-answer.e2e.test.ts | 4 +- packages/react-native/package.json | 6 +- packages/tenants/package.json | 6 +- tests/e2e-subject.test.ts | 3 +- tsconfig.build.json | 4 +- yarn.lock | 1012 ++++++++++------- 139 files changed, 982 insertions(+), 741 deletions(-) diff --git a/package.json b/package.json index 636ee9f284..24f487b9a2 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "test": "jest", "lint": "eslint --ignore-path .gitignore .", "validate": "yarn lint && yarn check-types && yarn check-format", + "prepare": "husky install", "run-mediator": "ts-node ./samples/mediator.ts", "next-version-bump": "ts-node ./scripts/get-next-bump.ts" }, @@ -31,12 +32,12 @@ "@types/eslint": "^7.2.13", "@types/express": "^4.17.13", "@types/jest": "^26.0.23", - "@types/node": "^15.14.4", + "@types/node": "^16.11.7", "@types/uuid": "^8.3.1", "@types/varint": "^6.0.0", "@types/ws": "^7.4.6", - "@typescript-eslint/eslint-plugin": "^4.26.1", - "@typescript-eslint/parser": "^4.26.1", + "@typescript-eslint/eslint-plugin": "^5.48.1", + "@typescript-eslint/parser": "^5.48.1", "conventional-changelog-conventionalcommits": "^5.0.0", "conventional-recommended-bump": "^6.1.0", "cors": "^2.8.5", @@ -47,6 +48,7 @@ "eslint-plugin-import": "^2.23.4", "eslint-plugin-prettier": "^3.4.0", "express": "^4.17.1", + "husky": "^7.0.1", "indy-sdk": "^1.16.0-dev-1636", "jest": "^27.0.4", "lerna": "^4.0.0", @@ -54,13 +56,13 @@ "rxjs": "^7.2.0", "ts-jest": "^27.0.3", "ts-node": "^10.0.0", - "tsconfig-paths": "^3.9.0", + "tsconfig-paths": "^4.1.2", "tsyringe": "^4.7.0", - "typescript": "~4.3.0", + "typescript": "~4.9.4", "ws": "^7.4.6" }, "resolutions": { - "@types/node": "^15.14.4" + "@types/node": "^16.11.7" }, "engines": { "node": ">= 14" diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 7537700f4c..e4b54a744a 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -18,7 +18,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -31,7 +31,7 @@ }, "devDependencies": { "reflect-metadata": "^0.1.13", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/packages/action-menu/src/services/ActionMenuService.ts b/packages/action-menu/src/services/ActionMenuService.ts index 89c27f54a4..8282bc60bf 100644 --- a/packages/action-menu/src/services/ActionMenuService.ts +++ b/packages/action-menu/src/services/ActionMenuService.ts @@ -1,5 +1,3 @@ -import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' -import type { ActionMenuProblemReportMessage } from '../messages' import type { ClearMenuOptions, CreateMenuOptions, @@ -7,6 +5,8 @@ import type { CreateRequestOptions, FindMenuOptions, } from './ActionMenuServiceOptions' +import type { ActionMenuStateChangedEvent } from '../ActionMenuEvents' +import type { ActionMenuProblemReportMessage } from '../messages' import type { AgentContext, InboundMessageContext, Logger, Query } from '@aries-framework/core' import { AgentConfig, EventEmitter, AriesFrameworkError, injectable, JsonTransformer } from '@aries-framework/core' diff --git a/packages/action-menu/tests/action-menu.e2e.test.ts b/packages/action-menu/tests/action-menu.e2e.test.ts index b15524fd93..553d7e0c20 100644 --- a/packages/action-menu/tests/action-menu.e2e.test.ts +++ b/packages/action-menu/tests/action-menu.e2e.test.ts @@ -9,8 +9,6 @@ import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutbou import { getAgentOptions, makeConnection } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' -import { waitForActionMenuRecord } from './helpers' - import { ActionMenu, ActionMenuModule, @@ -19,6 +17,8 @@ import { ActionMenuState, } from '@aries-framework/action-menu' +import { waitForActionMenuRecord } from './helpers' + const faberAgentOptions = getAgentOptions( 'Faber Action Menu', { diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 25033d94a2..473c115b01 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -19,7 +19,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -28,7 +28,7 @@ "@aries-framework/core": "0.3.2" }, "devDependencies": { - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts index 3e287bde00..4991dbca1f 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderService.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -1,5 +1,3 @@ -import type { CredentialInfo } from '../models' -import type { AnonCredsProof } from '../models/exchange' import type { CreateCredentialRequestOptions, CreateCredentialRequestReturn, @@ -9,6 +7,8 @@ import type { GetCredentialsForProofRequestOptions, GetCredentialsForProofRequestReturn, } from './AnonCredsHolderServiceOptions' +import type { CredentialInfo } from '../models' +import type { AnonCredsProof } from '../models/exchange' import type { AgentContext } from '@aries-framework/core' export interface AnonCredsHolderService { diff --git a/packages/anoncreds/src/services/AnonCredsIssuerService.ts b/packages/anoncreds/src/services/AnonCredsIssuerService.ts index f3fb8c128f..0f34d300ef 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerService.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerService.ts @@ -1,5 +1,3 @@ -import type { AnonCredsCredentialOffer } from '../models/exchange' -import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../models/registry' import type { CreateSchemaOptions, CreateCredentialDefinitionOptions, @@ -7,6 +5,8 @@ import type { CreateCredentialReturn, CreateCredentialOptions, } from './AnonCredsIssuerServiceOptions' +import type { AnonCredsCredentialOffer } from '../models/exchange' +import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../models/registry' import type { AgentContext } from '@aries-framework/core' export interface AnonCredsIssuerService { diff --git a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts index e2f7e14298..142e784405 100644 --- a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts @@ -1,4 +1,3 @@ -import type { AnonCredsCredentialDefinition } from '../../models/registry' import type { AnonCredsResolutionMetadata, Extensible, @@ -6,6 +5,7 @@ import type { AnonCredsOperationStateFinished, AnonCredsOperationState, } from './base' +import type { AnonCredsCredentialDefinition } from '../../models/registry' export interface GetCredentialDefinitionReturn { credentialDefinition: AnonCredsCredentialDefinition | null diff --git a/packages/anoncreds/src/services/registry/RevocationListOptions.ts b/packages/anoncreds/src/services/registry/RevocationListOptions.ts index aea0c5d5b9..b6f0edea42 100644 --- a/packages/anoncreds/src/services/registry/RevocationListOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationListOptions.ts @@ -1,5 +1,5 @@ -import type { AnonCredsRevocationList } from '../../models/registry' import type { AnonCredsResolutionMetadata, Extensible } from './base' +import type { AnonCredsRevocationList } from '../../models/registry' export interface GetRevocationListReturn { revocationList: AnonCredsRevocationList | null diff --git a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts index 5e63f79995..6d45377114 100644 --- a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts @@ -1,5 +1,5 @@ -import type { AnonCredsRevocationRegistryDefinition } from '../../models/registry' import type { AnonCredsResolutionMetadata, Extensible } from './base' +import type { AnonCredsRevocationRegistryDefinition } from '../../models/registry' export interface GetRevocationRegistryDefinitionReturn { revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition | null diff --git a/packages/anoncreds/src/services/registry/SchemaOptions.ts b/packages/anoncreds/src/services/registry/SchemaOptions.ts index f4d706223a..c436859060 100644 --- a/packages/anoncreds/src/services/registry/SchemaOptions.ts +++ b/packages/anoncreds/src/services/registry/SchemaOptions.ts @@ -1,4 +1,3 @@ -import type { AnonCredsSchema } from '../../models/registry' import type { AnonCredsResolutionMetadata, Extensible, @@ -6,6 +5,7 @@ import type { AnonCredsOperationStateFinished, AnonCredsOperationState, } from './base' +import type { AnonCredsSchema } from '../../models/registry' // Get Schema export interface GetSchemaReturn { diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 353048460f..96f1e6cdde 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -19,7 +19,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -36,8 +36,8 @@ "devDependencies": { "@aries-framework/node": "*", "reflect-metadata": "^0.1.13", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" }, "peerDependenciesMeta": { "@animo-id/react-native-bbs-signatures": { diff --git a/packages/core/package.json b/packages/core/package.json index cd0e119a36..971091140e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -18,7 +18,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build" }, @@ -60,8 +60,8 @@ "@types/uuid": "^8.3.0", "@types/varint": "^6.0.0", "node-fetch": "^2.0", - "rimraf": "~3.0.2", + "rimraf": "^4.0.7", "tslog": "^3.2.0", - "typescript": "~4.3.0" + "typescript": "~4.9.4" } } diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index c4c5609387..a3a6b11ab1 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -1,9 +1,9 @@ -import type { InboundTransport } from '../transport/InboundTransport' -import type { OutboundTransport } from '../transport/OutboundTransport' -import type { InitConfig } from '../types' import type { AgentDependencies } from './AgentDependencies' import type { AgentModulesInput } from './AgentModules' import type { AgentMessageReceivedEvent } from './Events' +import type { InboundTransport } from '../transport/InboundTransport' +import type { OutboundTransport } from '../transport/OutboundTransport' +import type { InitConfig } from '../types' import type { Subscription } from 'rxjs' import { Subject } from 'rxjs' diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index cceef0e271..28ad67488a 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -1,6 +1,6 @@ +import type { AgentDependencies } from './AgentDependencies' import type { Logger } from '../logger' import type { InitConfig } from '../types' -import type { AgentDependencies } from './AgentDependencies' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' import { AriesFrameworkError } from '../error' diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index ce59b48daa..4bb5cc4067 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -1,7 +1,7 @@ +import type { AgentConfig } from './AgentConfig' import type { Module, DependencyManager, ApiModule } from '../plugins' import type { IsAny } from '../types' import type { Constructor } from '../utils/mixins' -import type { AgentConfig } from './AgentConfig' import { BasicMessagesModule } from '../modules/basic-messages' import { ConnectionsModule } from '../modules/connections' diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index b0abda3209..606b586a67 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -1,9 +1,9 @@ -import type { Logger } from '../logger' -import type { CredentialsModule } from '../modules/credentials' -import type { DependencyManager } from '../plugins' import type { AgentConfig } from './AgentConfig' import type { AgentApi, EmptyModuleMap, ModulesMap, WithoutDefaultModules, CustomOrDefaultApi } from './AgentModules' import type { TransportSession } from './TransportService' +import type { Logger } from '../logger' +import type { CredentialsModule } from '../modules/credentials' +import type { DependencyManager } from '../plugins' import { AriesFrameworkError } from '../error' import { BasicMessagesApi } from '../modules/basic-messages' diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index cd50b22fc0..df72e3d983 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -1,6 +1,6 @@ -import type { EncryptedMessage, PlaintextMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { AgentContext } from './context' +import type { EncryptedMessage, PlaintextMessage } from '../types' import { InjectionSymbols } from '../constants' import { Key, KeyType } from '../crypto' diff --git a/packages/core/src/agent/Events.ts b/packages/core/src/agent/Events.ts index 4e7bb4b076..0eecc21fe4 100644 --- a/packages/core/src/agent/Events.ts +++ b/packages/core/src/agent/Events.ts @@ -1,6 +1,6 @@ -import type { ConnectionRecord } from '../modules/connections' import type { AgentMessage } from './AgentMessage' import type { OutboundMessageContext, OutboundMessageSendStatus } from './models' +import type { ConnectionRecord } from '../modules/connections' import type { Observable } from 'rxjs' import { filter } from 'rxjs' diff --git a/packages/core/src/agent/MessageHandlerRegistry.ts b/packages/core/src/agent/MessageHandlerRegistry.ts index 3942c43c55..574a9331f3 100644 --- a/packages/core/src/agent/MessageHandlerRegistry.ts +++ b/packages/core/src/agent/MessageHandlerRegistry.ts @@ -39,7 +39,7 @@ export class MessageHandlerRegistry { */ public get supportedMessageTypes() { return this.messageHandlers - .reduce((all, cur) => [...all, ...cur.supportedMessages], []) + .reduce<(typeof AgentMessage)[]>((all, cur) => [...all, ...cur.supportedMessages], []) .map((m) => m.type) } diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index a87b42b7e2..befabec616 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -1,10 +1,10 @@ -import type { ConnectionRecord } from '../modules/connections' -import type { InboundTransport } from '../transport' -import type { EncryptedMessage, PlaintextMessage } from '../types' import type { AgentMessage } from './AgentMessage' import type { DecryptedMessageContext } from './EnvelopeService' import type { TransportSession } from './TransportService' import type { AgentContext } from './context' +import type { ConnectionRecord } from '../modules/connections' +import type { InboundTransport } from '../transport' +import type { EncryptedMessage, PlaintextMessage } from '../types' import { InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error' diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index ac9ac731a2..04aad34d38 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -1,14 +1,14 @@ +import type { AgentMessage } from './AgentMessage' +import type { EnvelopeKeys } from './EnvelopeService' +import type { AgentMessageSentEvent } from './Events' +import type { TransportSession } from './TransportService' +import type { AgentContext } from './context' import type { ConnectionRecord } from '../modules/connections' import type { ResolvedDidCommService } from '../modules/didcomm' import type { DidDocument } from '../modules/dids' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundTransport } from '../transport/OutboundTransport' import type { OutboundPackage, EncryptedMessage } from '../types' -import type { AgentMessage } from './AgentMessage' -import type { EnvelopeKeys } from './EnvelopeService' -import type { AgentMessageSentEvent } from './Events' -import type { TransportSession } from './TransportService' -import type { AgentContext } from './context' import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index 55a9708ae0..0eda25500c 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -1,8 +1,8 @@ +import type { AgentMessage } from './AgentMessage' +import type { EnvelopeKeys } from './EnvelopeService' import type { ConnectionRecord } from '../modules/connections/repository' import type { DidDocument } from '../modules/dids' import type { EncryptedMessage } from '../types' -import type { AgentMessage } from './AgentMessage' -import type { EnvelopeKeys } from './EnvelopeService' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' import { injectable } from '../plugins' diff --git a/packages/core/src/agent/context/AgentContext.ts b/packages/core/src/agent/context/AgentContext.ts index 714a5933a5..73a857d518 100644 --- a/packages/core/src/agent/context/AgentContext.ts +++ b/packages/core/src/agent/context/AgentContext.ts @@ -1,6 +1,6 @@ +import type { AgentContextProvider } from './AgentContextProvider' import type { DependencyManager } from '../../plugins' import type { Wallet } from '../../wallet' -import type { AgentContextProvider } from './AgentContextProvider' import { InjectionSymbols } from '../../constants' import { AgentConfig } from '../AgentConfig' diff --git a/packages/core/src/cache/PersistedLruCache.ts b/packages/core/src/cache/PersistedLruCache.ts index ab00e0d14e..bb94c41bee 100644 --- a/packages/core/src/cache/PersistedLruCache.ts +++ b/packages/core/src/cache/PersistedLruCache.ts @@ -1,5 +1,5 @@ -import type { AgentContext } from '../agent' import type { CacheRepository } from './CacheRepository' +import type { AgentContext } from '../agent' import { LRUMap } from 'lru_map' diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index 29a7f390e0..ffad03c128 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -1,6 +1,6 @@ +import type { Jws, JwsGeneralFormat } from './JwsTypes' import type { AgentContext } from '../agent' import type { Buffer } from '../utils' -import type { Jws, JwsGeneralFormat } from './JwsTypes' import { AriesFrameworkError } from '../error' import { injectable } from '../plugins' diff --git a/packages/core/src/crypto/WalletKeyPair.ts b/packages/core/src/crypto/WalletKeyPair.ts index f5008112db..7df8cbb6ad 100644 --- a/packages/core/src/crypto/WalletKeyPair.ts +++ b/packages/core/src/crypto/WalletKeyPair.ts @@ -1,6 +1,6 @@ +import type { Key } from './Key' import type { LdKeyPairOptions } from '../modules/vc/models/LdKeyPair' import type { Wallet } from '../wallet' -import type { Key } from './Key' import { VerificationMethod } from '../modules/dids' import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type/keyDidMapping' diff --git a/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts b/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts index 8a33483d2d..db71348ef6 100644 --- a/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts +++ b/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts @@ -1,5 +1,5 @@ -import type { KeyType } from '..' import type { SigningProvider } from './SigningProvider' +import type { KeyType } from '../KeyType' import { AriesFrameworkError } from '../../error' import { injectable, injectAll } from '../../plugins' diff --git a/packages/core/src/decorators/service/ServiceDecoratorExtension.ts b/packages/core/src/decorators/service/ServiceDecoratorExtension.ts index ecf2f9a044..776e8f702f 100644 --- a/packages/core/src/decorators/service/ServiceDecoratorExtension.ts +++ b/packages/core/src/decorators/service/ServiceDecoratorExtension.ts @@ -1,5 +1,5 @@ -import type { BaseMessageConstructor } from '../../agent/BaseMessage' import type { ServiceDecoratorOptions } from './ServiceDecorator' +import type { BaseMessageConstructor } from '../../agent/BaseMessage' import { Expose, Type } from 'class-transformer' import { IsOptional, ValidateNested } from 'class-validator' diff --git a/packages/core/src/modules/basic-messages/BasicMessageEvents.ts b/packages/core/src/modules/basic-messages/BasicMessageEvents.ts index 7c25f5b9c4..f05873f5de 100644 --- a/packages/core/src/modules/basic-messages/BasicMessageEvents.ts +++ b/packages/core/src/modules/basic-messages/BasicMessageEvents.ts @@ -1,6 +1,6 @@ -import type { BaseEvent } from '../../agent/Events' import type { BasicMessage } from './messages' import type { BasicMessageRecord } from './repository' +import type { BaseEvent } from '../../agent/Events' export enum BasicMessageEventTypes { BasicMessageStateChanged = 'BasicMessageStateChanged', diff --git a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts index 816340429d..938f6b8407 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts @@ -1,5 +1,5 @@ -import type { Query } from '../../storage/StorageService' import type { BasicMessageRecord } from './repository/BasicMessageRecord' +import type { Query } from '../../storage/StorageService' import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' diff --git a/packages/core/src/modules/connections/ConnectionEvents.ts b/packages/core/src/modules/connections/ConnectionEvents.ts index 327a897dda..c9f1064bab 100644 --- a/packages/core/src/modules/connections/ConnectionEvents.ts +++ b/packages/core/src/modules/connections/ConnectionEvents.ts @@ -1,6 +1,6 @@ -import type { BaseEvent } from '../../agent/Events' import type { DidExchangeState } from './models' import type { ConnectionRecord } from './repository/ConnectionRecord' +import type { BaseEvent } from '../../agent/Events' export enum ConnectionEventTypes { ConnectionStateChanged = 'ConnectionStateChanged', diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index da895de772..9777bc903a 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -1,8 +1,8 @@ -import type { Query } from '../../storage/StorageService' -import type { OutOfBandRecord } from '../oob/repository' import type { ConnectionType } from './models' import type { ConnectionRecord } from './repository/ConnectionRecord' import type { Routing } from './services' +import type { Query } from '../../storage/StorageService' +import type { OutOfBandRecord } from '../oob/repository' import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index b589f202ce..537f7695a7 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -1,6 +1,6 @@ +import type { ConnectionsModuleConfigOptions } from './ConnectionsModuleConfig' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' -import type { ConnectionsModuleConfigOptions } from './ConnectionsModuleConfig' import { Protocol } from '../../agent/models' diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 23965bbebc..c577a260f7 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -1,11 +1,11 @@ +import type { ConnectionRecord } from './repository' +import type { Routing } from './services/ConnectionService' import type { AgentContext } from '../../agent' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { ParsedMessageType } from '../../utils/messageType' import type { ResolvedDidCommService } from '../didcomm' import type { PeerDidCreateOptions } from '../dids' import type { OutOfBandRecord } from '../oob/repository' -import type { ConnectionRecord } from './repository' -import type { Routing } from './services/ConnectionService' import { InjectionSymbols } from '../../constants' import { Key, KeyType } from '../../crypto' diff --git a/packages/core/src/modules/connections/DidExchangeStateMachine.ts b/packages/core/src/modules/connections/DidExchangeStateMachine.ts index 3f32f4e48d..be73793ab3 100644 --- a/packages/core/src/modules/connections/DidExchangeStateMachine.ts +++ b/packages/core/src/modules/connections/DidExchangeStateMachine.ts @@ -1,5 +1,5 @@ -import type { ParsedMessageType } from '../../utils/messageType' import type { ConnectionRecord } from './repository' +import type { ParsedMessageType } from '../../utils/messageType' import { AriesFrameworkError } from '../../error' import { canHandleMessageType } from '../../utils/messageType' diff --git a/packages/core/src/modules/connections/TrustPingEvents.ts b/packages/core/src/modules/connections/TrustPingEvents.ts index 55200e6c5b..2b0fcf66c9 100644 --- a/packages/core/src/modules/connections/TrustPingEvents.ts +++ b/packages/core/src/modules/connections/TrustPingEvents.ts @@ -1,6 +1,6 @@ -import type { BaseEvent } from '../../agent/Events' import type { TrustPingMessage, TrustPingResponseMessage } from './messages' import type { ConnectionRecord } from './repository/ConnectionRecord' +import type { BaseEvent } from '../../agent/Events' export enum TrustPingEventTypes { TrustPingReceivedEvent = 'TrustPingReceivedEvent', diff --git a/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts b/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts index 764be043f9..f96f98261d 100644 --- a/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts +++ b/packages/core/src/modules/connections/errors/ConnectionProblemReportError.ts @@ -1,5 +1,5 @@ -import type { ProblemReportErrorOptions } from '../../problem-reports' import type { ConnectionProblemReportReason } from './ConnectionProblemReportReason' +import type { ProblemReportErrorOptions } from '../../problem-reports' import { ProblemReportError } from '../../problem-reports' import { ConnectionProblemReportMessage } from '../messages' diff --git a/packages/core/src/modules/connections/errors/DidExchangeProblemReportError.ts b/packages/core/src/modules/connections/errors/DidExchangeProblemReportError.ts index 17bf72ad9b..6e8d9a9925 100644 --- a/packages/core/src/modules/connections/errors/DidExchangeProblemReportError.ts +++ b/packages/core/src/modules/connections/errors/DidExchangeProblemReportError.ts @@ -1,5 +1,5 @@ -import type { ProblemReportErrorOptions } from '../../problem-reports' import type { DidExchangeProblemReportReason } from './DidExchangeProblemReportReason' +import type { ProblemReportErrorOptions } from '../../problem-reports' import { ProblemReportError } from '../../problem-reports' import { DidExchangeProblemReportMessage } from '../messages' diff --git a/packages/core/src/modules/connections/models/did/DidDoc.ts b/packages/core/src/modules/connections/models/did/DidDoc.ts index 896d314221..4d86fa7e19 100644 --- a/packages/core/src/modules/connections/models/did/DidDoc.ts +++ b/packages/core/src/modules/connections/models/did/DidDoc.ts @@ -1,6 +1,6 @@ -import type { DidDocumentService } from '../../../dids/domain/service' import type { Authentication } from './authentication' import type { PublicKey } from './publicKey' +import type { DidDocumentService } from '../../../dids/domain/service' import { Expose } from 'class-transformer' import { Equals, IsArray, IsString, ValidateNested } from 'class-validator' diff --git a/packages/core/src/modules/connections/models/did/authentication/Authentication.ts b/packages/core/src/modules/connections/models/did/authentication/Authentication.ts index 76c2cef823..41ff0bc5d5 100644 --- a/packages/core/src/modules/connections/models/did/authentication/Authentication.ts +++ b/packages/core/src/modules/connections/models/did/authentication/Authentication.ts @@ -1,5 +1,5 @@ import type { PublicKey } from '../publicKey/PublicKey' export abstract class Authentication { - abstract publicKey: PublicKey + public abstract publicKey: PublicKey } diff --git a/packages/core/src/modules/connections/repository/ConnectionRecord.ts b/packages/core/src/modules/connections/repository/ConnectionRecord.ts index 382b0f0eac..f05106e5c9 100644 --- a/packages/core/src/modules/connections/repository/ConnectionRecord.ts +++ b/packages/core/src/modules/connections/repository/ConnectionRecord.ts @@ -1,7 +1,7 @@ +import type { ConnectionMetadata } from './ConnectionMetadataTypes' import type { TagsBase } from '../../../storage/BaseRecord' import type { HandshakeProtocol } from '../models' import type { ConnectionType } from '../models/ConnectionType' -import type { ConnectionMetadata } from './ConnectionMetadataTypes' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' diff --git a/packages/core/src/modules/credentials/CredentialEvents.ts b/packages/core/src/modules/credentials/CredentialEvents.ts index d9324d2bfe..2a60193d51 100644 --- a/packages/core/src/modules/credentials/CredentialEvents.ts +++ b/packages/core/src/modules/credentials/CredentialEvents.ts @@ -1,6 +1,6 @@ -import type { BaseEvent } from '../../agent/Events' import type { CredentialState } from './models/CredentialState' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' +import type { BaseEvent } from '../../agent/Events' export enum CredentialEventTypes { CredentialStateChanged = 'CredentialStateChanged', diff --git a/packages/core/src/modules/credentials/CredentialProtocolOptions.ts b/packages/core/src/modules/credentials/CredentialProtocolOptions.ts index a26a0e6861..5b934bdb0c 100644 --- a/packages/core/src/modules/credentials/CredentialProtocolOptions.ts +++ b/packages/core/src/modules/credentials/CredentialProtocolOptions.ts @@ -1,6 +1,3 @@ -import type { AgentMessage } from '../../agent/AgentMessage' -import type { FlatArray } from '../../types' -import type { ConnectionRecord } from '../connections/repository/ConnectionRecord' import type { CredentialFormat, CredentialFormatPayload, @@ -11,6 +8,8 @@ import type { CredentialPreviewAttributeOptions } from './models' import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' import type { CredentialProtocol } from './protocol/CredentialProtocol' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' +import type { AgentMessage } from '../../agent/AgentMessage' +import type { ConnectionRecord } from '../connections/repository/ConnectionRecord' /** * Get the format data payload for a specific message from a list of CredentialFormat interfaces and a message @@ -42,14 +41,15 @@ export type FormatDataMessagePayload< } /** - * Infer an array of {@link CredentialFormatService} types based on a {@link CredentialProtocol}. + * Infer the {@link CredentialFormat} types based on an array of {@link CredentialProtocol} types. * - * It does this by extracting the `CredentialFormatServices` generic from the `CredentialProtocol`. + * It does this by extracting the `CredentialFormatServices` generic from the `CredentialProtocol`, and + * then extracting the `CredentialFormat` generic from each of the `CredentialFormatService` types. * * @example * ``` * // TheCredentialFormatServices is now equal to [IndyCredentialFormatService] - * type TheCredentialFormatServices = ExtractCredentialFormatServices + * type TheCredentialFormatServices = CredentialFormatsFromProtocols<[V1CredentialProtocol]> * ``` * * Because the `V1CredentialProtocol` is defined as follows: @@ -58,27 +58,14 @@ export type FormatDataMessagePayload< * } * ``` */ -export type ExtractCredentialFormatServices = Type extends CredentialProtocol - ? CredentialFormatServices +export type CredentialFormatsFromProtocols = Type[number] extends CredentialProtocol< + infer CredentialFormatServices +> + ? CredentialFormatServices extends CredentialFormatService[] + ? ExtractCredentialFormats + : never : never -/** - * Infer an array of {@link CredentialFormat} types based on an array of {@link CredentialProtocol} types. - * - * This is based on {@link ExtractCredentialFormatServices}, but allows to handle arrays. - */ -export type CFsFromCPs = _CFsFromCPs extends CredentialFormat[] - ? _CFsFromCPs - : [] - -/** - * Utility type for {@link ExtractCredentialFormatServicesFromCredentialProtocols} to reduce duplication. - * Should not be used directly. - */ -type _CFsFromCPs = FlatArray<{ - [CP in keyof CPs]: ExtractCredentialFormats> -}>[] - /** * Get format data return value. Each key holds a mapping of credential format key to format data. * diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index 10520e4c2e..e69fe6c6a5 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -1,6 +1,4 @@ -import type { AgentMessage } from '../../agent/AgentMessage' -import type { Query } from '../../storage/StorageService' -import type { CFsFromCPs, DeleteCredentialOptions } from './CredentialProtocolOptions' +import type { CredentialFormatsFromProtocols, DeleteCredentialOptions } from './CredentialProtocolOptions' import type { AcceptCredentialOptions, AcceptCredentialOfferOptions, @@ -21,6 +19,8 @@ import type { } from './CredentialsApiOptions' import type { CredentialProtocol } from './protocol/CredentialProtocol' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' +import type { AgentMessage } from '../../agent/AgentMessage' +import type { Query } from '../../storage/StorageService' import { AgentContext } from '../../agent' import { MessageSender } from '../../agent/MessageSender' @@ -77,7 +77,7 @@ export interface CredentialsApi { findById(credentialRecordId: string): Promise deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise update(credentialRecord: CredentialExchangeRecord): Promise - getFormatData(credentialRecordId: string): Promise>> + getFormatData(credentialRecordId: string): Promise>> // DidComm Message Records findProposalMessage(credentialExchangeId: string): Promise> @@ -134,12 +134,12 @@ export class CredentialsApi implements Credent ) as CredentialProtocolMap } - private getProtocol>(protocolVersion: PVT) { + private getProtocol>(protocolVersion: PVT): CredentialProtocol { if (!this.credentialProtocolMap[protocolVersion]) { throw new AriesFrameworkError(`No credential protocol registered for protocol version ${protocolVersion}`) } - return this.credentialProtocolMap[protocolVersion] + return this.credentialProtocolMap[protocolVersion] as CredentialProtocol } /** @@ -593,7 +593,9 @@ export class CredentialsApi implements Credent return credentialRecord } - public async getFormatData(credentialRecordId: string): Promise>> { + public async getFormatData( + credentialRecordId: string + ): Promise>> { const credentialRecord = await this.getById(credentialRecordId) const service = this.getProtocol(credentialRecord.protocolVersion) @@ -664,25 +666,31 @@ export class CredentialsApi implements Credent public async findProposalMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findProposalMessage(this.agentContext, credentialExchangeId) + return service.findProposalMessage( + this.agentContext, + credentialExchangeId + ) as FindCredentialProposalMessageReturn } public async findOfferMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findOfferMessage(this.agentContext, credentialExchangeId) + return service.findOfferMessage(this.agentContext, credentialExchangeId) as FindCredentialOfferMessageReturn } public async findRequestMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findRequestMessage(this.agentContext, credentialExchangeId) + return service.findRequestMessage( + this.agentContext, + credentialExchangeId + ) as FindCredentialRequestMessageReturn } public async findCredentialMessage(credentialExchangeId: string): Promise> { const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findCredentialMessage(this.agentContext, credentialExchangeId) + return service.findCredentialMessage(this.agentContext, credentialExchangeId) as FindCredentialMessageReturn } private async getServiceForCredentialExchangeId(credentialExchangeId: string) { diff --git a/packages/core/src/modules/credentials/CredentialsApiOptions.ts b/packages/core/src/modules/credentials/CredentialsApiOptions.ts index 7eb6c7488f..24fb0a86d1 100644 --- a/packages/core/src/modules/credentials/CredentialsApiOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsApiOptions.ts @@ -1,4 +1,4 @@ -import type { CFsFromCPs, GetFormatDataReturn } from './CredentialProtocolOptions' +import type { CredentialFormatsFromProtocols, GetFormatDataReturn } from './CredentialProtocolOptions' import type { CredentialFormatPayload } from './formats' import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' import type { CredentialProtocol } from './protocol/CredentialProtocol' @@ -53,7 +53,7 @@ interface BaseOptions { export interface ProposeCredentialOptions extends BaseOptions { connectionId: string protocolVersion: CredentialProtocolVersionType - credentialFormats: CredentialFormatPayload, 'createProposal'> + credentialFormats: CredentialFormatPayload, 'createProposal'> } /** @@ -64,7 +64,7 @@ export interface ProposeCredentialOptions extends BaseOptions { credentialRecordId: string - credentialFormats?: CredentialFormatPayload, 'acceptProposal'> + credentialFormats?: CredentialFormatPayload, 'acceptProposal'> } /** @@ -73,7 +73,7 @@ export interface AcceptCredentialProposalOptions extends BaseOptions { credentialRecordId: string - credentialFormats: CredentialFormatPayload, 'createOffer'> + credentialFormats: CredentialFormatPayload, 'createOffer'> } /** @@ -81,7 +81,7 @@ export interface NegotiateCredentialProposalOptions extends BaseOptions { protocolVersion: CredentialProtocolVersionType - credentialFormats: CredentialFormatPayload, 'createOffer'> + credentialFormats: CredentialFormatPayload, 'createOffer'> } /** @@ -101,7 +101,7 @@ export interface OfferCredentialOptions extends BaseOptions { credentialRecordId: string - credentialFormats?: CredentialFormatPayload, 'acceptOffer'> + credentialFormats?: CredentialFormatPayload, 'acceptOffer'> } /** @@ -110,7 +110,7 @@ export interface AcceptCredentialOfferOptions extends BaseOptions { credentialRecordId: string - credentialFormats: CredentialFormatPayload, 'createProposal'> + credentialFormats: CredentialFormatPayload, 'createProposal'> } /** @@ -121,7 +121,7 @@ export interface NegotiateCredentialOfferOptions extends BaseOptions { credentialRecordId: string - credentialFormats?: CredentialFormatPayload, 'acceptRequest'> + credentialFormats?: CredentialFormatPayload, 'acceptRequest'> autoAcceptCredential?: AutoAcceptCredential comment?: string } diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index a581f5e551..b141f63f8a 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -1,9 +1,9 @@ +import type { CredentialsModuleConfigOptions } from './CredentialsModuleConfig' +import type { CredentialProtocol } from './protocol/CredentialProtocol' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { ApiModule, DependencyManager } from '../../plugins' import type { Constructor } from '../../utils/mixins' import type { Optional } from '../../utils/type' -import type { CredentialsModuleConfigOptions } from './CredentialsModuleConfig' -import type { CredentialProtocol } from './protocol/CredentialProtocol' import { Protocol } from '../../agent/models' diff --git a/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts b/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts index ffbe633004..41a5ed808b 100644 --- a/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts +++ b/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts @@ -1,5 +1,5 @@ -import type { ProblemReportErrorOptions } from '../../problem-reports' import type { CredentialProblemReportReason } from './CredentialProblemReportReason' +import type { ProblemReportErrorOptions } from '../../problem-reports' import { V1CredentialProblemReportMessage } from '../protocol/v1/messages' diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts index 4e8e1e4102..20d98623d3 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -1,5 +1,3 @@ -import type { AgentContext } from '../../../agent' -import type { Attachment } from '../../../decorators/attachment/Attachment' import type { CredentialFormat } from './CredentialFormat' import type { FormatCreateProposalOptions, @@ -18,6 +16,8 @@ import type { FormatAutoRespondRequestOptions, FormatProcessCredentialOptions, } from './CredentialFormatServiceOptions' +import type { AgentContext } from '../../../agent' +import type { Attachment } from '../../../decorators/attachment/Attachment' export interface CredentialFormatService { formatKey: CF['formatKey'] diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts index eb2430498f..2f494da4ae 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts @@ -1,9 +1,9 @@ +import type { CredentialFormat, CredentialFormatPayload } from './CredentialFormat' +import type { CredentialFormatService } from './CredentialFormatService' import type { Attachment } from '../../../decorators/attachment/Attachment' import type { CredentialFormatSpec } from '../models/CredentialFormatSpec' import type { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' import type { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' -import type { CredentialFormat, CredentialFormatPayload } from './CredentialFormat' -import type { CredentialFormatService } from './CredentialFormatService' /** * Infer the {@link CredentialFormat} based on a {@link CredentialFormatService}. diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts index 9f15c1152f..eeee56e5d9 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts @@ -1,7 +1,7 @@ +import type { IndyCredProposeOptions } from './models/IndyCredPropose' import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' import type { CredentialPreviewAttributeOptions } from '../../models' import type { CredentialFormat } from '../CredentialFormat' -import type { IndyCredProposeOptions } from './models/IndyCredPropose' import type { Cred, CredOffer, CredReq } from 'indy-sdk' /** diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index cf14a6c4db..aca8aec43a 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -1,3 +1,4 @@ +import type { IndyCredentialFormat } from './IndyCredentialFormat' import type { AgentContext } from '../../../../agent' import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' import type { CredentialPreviewAttributeOptions } from '../../models/CredentialPreviewAttribute' @@ -19,7 +20,6 @@ import type { FormatProcessOptions, FormatProcessCredentialOptions, } from '../CredentialFormatServiceOptions' -import type { IndyCredentialFormat } from './IndyCredentialFormat' import type * as Indy from 'indy-sdk' import { KeyType } from '../../../../crypto' diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index a5df1d86e6..36f88eb4db 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -1,3 +1,9 @@ +import type { + JsonLdCredentialFormat, + JsonCredential, + JsonLdFormatDataCredentialDetail, + JsonLdFormatDataVerifiableCredential, +} from './JsonLdCredentialFormat' import type { AgentContext } from '../../../../agent' import type { CredentialFormatService } from '../CredentialFormatService' import type { @@ -17,12 +23,6 @@ import type { FormatProcessOptions, FormatAutoRespondCredentialOptions, } from '../CredentialFormatServiceOptions' -import type { - JsonLdCredentialFormat, - JsonCredential, - JsonLdFormatDataCredentialDetail, - JsonLdFormatDataVerifiableCredential, -} from './JsonLdCredentialFormat' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' diff --git a/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts index d6a7682c8b..d4a10f4ebc 100644 --- a/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/BaseCredentialProtocol.ts @@ -1,3 +1,4 @@ +import type { CredentialProtocol } from './CredentialProtocol' import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' @@ -23,7 +24,6 @@ import type { } from '../CredentialProtocolOptions' import type { CredentialFormatService, ExtractCredentialFormats } from '../formats' import type { CredentialExchangeRecord } from '../repository' -import type { CredentialProtocol } from './CredentialProtocol' import { EventEmitter } from '../../../agent/EventEmitter' import { DidCommMessageRepository } from '../../../storage' @@ -39,74 +39,93 @@ import { CredentialRepository } from '../repository' export abstract class BaseCredentialProtocol implements CredentialProtocol { - abstract readonly version: string + public abstract readonly version: string protected abstract getFormatServiceForRecordType(credentialRecordType: string): CFs[number] // methods for proposal - abstract createProposal( + public abstract createProposal( agentContext: AgentContext, options: CreateProposalOptions ): Promise> - abstract processProposal(messageContext: InboundMessageContext): Promise - abstract acceptProposal( + public abstract processProposal( + messageContext: InboundMessageContext + ): Promise + public abstract acceptProposal( agentContext: AgentContext, options: AcceptProposalOptions ): Promise> - abstract negotiateProposal( + public abstract negotiateProposal( agentContext: AgentContext, options: NegotiateProposalOptions ): Promise> // methods for offer - abstract createOffer( + public abstract createOffer( agentContext: AgentContext, options: CreateOfferOptions ): Promise> - abstract processOffer(messageContext: InboundMessageContext): Promise - abstract acceptOffer( + public abstract processOffer(messageContext: InboundMessageContext): Promise + public abstract acceptOffer( agentContext: AgentContext, options: AcceptOfferOptions ): Promise> - abstract negotiateOffer( + public abstract negotiateOffer( agentContext: AgentContext, options: NegotiateOfferOptions ): Promise> // methods for request - abstract createRequest( + public abstract createRequest( agentContext: AgentContext, options: CreateRequestOptions ): Promise> - abstract processRequest(messageContext: InboundMessageContext): Promise - abstract acceptRequest( + public abstract processRequest(messageContext: InboundMessageContext): Promise + public abstract acceptRequest( agentContext: AgentContext, options: AcceptRequestOptions ): Promise> // methods for issue - abstract processCredential(messageContext: InboundMessageContext): Promise - abstract acceptCredential( + public abstract processCredential( + messageContext: InboundMessageContext + ): Promise + public abstract acceptCredential( agentContext: AgentContext, options: AcceptCredentialOptions ): Promise> // methods for ack - abstract processAck(messageContext: InboundMessageContext): Promise + public abstract processAck(messageContext: InboundMessageContext): Promise // methods for problem-report - abstract createProblemReport(agentContext: AgentContext, options: CreateProblemReportOptions): ProblemReportMessage + public abstract createProblemReport( + agentContext: AgentContext, + options: CreateProblemReportOptions + ): ProblemReportMessage - abstract findProposalMessage(agentContext: AgentContext, credentialExchangeId: string): Promise - abstract findOfferMessage(agentContext: AgentContext, credentialExchangeId: string): Promise - abstract findRequestMessage(agentContext: AgentContext, credentialExchangeId: string): Promise - abstract findCredentialMessage(agentContext: AgentContext, credentialExchangeId: string): Promise - abstract getFormatData( + public abstract findProposalMessage( + agentContext: AgentContext, + credentialExchangeId: string + ): Promise + public abstract findOfferMessage( + agentContext: AgentContext, + credentialExchangeId: string + ): Promise + public abstract findRequestMessage( + agentContext: AgentContext, + credentialExchangeId: string + ): Promise + public abstract findCredentialMessage( + agentContext: AgentContext, + credentialExchangeId: string + ): Promise + public abstract getFormatData( agentContext: AgentContext, credentialExchangeId: string ): Promise>> - abstract register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void + public abstract register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void /** * Decline a credential offer diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts index e8085a05f2..c7780cf414 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts @@ -58,8 +58,8 @@ let wallet let signCredentialOptions: JsonLdCredentialDetailFormat describe('credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent + let faberAgent: Agent<(typeof faberAgentOptions)['modules']> + let aliceAgent: Agent<(typeof aliceAgentOptions)['modules']> let faberReplay: ReplaySubject let aliceReplay: ReplaySubject const seed = 'testseed000000000000000000000001' diff --git a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index 304fc67487..290865b83a 100644 --- a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -1,8 +1,8 @@ +import type { CredentialMetadata } from './CredentialMetadataTypes' import type { TagsBase } from '../../../storage/BaseRecord' import type { AutoAcceptCredential } from '../models/CredentialAutoAcceptType' import type { CredentialState } from '../models/CredentialState' import type { RevocationNotification } from '../models/RevocationNotification' -import type { CredentialMetadata } from './CredentialMetadataTypes' import { Type } from 'class-transformer' diff --git a/packages/core/src/modules/dids/DidsModule.ts b/packages/core/src/modules/dids/DidsModule.ts index cf438e3ae8..a82dabeb8f 100644 --- a/packages/core/src/modules/dids/DidsModule.ts +++ b/packages/core/src/modules/dids/DidsModule.ts @@ -1,5 +1,5 @@ -import type { DependencyManager, Module } from '../../plugins' import type { DidsModuleConfigOptions } from './DidsModuleConfig' +import type { DependencyManager, Module } from '../../plugins' import { DidsApi } from './DidsApi' import { DidsModuleConfig } from './DidsModuleConfig' diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index 7dd78ca824..f6d6763a4f 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -10,10 +10,11 @@ import { Agent } from '../../../agent/Agent' import { KeyType } from '../../../crypto' import { TypedArrayEncoder } from '../../../utils' import { indyDidFromPublicKeyBase58 } from '../../../utils/did' -import { PeerDidNumAlgo } from '../methods/peer/didPeer' import { InjectionSymbols, JsonTransformer } from '@aries-framework/core' +import { PeerDidNumAlgo } from '../methods/peer/didPeer' + const agentOptions = getAgentOptions('Faber Dids Registrar', { indyLedgers: [ { diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts index 6ac241f5d9..aee5774210 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g1.ts @@ -1,5 +1,5 @@ -import type { VerificationMethod } from '../verificationMethod' import type { KeyDidMapping } from './keyDidMapping' +import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' diff --git a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts index f7cc4b2a6f..e980f9d142 100644 --- a/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts +++ b/packages/core/src/modules/dids/domain/key-type/bls12381g2.ts @@ -1,5 +1,5 @@ -import type { VerificationMethod } from '../verificationMethod' import type { KeyDidMapping } from './keyDidMapping' +import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts index 4098d230b5..5058accff3 100644 --- a/packages/core/src/modules/dids/domain/key-type/ed25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -1,5 +1,5 @@ -import type { VerificationMethod } from '../verificationMethod' import type { KeyDidMapping } from './keyDidMapping' +import type { VerificationMethod } from '../verificationMethod' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' diff --git a/packages/core/src/modules/dids/domain/key-type/x25519.ts b/packages/core/src/modules/dids/domain/key-type/x25519.ts index 5ce7ff0683..399029928e 100644 --- a/packages/core/src/modules/dids/domain/key-type/x25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/x25519.ts @@ -1,5 +1,5 @@ -import type { VerificationMethod } from '../verificationMethod' import type { KeyDidMapping } from './keyDidMapping' +import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto' import { Key } from '../../../../crypto/Key' diff --git a/packages/core/src/modules/dids/repository/DidRecord.ts b/packages/core/src/modules/dids/repository/DidRecord.ts index 752088323b..b36f3f03d0 100644 --- a/packages/core/src/modules/dids/repository/DidRecord.ts +++ b/packages/core/src/modules/dids/repository/DidRecord.ts @@ -1,5 +1,5 @@ -import type { TagsBase } from '../../../storage/BaseRecord' import type { DidRecordMetadata } from './didRecordMetadataTypes' +import type { TagsBase } from '../../../storage/BaseRecord' import { Type } from 'class-transformer' import { IsEnum, ValidateNested } from 'class-validator' diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts index 94e376f08d..3d074f9d18 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts @@ -1,4 +1,3 @@ -import type { Feature } from '../../agent/models' import type { DiscloseFeaturesOptions, QueryFeaturesOptions, @@ -6,6 +5,7 @@ import type { } from './DiscoverFeaturesApiOptions' import type { DiscoverFeaturesDisclosureReceivedEvent } from './DiscoverFeaturesEvents' import type { DiscoverFeaturesService } from './services' +import type { Feature } from '../../agent/models' import { firstValueFrom, of, ReplaySubject, Subject } from 'rxjs' import { catchError, filter, map, takeUntil, timeout } from 'rxjs/operators' @@ -80,7 +80,7 @@ export class DiscoverFeaturesApi< throw new AriesFrameworkError(`No discover features service registered for protocol version ${protocolVersion}`) } - return this.serviceMap[protocolVersion] + return this.serviceMap[protocolVersion] as DiscoverFeaturesService } /** diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts index 7cdcc18cb4..11bdf538b7 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApiOptions.ts @@ -1,5 +1,5 @@ -import type { FeatureQueryOptions } from '../../agent/models' import type { DiscoverFeaturesService } from './services' +import type { FeatureQueryOptions } from '../../agent/models' /** * Get the supported protocol versions based on the provided discover features services. diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts index 79caa885f9..bd97e12ec4 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts @@ -1,6 +1,6 @@ +import type { DiscoverFeaturesModuleConfigOptions } from './DiscoverFeaturesModuleConfig' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' -import type { DiscoverFeaturesModuleConfigOptions } from './DiscoverFeaturesModuleConfig' import { Protocol } from '../../agent/models' diff --git a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts index 0720c8747a..fb5cd56a1e 100644 --- a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts @@ -32,15 +32,15 @@ export abstract class DiscoverFeaturesService { this.discoverFeaturesModuleConfig = discoverFeaturesModuleConfig } - abstract readonly version: string + public abstract readonly version: string - abstract createQuery(options: CreateQueryOptions): Promise> - abstract processQuery( + public abstract createQuery(options: CreateQueryOptions): Promise> + public abstract processQuery( messageContext: InboundMessageContext ): Promise | void> - abstract createDisclosure( + public abstract createDisclosure( options: CreateDisclosureOptions ): Promise> - abstract processDisclosure(messageContext: InboundMessageContext): Promise + public abstract processDisclosure(messageContext: InboundMessageContext): Promise } diff --git a/packages/core/src/modules/generic-records/GenericRecordsApi.ts b/packages/core/src/modules/generic-records/GenericRecordsApi.ts index 56efe6667e..a995d7b4c5 100644 --- a/packages/core/src/modules/generic-records/GenericRecordsApi.ts +++ b/packages/core/src/modules/generic-records/GenericRecordsApi.ts @@ -1,5 +1,5 @@ -import type { Query } from '../../storage/StorageService' import type { GenericRecord, SaveGenericRecordOption } from './repository/GenericRecord' +import type { Query } from '../../storage/StorageService' import { AgentContext } from '../../agent' import { InjectionSymbols } from '../../constants' diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts index 4dd89c2e4d..c1caf5b297 100644 --- a/packages/core/src/modules/indy/services/IndyRevocationService.ts +++ b/packages/core/src/modules/indy/services/IndyRevocationService.ts @@ -1,7 +1,7 @@ import type { AgentContext } from '../../../agent' import type { IndyRevocationInterval } from '../../credentials' import type { RequestedCredentials } from '../../proofs/formats/indy/models/RequestedCredentials' -import type { default as Indy, RevState, RevStates } from 'indy-sdk' +import type { default as Indy, RevStates } from 'indy-sdk' import { AgentDependencies } from '../../../agent/AgentDependencies' import { InjectionSymbols } from '../../../constants' diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts index 8bb9a3de82..4090d146ab 100644 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ b/packages/core/src/modules/ledger/LedgerModule.ts @@ -1,5 +1,5 @@ -import type { DependencyManager, Module } from '../../plugins' import type { LedgerModuleConfigOptions } from './LedgerModuleConfig' +import type { DependencyManager, Module } from '../../plugins' import { AnonCredsCredentialDefinitionRepository } from '../indy/repository/AnonCredsCredentialDefinitionRepository' import { AnonCredsSchemaRepository } from '../indy/repository/AnonCredsSchemaRepository' diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 5936baac7f..aee58655da 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -1,10 +1,10 @@ +import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' import type { AgentMessage } from '../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../agent/Events' import type { Attachment } from '../../decorators/attachment/Attachment' import type { Query } from '../../storage/StorageService' import type { PlaintextMessage } from '../../types' import type { ConnectionInvitationMessage, ConnectionRecord, Routing } from '../connections' -import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' import { catchError, EmptyError, first, firstValueFrom, map, of, timeout } from 'rxjs' diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index f1a77c9bd5..377e8867c2 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -1,10 +1,10 @@ +import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' +import type { OutOfBandRecord } from './repository' import type { AgentContext } from '../../agent' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { Key } from '../../crypto' import type { Query } from '../../storage/StorageService' import type { ConnectionRecord } from '../connections' -import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' -import type { OutOfBandRecord } from './repository' import { EventEmitter } from '../../agent/EventEmitter' import { AriesFrameworkError } from '../../error' diff --git a/packages/core/src/modules/oob/domain/OutOfBandEvents.ts b/packages/core/src/modules/oob/domain/OutOfBandEvents.ts index a3936cc784..15561062b5 100644 --- a/packages/core/src/modules/oob/domain/OutOfBandEvents.ts +++ b/packages/core/src/modules/oob/domain/OutOfBandEvents.ts @@ -1,7 +1,7 @@ +import type { OutOfBandState } from './OutOfBandState' import type { BaseEvent } from '../../../agent/Events' import type { ConnectionRecord } from '../../connections' import type { OutOfBandRecord } from '../repository' -import type { OutOfBandState } from './OutOfBandState' export enum OutOfBandEventTypes { OutOfBandStateChanged = 'OutOfBandStateChanged', diff --git a/packages/core/src/modules/proofs/ProofEvents.ts b/packages/core/src/modules/proofs/ProofEvents.ts index 20394c56a9..86b0e7673c 100644 --- a/packages/core/src/modules/proofs/ProofEvents.ts +++ b/packages/core/src/modules/proofs/ProofEvents.ts @@ -1,6 +1,6 @@ -import type { BaseEvent } from '../../agent/Events' import type { ProofState } from './models/ProofState' import type { ProofExchangeRecord } from './repository' +import type { BaseEvent } from '../../agent/Events' export enum ProofEventTypes { ProofStateChanged = 'ProofStateChanged', diff --git a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts index 77c9a04fd5..db625f71e0 100644 --- a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts +++ b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts @@ -1,5 +1,5 @@ -import type { AgentContext } from '../../agent/context/AgentContext' import type { ProofExchangeRecord } from './repository' +import type { AgentContext } from '../../agent/context/AgentContext' import { injectable } from '../../plugins' diff --git a/packages/core/src/modules/proofs/ProofService.ts b/packages/core/src/modules/proofs/ProofService.ts index dca7cea41f..2fcbe509b1 100644 --- a/packages/core/src/modules/proofs/ProofService.ts +++ b/packages/core/src/modules/proofs/ProofService.ts @@ -1,14 +1,3 @@ -import type { AgentConfig } from '../../agent/AgentConfig' -import type { AgentMessage } from '../../agent/AgentMessage' -import type { Dispatcher } from '../../agent/Dispatcher' -import type { EventEmitter } from '../../agent/EventEmitter' -import type { AgentContext } from '../../agent/context/AgentContext' -import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' -import type { Logger } from '../../logger' -import type { DidCommMessageRepository, DidCommMessageRole } from '../../storage' -import type { Wallet } from '../../wallet/Wallet' -import type { ConnectionService } from '../connections/services' -import type { MediationRecipientService, RoutingService } from '../routing' import type { ProofStateChangedEvent } from './ProofEvents' import type { ProofResponseCoordinator } from './ProofResponseCoordinator' import type { ProofFormat } from './formats/ProofFormat' @@ -30,6 +19,17 @@ import type { } from './models/ProofServiceOptions' import type { ProofState } from './models/ProofState' import type { ProofExchangeRecord, ProofRepository } from './repository' +import type { AgentConfig } from '../../agent/AgentConfig' +import type { AgentMessage } from '../../agent/AgentMessage' +import type { Dispatcher } from '../../agent/Dispatcher' +import type { EventEmitter } from '../../agent/EventEmitter' +import type { AgentContext } from '../../agent/context/AgentContext' +import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' +import type { Logger } from '../../logger' +import type { DidCommMessageRepository, DidCommMessageRole } from '../../storage' +import type { Wallet } from '../../wallet/Wallet' +import type { ConnectionService } from '../connections/services' +import type { MediationRecipientService, RoutingService } from '../routing' import { JsonTransformer } from '../../utils/JsonTransformer' @@ -58,7 +58,7 @@ export abstract class ProofService { this.wallet = wallet this.logger = agentConfig.logger } - abstract readonly version: string + public abstract readonly version: string public emitStateChangedEvent( agentContext: AgentContext, @@ -104,7 +104,7 @@ export abstract class ProofService { * 5. Store proposal message * 6. Return proposal message + proof record */ - abstract createProposal( + public abstract createProposal( agentContext: AgentContext, options: CreateProposalOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> @@ -119,7 +119,7 @@ export abstract class ProofService { * 5. Create or update proposal message * 6. Return proposal message + proof record */ - abstract createProposalAsResponse( + public abstract createProposalAsResponse( agentContext: AgentContext, options: CreateProposalAsResponseOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> @@ -142,40 +142,42 @@ export abstract class ProofService { * 4. Loop through all format services to process proposal message * 5. Save & return record */ - abstract processProposal(messageContext: InboundMessageContext): Promise + public abstract processProposal(messageContext: InboundMessageContext): Promise - abstract createRequest( + public abstract createRequest( agentContext: AgentContext, options: CreateRequestOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract createRequestAsResponse( + public abstract createRequestAsResponse( agentContext: AgentContext, options: CreateRequestAsResponseOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract processRequest(messageContext: InboundMessageContext): Promise + public abstract processRequest(messageContext: InboundMessageContext): Promise - abstract createPresentation( + public abstract createPresentation( agentContext: AgentContext, options: CreatePresentationOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract processPresentation(messageContext: InboundMessageContext): Promise + public abstract processPresentation(messageContext: InboundMessageContext): Promise - abstract createAck( + public abstract createAck( agentContext: AgentContext, options: CreateAckOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract processAck(messageContext: InboundMessageContext): Promise + public abstract processAck(messageContext: InboundMessageContext): Promise - abstract createProblemReport( + public abstract createProblemReport( agentContext: AgentContext, options: CreateProblemReportOptions ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - abstract processProblemReport(messageContext: InboundMessageContext): Promise + public abstract processProblemReport( + messageContext: InboundMessageContext + ): Promise public abstract shouldAutoRespondToProposal( agentContext: AgentContext, diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index af54ebe234..38f5e642bd 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -1,5 +1,3 @@ -import type { AgentMessage } from '../../agent/AgentMessage' -import type { Query } from '../../storage/StorageService' import type { ProofService } from './ProofService' import type { AcceptProofPresentationOptions, @@ -33,6 +31,8 @@ import type { CreateProposalAsResponseOptions, } from './models/ProofServiceOptions' import type { ProofExchangeRecord } from './repository/ProofExchangeRecord' +import type { AgentMessage } from '../../agent/AgentMessage' +import type { Query } from '../../storage/StorageService' import { inject, injectable } from 'tsyringe' @@ -158,7 +158,7 @@ export class ProofsApi< throw new AriesFrameworkError(`No proof service registered for protocol version ${protocolVersion}`) } - return this.serviceMap[protocolVersion] + return this.serviceMap[protocolVersion] as ProofService } /** @@ -728,19 +728,19 @@ export class ProofsApi< public async findProposalMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) const service = this.getService(record.protocolVersion) - return service.findProposalMessage(this.agentContext, proofRecordId) + return service.findProposalMessage(this.agentContext, proofRecordId) as FindProofProposalMessageReturn } public async findRequestMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) const service = this.getService(record.protocolVersion) - return service.findRequestMessage(this.agentContext, proofRecordId) + return service.findRequestMessage(this.agentContext, proofRecordId) as FindProofRequestMessageReturn } public async findPresentationMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) const service = this.getService(record.protocolVersion) - return service.findPresentationMessage(this.agentContext, proofRecordId) + return service.findPresentationMessage(this.agentContext, proofRecordId) as FindProofPresentationMessageReturn } private registerMessageHandlers(dispatcher: Dispatcher, mediationRecipientService: MediationRecipientService) { diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 05136c95a0..339ecd0c41 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -1,6 +1,6 @@ +import type { ProofsModuleConfigOptions } from './ProofsModuleConfig' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' -import type { ProofsModuleConfigOptions } from './ProofsModuleConfig' import { Protocol } from '../../agent/models' diff --git a/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts b/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts index 180a1b8a34..3da57e27c1 100644 --- a/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts +++ b/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts @@ -1,8 +1,8 @@ +import type { CustomProofTags } from './../repository/ProofExchangeRecord' import type { AgentContext } from '../../../agent' import type { Wallet } from '../../../wallet/Wallet' import type { CredentialRepository } from '../../credentials/repository' import type { ProofStateChangedEvent } from '../ProofEvents' -import type { CustomProofTags } from './../repository/ProofExchangeRecord' import { Subject } from 'rxjs' diff --git a/packages/core/src/modules/proofs/formats/ProofFormatService.ts b/packages/core/src/modules/proofs/formats/ProofFormatService.ts index 1ce367cf33..931d4c886f 100644 --- a/packages/core/src/modules/proofs/formats/ProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/ProofFormatService.ts @@ -1,12 +1,3 @@ -import type { AgentContext } from '../../../agent' -import type { AgentConfig } from '../../../agent/AgentConfig' -import type { DidCommMessageRepository } from '../../../storage' -import type { - CreateRequestAsResponseOptions, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, -} from '../models/ProofServiceOptions' -import type { ProofRequestFormats } from '../models/SharedOptions' import type { ProofFormat } from './ProofFormat' import type { IndyProofFormat } from './indy/IndyProofFormat' import type { GetRequestedCredentialsFormat } from './indy/IndyProofFormatsServiceOptions' @@ -20,6 +11,15 @@ import type { ProcessProposalOptions, ProcessRequestOptions, } from './models/ProofFormatServiceOptions' +import type { AgentContext } from '../../../agent' +import type { AgentConfig } from '../../../agent/AgentConfig' +import type { DidCommMessageRepository } from '../../../storage' +import type { + CreateRequestAsResponseOptions, + FormatRequestedCredentialReturn, + FormatRetrievedCredentialOptions, +} from '../models/ProofServiceOptions' +import type { ProofRequestFormats } from '../models/SharedOptions' /** * This abstract class is the base class for any proof format @@ -33,29 +33,31 @@ export abstract class ProofFormatService { protected didCommMessageRepository: DidCommMessageRepository protected agentConfig: AgentConfig - abstract readonly formatKey: PF['formatKey'] + public abstract readonly formatKey: PF['formatKey'] public constructor(didCommMessageRepository: DidCommMessageRepository, agentConfig: AgentConfig) { this.didCommMessageRepository = didCommMessageRepository this.agentConfig = agentConfig } - abstract createProposal(options: FormatCreateProofProposalOptions): Promise + public abstract createProposal(options: FormatCreateProofProposalOptions): Promise - abstract processProposal(options: ProcessProposalOptions): Promise + public abstract processProposal(options: ProcessProposalOptions): Promise - abstract createRequest(options: CreateRequestOptions): Promise + public abstract createRequest(options: CreateRequestOptions): Promise - abstract processRequest(options: ProcessRequestOptions): Promise + public abstract processRequest(options: ProcessRequestOptions): Promise - abstract createPresentation( + public abstract createPresentation( agentContext: AgentContext, options: FormatCreatePresentationOptions ): Promise - abstract processPresentation(agentContext: AgentContext, options: ProcessPresentationOptions): Promise + public abstract processPresentation(agentContext: AgentContext, options: ProcessPresentationOptions): Promise - abstract createProofRequestFromProposal(options: CreatePresentationFormatsOptions): Promise + public abstract createProofRequestFromProposal( + options: CreatePresentationFormatsOptions + ): Promise public abstract getRequestedCredentialsForProofRequest( agentContext: AgentContext, @@ -66,14 +68,14 @@ export abstract class ProofFormatService { options: FormatRetrievedCredentialOptions<[PF]> ): Promise> - abstract proposalAndRequestAreEqual( + public abstract proposalAndRequestAreEqual( proposalAttachments: ProofAttachmentFormat[], requestAttachments: ProofAttachmentFormat[] ): boolean - abstract supportsFormat(formatIdentifier: string): boolean + public abstract supportsFormat(formatIdentifier: string): boolean - abstract createRequestAsResponse( + public abstract createRequestAsResponse( options: CreateRequestAsResponseOptions<[IndyProofFormat]> ): Promise } diff --git a/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts index 0fcd3d405c..25731e43c8 100644 --- a/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts @@ -1,7 +1,7 @@ -import type { Attachment } from '../../../decorators/attachment/Attachment' -import type { ProofFormatSpec } from '../models/ProofFormatSpec' import type { ProofFormat } from './ProofFormat' import type { ProofFormatService } from './ProofFormatService' +import type { Attachment } from '../../../decorators/attachment/Attachment' +import type { ProofFormatSpec } from '../models/ProofFormatSpec' /** * Get the service map for usage in the proofs module. Will return a type mapping of protocol version to service. diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts index 8d6769be1e..ae58b2db75 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts @@ -1,9 +1,9 @@ -import type { PresentationPreviewAttribute, PresentationPreviewPredicate } from '../../protocol/v1' -import type { ProofFormat } from '../ProofFormat' -import type { IndyRequestProofFormat } from '../indy/IndyProofFormatsServiceOptions' import type { RequestedAttribute } from './models/RequestedAttribute' import type { IndyRequestedCredentialsOptions } from './models/RequestedCredentials' import type { RequestedPredicate } from './models/RequestedPredicate' +import type { PresentationPreviewAttribute, PresentationPreviewPredicate } from '../../protocol/v1' +import type { ProofFormat } from '../ProofFormat' +import type { IndyRequestProofFormat } from '../indy/IndyProofFormatsServiceOptions' import type { IndyProof, IndyProofRequest } from 'indy-sdk' export interface IndyProposeProofFormat { diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts index 1aec763e65..ecdb78f358 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts @@ -1,3 +1,5 @@ +import type { IndyProofFormat, IndyProposeProofFormat } from './IndyProofFormat' +import type { GetRequestedCredentialsFormat } from './IndyProofFormatsServiceOptions' import type { AgentContext } from '../../../../agent' import type { Logger } from '../../../../logger' import type { @@ -20,8 +22,6 @@ import type { ProcessRequestOptions, VerifyProofOptions, } from '../models/ProofFormatServiceOptions' -import type { IndyProofFormat, IndyProposeProofFormat } from './IndyProofFormat' -import type { GetRequestedCredentialsFormat } from './IndyProofFormatsServiceOptions' import type { CredDef, IndyProof, Schema } from 'indy-sdk' import { Lifecycle, scoped } from 'tsyringe' diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts index b1ff554453..bb78139bb7 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts @@ -1,11 +1,11 @@ +import type { IndyRequestedCredentialsFormat } from './IndyProofFormat' +import type { ProofAttributeInfo } from '.././indy/models/ProofAttributeInfo' +import type { ProofPredicateInfo } from '.././indy/models/ProofPredicateInfo' import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { IndyRevocationInterval } from '../../../credentials' import type { GetRequestedCredentialsConfig } from '../../models/GetRequestedCredentialsConfig' import type { PresentationPreview } from '../../protocol/v1/models/V1PresentationPreview' import type { ProofExchangeRecord } from '../../repository/ProofExchangeRecord' -import type { ProofAttributeInfo } from '.././indy/models/ProofAttributeInfo' -import type { ProofPredicateInfo } from '.././indy/models/ProofPredicateInfo' -import type { IndyRequestedCredentialsFormat } from './IndyProofFormat' export type IndyPresentationProofFormat = IndyRequestedCredentialsFormat diff --git a/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts index 2538aaf1b4..8859c48b64 100644 --- a/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts @@ -1,9 +1,9 @@ +import type { ProofAttachmentFormat } from './ProofAttachmentFormat' import type { Attachment } from '../../../../decorators/attachment/Attachment' import type { ProposeProofFormats } from '../../models/SharedOptions' import type { ProofExchangeRecord } from '../../repository' import type { ProofFormat, ProofFormatPayload } from '../ProofFormat' import type { ProofRequestOptions } from '../indy/models/ProofRequest' -import type { ProofAttachmentFormat } from './ProofAttachmentFormat' export interface CreateRequestAttachmentOptions { id?: string diff --git a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts index 3c7e7e47af..1a978404eb 100644 --- a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts +++ b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts @@ -1,8 +1,8 @@ +import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' +import type { AutoAcceptProof } from './ProofAutoAcceptType' import type { ConnectionRecord } from '../../connections' import type { ProofFormat, ProofFormatPayload } from '../formats/ProofFormat' import type { ProofExchangeRecord } from '../repository' -import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' -import type { AutoAcceptProof } from './ProofAutoAcceptType' export type FormatDataMessagePayload< CFs extends ProofFormat[] = ProofFormat[], diff --git a/packages/core/src/modules/proofs/models/SharedOptions.ts b/packages/core/src/modules/proofs/models/SharedOptions.ts index e479dea456..18fe5ef7f3 100644 --- a/packages/core/src/modules/proofs/models/SharedOptions.ts +++ b/packages/core/src/modules/proofs/models/SharedOptions.ts @@ -1,9 +1,9 @@ +import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' import type { IndyProposeProofFormat } from '../formats/indy/IndyProofFormat' import type { IndyRequestProofFormat, IndyVerifyProofFormat } from '../formats/indy/IndyProofFormatsServiceOptions' import type { ProofRequest } from '../formats/indy/models/ProofRequest' import type { IndyRequestedCredentialsOptions } from '../formats/indy/models/RequestedCredentials' import type { RetrievedCredentials } from '../formats/indy/models/RetrievedCredentials' -import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' export interface ProposeProofFormats { // If you want to propose an indy proof without attributes or diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts index 1596e55e03..cfe6ae8e43 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts @@ -340,7 +340,7 @@ export class V2ProofService extends P for (const attachmentFormat of requestAttachments) { const service = this.getFormatServiceForFormat(attachmentFormat.format) - service?.processRequest({ + await service?.processRequest({ requestAttachment: attachmentFormat, }) } diff --git a/packages/core/src/modules/routing/MediatorApi.ts b/packages/core/src/modules/routing/MediatorApi.ts index bc366e0015..78a1cbd849 100644 --- a/packages/core/src/modules/routing/MediatorApi.ts +++ b/packages/core/src/modules/routing/MediatorApi.ts @@ -1,5 +1,5 @@ -import type { EncryptedMessage } from '../../types' import type { MediationRecord } from './repository' +import type { EncryptedMessage } from '../../types' import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index db81da0f1a..fa4ef31f13 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -1,6 +1,6 @@ +import type { MediatorModuleConfigOptions } from './MediatorModuleConfig' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' -import type { MediatorModuleConfigOptions } from './MediatorModuleConfig' import { Protocol } from '../../agent/models' diff --git a/packages/core/src/modules/routing/RecipientApi.ts b/packages/core/src/modules/routing/RecipientApi.ts index 49c09365eb..e74ee664ca 100644 --- a/packages/core/src/modules/routing/RecipientApi.ts +++ b/packages/core/src/modules/routing/RecipientApi.ts @@ -1,8 +1,8 @@ -import type { OutboundWebSocketClosedEvent, OutboundWebSocketOpenedEvent } from '../../transport' -import type { ConnectionRecord } from '../connections' import type { MediationStateChangedEvent } from './RoutingEvents' import type { MediationRecord } from './repository' import type { GetRoutingOptions } from './services/RoutingService' +import type { OutboundWebSocketClosedEvent, OutboundWebSocketOpenedEvent } from '../../transport' +import type { ConnectionRecord } from '../connections' import { firstValueFrom, interval, merge, ReplaySubject, Subject, timer } from 'rxjs' import { delayWhen, filter, first, takeUntil, tap, throttleTime, timeout } from 'rxjs/operators' diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index 7f160cdb4a..8ba9364a9d 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -1,6 +1,6 @@ +import type { RecipientModuleConfigOptions } from './RecipientModuleConfig' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' -import type { RecipientModuleConfigOptions } from './RecipientModuleConfig' import { Protocol } from '../../agent/models' diff --git a/packages/core/src/modules/routing/RoutingEvents.ts b/packages/core/src/modules/routing/RoutingEvents.ts index f7aa892ebf..86a151abff 100644 --- a/packages/core/src/modules/routing/RoutingEvents.ts +++ b/packages/core/src/modules/routing/RoutingEvents.ts @@ -1,8 +1,8 @@ -import type { BaseEvent } from '../../agent/Events' -import type { Routing } from '../connections' import type { KeylistUpdate } from './messages/KeylistUpdateMessage' import type { MediationState } from './models/MediationState' import type { MediationRecord } from './repository/MediationRecord' +import type { BaseEvent } from '../../agent/Events' +import type { Routing } from '../connections' export enum RoutingEventTypes { MediationStateChanged = 'MediationStateChanged', diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts index 7c6624af68..9211359eb0 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts @@ -1,6 +1,6 @@ +import type { BatchPickupMessage } from './messages' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { EncryptedMessage } from '../../../../../types' -import type { BatchPickupMessage } from './messages' import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts index 77d23c2e69..147467f10d 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts @@ -1,6 +1,6 @@ +import type { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from './messages' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { EncryptedMessage } from '../../../../../types' -import type { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from './messages' import { Dispatcher } from '../../../../../agent/Dispatcher' import { OutboundMessageContext } from '../../../../../agent/models' diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index c2ff4acba6..aac33420d6 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -1,3 +1,4 @@ +import type { GetRoutingOptions, RemoveRoutingOptions } from './RoutingService' import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../../agent/Events' @@ -9,7 +10,6 @@ import type { Routing } from '../../connections/services/ConnectionService' import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' import type { MediationDenyMessage } from '../messages' import type { StatusMessage, MessageDeliveryMessage } from '../protocol' -import type { GetRoutingOptions, RemoveRoutingOptions } from './RoutingService' import { firstValueFrom, ReplaySubject } from 'rxjs' import { filter, first, timeout } from 'rxjs/operators' diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 46ac773401..e841a7162d 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -1,6 +1,3 @@ -import type { AgentContext } from '../../agent/context' -import type { Key } from '../../crypto/Key' -import type { Query } from '../../storage/StorageService' import type { W3cVerifyCredentialResult } from './models' import type { CreatePresentationOptions, @@ -12,6 +9,9 @@ import type { VerifyPresentationOptions, } from './models/W3cCredentialServiceOptions' import type { VerifyPresentationResult } from './models/presentation/VerifyPresentationResult' +import type { AgentContext } from '../../agent/context' +import type { Key } from '../../crypto/Key' +import type { Query } from '../../storage/StorageService' import { createWalletKeyPairClass } from '../../crypto/WalletKeyPair' import { AriesFrameworkError } from '../../error' diff --git a/packages/core/src/modules/vc/W3cVcModule.ts b/packages/core/src/modules/vc/W3cVcModule.ts index 96231aa168..a793da49f4 100644 --- a/packages/core/src/modules/vc/W3cVcModule.ts +++ b/packages/core/src/modules/vc/W3cVcModule.ts @@ -1,5 +1,5 @@ -import type { DependencyManager, Module } from '../../plugins' import type { W3cVcModuleConfigOptions } from './W3cVcModuleConfig' +import type { DependencyManager, Module } from '../../plugins' import { KeyType } from '../../crypto' import { diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index 2ab30fe7e5..c339fbfb4e 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -310,7 +310,7 @@ describe('W3cCredentialService', () => { describe('Credential Storage', () => { let w3cCredentialRecord: W3cCredentialRecord - let w3cCredentialRepositoryDeleteMock: jest.MockedFunction + let w3cCredentialRepositoryDeleteMock: jest.MockedFunction<(typeof w3cCredentialRepository)['delete']> beforeEach(async () => { const credential = JsonTransformer.fromJSON( diff --git a/packages/core/src/modules/vc/jsonldUtil.ts b/packages/core/src/modules/vc/jsonldUtil.ts index 761e22726f..b4500c3ba1 100644 --- a/packages/core/src/modules/vc/jsonldUtil.ts +++ b/packages/core/src/modules/vc/jsonldUtil.ts @@ -1,6 +1,6 @@ +import type { GetProofsOptions, GetProofsResult, GetTypeOptions } from './models' import type { JsonObject, JsonValue } from '../../types' import type { SingleOrArray } from '../../utils/type' -import type { GetProofsOptions, GetProofsResult, GetTypeOptions } from './models' import { SECURITY_CONTEXT_URL } from './constants' import jsonld from './libraries/jsonld' diff --git a/packages/core/src/modules/vc/libraries/documentLoader.ts b/packages/core/src/modules/vc/libraries/documentLoader.ts index d8679884d8..50fcde95d0 100644 --- a/packages/core/src/modules/vc/libraries/documentLoader.ts +++ b/packages/core/src/modules/vc/libraries/documentLoader.ts @@ -1,5 +1,5 @@ -import type { AgentContext } from '../../../agent/context/AgentContext' import type { DocumentLoader } from './jsonld' +import type { AgentContext } from '../../../agent/context/AgentContext' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { DidResolverService } from '../../dids' diff --git a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts index c15302a7a8..042550fd8e 100644 --- a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts +++ b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts @@ -1,10 +1,10 @@ -import type { JsonObject } from '../../../types' -import type { SingleOrArray } from '../../../utils/type' -import type { ProofPurpose } from '../proof-purposes/ProofPurpose' import type { W3cCredential } from './credential/W3cCredential' import type { W3cVerifiableCredential } from './credential/W3cVerifiableCredential' import type { W3cPresentation } from './presentation/W3cPresentation' import type { W3cVerifiablePresentation } from './presentation/W3cVerifiablePresentation' +import type { JsonObject } from '../../../types' +import type { SingleOrArray } from '../../../utils/type' +import type { ProofPurpose } from '../proof-purposes/ProofPurpose' export interface SignCredentialOptions { credential: W3cCredential diff --git a/packages/core/src/modules/vc/models/credential/W3cCredential.ts b/packages/core/src/modules/vc/models/credential/W3cCredential.ts index beed50d700..ca5a1398bc 100644 --- a/packages/core/src/modules/vc/models/credential/W3cCredential.ts +++ b/packages/core/src/modules/vc/models/credential/W3cCredential.ts @@ -1,6 +1,6 @@ -import type { JsonObject } from '../../../../types' import type { CredentialSubjectOptions } from './CredentialSubject' import type { IssuerOptions } from './Issuer' +import type { JsonObject } from '../../../../types' import type { ValidationOptions } from 'class-validator' import { Expose, Type } from 'class-transformer' diff --git a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts index 4fbce4e41e..67c09e1ac2 100644 --- a/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts +++ b/packages/core/src/modules/vc/models/credential/W3cVerifiableCredential.ts @@ -1,5 +1,5 @@ -import type { LinkedDataProofOptions } from '../LinkedDataProof' import type { W3cCredentialOptions } from './W3cCredential' +import type { LinkedDataProofOptions } from '../LinkedDataProof' import { instanceToPlain, plainToInstance, Transform, TransformationType } from 'class-transformer' diff --git a/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts b/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts index 9f8880467a..aaecf7c931 100644 --- a/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts +++ b/packages/core/src/modules/vc/models/credential/W3cVerifyCredentialResult.ts @@ -1,5 +1,5 @@ -import type { JsonObject } from '../../../../types' import type { W3cVerifiableCredential } from './W3cVerifiableCredential' +import type { JsonObject } from '../../../../types' export interface VerifyCredentialResult { credential: W3cVerifiableCredential diff --git a/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts index fa1fd6001a..67a106ee59 100644 --- a/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts +++ b/packages/core/src/modules/vc/models/presentation/W3cVerifiablePresentation.ts @@ -1,5 +1,5 @@ -import type { LinkedDataProofOptions } from '../LinkedDataProof' import type { W3cPresentationOptions } from './W3cPresentation' +import type { LinkedDataProofOptions } from '../LinkedDataProof' import { SingleOrArray } from '../../../../utils/type' import { IsInstanceOrArrayOfInstances } from '../../../../utils/validators' diff --git a/packages/core/src/plugins/Module.ts b/packages/core/src/plugins/Module.ts index 8170b159de..93196d71cf 100644 --- a/packages/core/src/plugins/Module.ts +++ b/packages/core/src/plugins/Module.ts @@ -1,6 +1,6 @@ +import type { DependencyManager } from './DependencyManager' import type { FeatureRegistry } from '../agent/FeatureRegistry' import type { Constructor } from '../utils/mixins' -import type { DependencyManager } from './DependencyManager' export interface Module { api?: Constructor diff --git a/packages/core/src/storage/InMemoryMessageRepository.ts b/packages/core/src/storage/InMemoryMessageRepository.ts index a1f01b6515..cf98440a7d 100644 --- a/packages/core/src/storage/InMemoryMessageRepository.ts +++ b/packages/core/src/storage/InMemoryMessageRepository.ts @@ -1,5 +1,5 @@ -import type { EncryptedMessage } from '../types' import type { MessageRepository } from './MessageRepository' +import type { EncryptedMessage } from '../types' import { InjectionSymbols } from '../constants' import { Logger } from '../logger' diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts index a66cb579fd..452ef555c1 100644 --- a/packages/core/src/storage/IndyStorageService.ts +++ b/packages/core/src/storage/IndyStorageService.ts @@ -1,7 +1,7 @@ -import type { AgentContext } from '../agent' -import type { IndyWallet } from '../wallet/IndyWallet' import type { BaseRecord, TagsBase } from './BaseRecord' import type { BaseRecordConstructor, Query, StorageService } from './StorageService' +import type { AgentContext } from '../agent' +import type { IndyWallet } from '../wallet/IndyWallet' import type { default as Indy, WalletQuery, WalletRecord, WalletSearchOptions } from 'indy-sdk' import { AgentDependencies } from '../agent/AgentDependencies' @@ -14,7 +14,8 @@ import { isBoolean } from '../utils/type' import { assertIndyWallet } from '../wallet/util/assertIndyWallet' @injectable() -export class IndyStorageService implements StorageService { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export class IndyStorageService> implements StorageService { private indy: typeof Indy private static DEFAULT_QUERY_OPTIONS = { diff --git a/packages/core/src/storage/Repository.ts b/packages/core/src/storage/Repository.ts index 674d2e3e7a..30cc980d7a 100644 --- a/packages/core/src/storage/Repository.ts +++ b/packages/core/src/storage/Repository.ts @@ -1,8 +1,8 @@ -import type { AgentContext } from '../agent' -import type { EventEmitter } from '../agent/EventEmitter' import type { BaseRecord } from './BaseRecord' import type { RecordSavedEvent, RecordUpdatedEvent, RecordDeletedEvent } from './RepositoryEvents' import type { BaseRecordConstructor, Query, StorageService } from './StorageService' +import type { AgentContext } from '../agent' +import type { EventEmitter } from '../agent/EventEmitter' import { RecordDuplicateError, RecordNotFoundError } from '../error' import { JsonTransformer } from '../utils/JsonTransformer' diff --git a/packages/core/src/storage/RepositoryEvents.ts b/packages/core/src/storage/RepositoryEvents.ts index cf9a0d3157..3e3b1e2952 100644 --- a/packages/core/src/storage/RepositoryEvents.ts +++ b/packages/core/src/storage/RepositoryEvents.ts @@ -1,5 +1,5 @@ -import type { BaseEvent } from '../agent/Events' import type { BaseRecord } from './BaseRecord' +import type { BaseEvent } from '../agent/Events' export enum RepositoryEventTypes { RecordSaved = 'RecordSaved', diff --git a/packages/core/src/storage/StorageService.ts b/packages/core/src/storage/StorageService.ts index 6ea701df56..180af06bd4 100644 --- a/packages/core/src/storage/StorageService.ts +++ b/packages/core/src/storage/StorageService.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import type { BaseRecord, TagsBase } from './BaseRecord' import type { AgentContext } from '../agent' import type { Constructor } from '../utils/mixins' -import type { BaseRecord, TagsBase } from './BaseRecord' // https://stackoverflow.com/questions/51954558/how-can-i-remove-a-wider-type-from-a-union-type-without-removing-its-subtypes-in/51955852#51955852 export type SimpleQuery = Partial> & TagsBase diff --git a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts index e7c28a84a8..9f234bb15b 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts @@ -1,6 +1,6 @@ +import type { DidCommMessageRole } from './DidCommMessageRole' import type { ConstructableAgentMessage } from '../../agent/AgentMessage' import type { JsonObject } from '../../types' -import type { DidCommMessageRole } from './DidCommMessageRole' import { AriesFrameworkError } from '../../error' import { JsonTransformer } from '../../utils/JsonTransformer' diff --git a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts index db2db2d04f..cffa511e3a 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRepository.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRepository.ts @@ -1,7 +1,7 @@ +import type { DidCommMessageRole } from './DidCommMessageRole' import type { AgentContext } from '../../agent' import type { AgentMessage, ConstructableAgentMessage } from '../../agent/AgentMessage' import type { JsonObject } from '../../types' -import type { DidCommMessageRole } from './DidCommMessageRole' import { EventEmitter } from '../../agent/EventEmitter' import { InjectionSymbols } from '../../constants' diff --git a/packages/core/src/storage/migration/StorageUpdateService.ts b/packages/core/src/storage/migration/StorageUpdateService.ts index eef16af7ff..b5b196406d 100644 --- a/packages/core/src/storage/migration/StorageUpdateService.ts +++ b/packages/core/src/storage/migration/StorageUpdateService.ts @@ -1,6 +1,6 @@ +import type { UpdateToVersion } from './updates' import type { AgentContext } from '../../agent' import type { VersionString } from '../../utils/version' -import type { UpdateToVersion } from './updates' import { InjectionSymbols } from '../../constants' import { Logger } from '../../logger' diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index 756da0e093..ad34a75a28 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -1,6 +1,6 @@ +import type { UpdateConfig, UpdateToVersion } from './updates' import type { BaseAgent } from '../../agent/BaseAgent' import type { FileSystem } from '../FileSystem' -import type { UpdateConfig, UpdateToVersion } from './updates' import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' diff --git a/packages/core/src/storage/migration/updates.ts b/packages/core/src/storage/migration/updates.ts index 08c890fdd0..294975e0f4 100644 --- a/packages/core/src/storage/migration/updates.ts +++ b/packages/core/src/storage/migration/updates.ts @@ -1,6 +1,6 @@ +import type { V0_1ToV0_2UpdateConfig } from './updates/0.1-0.2' import type { BaseAgent } from '../../agent/BaseAgent' import type { VersionString } from '../../utils/version' -import type { V0_1ToV0_2UpdateConfig } from './updates/0.1-0.2' import { updateV0_1ToV0_2 } from './updates/0.1-0.2' import { updateV0_2ToV0_3 } from './updates/0.2-0.3' @@ -49,4 +49,4 @@ export const CURRENT_FRAMEWORK_STORAGE_VERSION = supportedUpdates[supportedUpdat // eslint-disable-next-line @typescript-eslint/no-unused-vars type LastItem = T extends readonly [...infer _, infer U] ? U : T[0] | undefined -export type UpdateToVersion = typeof supportedUpdates[number]['toVersion'] +export type UpdateToVersion = (typeof supportedUpdates)[number]['toVersion'] diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts b/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts index 7d41c3366d..c131646507 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/mediation.ts @@ -1,6 +1,6 @@ +import type { V0_1ToV0_2UpdateConfig } from './index' import type { BaseAgent } from '../../../../agent/BaseAgent' import type { MediationRecord } from '../../../../modules/routing' -import type { V0_1ToV0_2UpdateConfig } from './index' import { MediationRepository, MediationRole } from '../../../../modules/routing' diff --git a/packages/core/src/transport/HttpOutboundTransport.ts b/packages/core/src/transport/HttpOutboundTransport.ts index a9ff5c28d6..8ff21d71da 100644 --- a/packages/core/src/transport/HttpOutboundTransport.ts +++ b/packages/core/src/transport/HttpOutboundTransport.ts @@ -1,8 +1,8 @@ +import type { OutboundTransport } from './OutboundTransport' import type { Agent } from '../agent/Agent' import type { AgentMessageReceivedEvent } from '../agent/Events' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' -import type { OutboundTransport } from './OutboundTransport' import type fetch from 'node-fetch' import { AbortController } from 'abort-controller' diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index 8e97107141..1c248036da 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -1,9 +1,9 @@ +import type { OutboundTransport } from './OutboundTransport' +import type { OutboundWebSocketClosedEvent, OutboundWebSocketOpenedEvent } from './TransportEventTypes' import type { Agent } from '../agent/Agent' import type { AgentMessageReceivedEvent } from '../agent/Events' import type { Logger } from '../logger' import type { OutboundPackage } from '../types' -import type { OutboundTransport } from './OutboundTransport' -import type { OutboundWebSocketClosedEvent, OutboundWebSocketOpenedEvent } from './TransportEventTypes' import type WebSocket from 'ws' import { AgentEventTypes } from '../agent/Events' diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 36450e0c3c..d2f5a21c8f 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -176,7 +176,6 @@ export interface JsonObject { [property: string]: JsonValue } -// Flatten an array of arrays /** * Flatten an array of arrays * @example diff --git a/packages/core/src/utils/attachment.ts b/packages/core/src/utils/attachment.ts index 5831e7a37c..a50441abb5 100644 --- a/packages/core/src/utils/attachment.ts +++ b/packages/core/src/utils/attachment.ts @@ -1,5 +1,5 @@ -import type { Attachment } from '../decorators/attachment/Attachment' import type { BaseName } from './MultiBaseEncoder' +import type { Attachment } from '../decorators/attachment/Attachment' import { AriesFrameworkError } from '../error/AriesFrameworkError' diff --git a/packages/core/src/utils/indyError.ts b/packages/core/src/utils/indyError.ts index c46ebef13e..0472ac0f04 100644 --- a/packages/core/src/utils/indyError.ts +++ b/packages/core/src/utils/indyError.ts @@ -60,7 +60,7 @@ export const indyErrors = { 706: 'TransactionNotAllowedError', } as const -type IndyErrorValues = typeof indyErrors[keyof typeof indyErrors] +type IndyErrorValues = (typeof indyErrors)[keyof typeof indyErrors] export interface IndyError { name: 'IndyError' diff --git a/packages/core/src/utils/messageType.ts b/packages/core/src/utils/messageType.ts index acac87978e..7d7232d330 100644 --- a/packages/core/src/utils/messageType.ts +++ b/packages/core/src/utils/messageType.ts @@ -1,5 +1,5 @@ -import type { PlaintextMessage } from '../types' import type { VersionString } from './version' +import type { PlaintextMessage } from '../types' import type { ValidationOptions, ValidationArguments } from 'class-validator' import { ValidateBy, buildMessage } from 'class-validator' diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 3e279ff2aa..0bef6447d6 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -1,12 +1,3 @@ -import type { KeyPair } from '../crypto/signing-provider/SigningProvider' -import type { - EncryptedMessage, - KeyDerivationMethod, - WalletConfig, - WalletConfigRekey, - WalletExportImportConfig, -} from '../types' -import type { Buffer } from '../utils/buffer' import type { WalletCreateKeyOptions, DidConfig, @@ -16,6 +7,15 @@ import type { WalletVerifyOptions, Wallet, } from './Wallet' +import type { KeyPair } from '../crypto/signing-provider/SigningProvider' +import type { + EncryptedMessage, + KeyDerivationMethod, + WalletConfig, + WalletConfigRekey, + WalletExportImportConfig, +} from '../types' +import type { Buffer } from '../utils/buffer' import type { default as Indy, WalletStorageConfig } from 'indy-sdk' import { inject, injectable } from 'tsyringe' diff --git a/packages/core/src/wallet/WalletApi.ts b/packages/core/src/wallet/WalletApi.ts index 548df623cf..144e9722c3 100644 --- a/packages/core/src/wallet/WalletApi.ts +++ b/packages/core/src/wallet/WalletApi.ts @@ -1,5 +1,5 @@ -import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '../types' import type { Wallet } from './Wallet' +import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '../types' import { AgentContext } from '../agent' import { InjectionSymbols } from '../constants' diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 96f7715bd6..4df883e972 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { CreateOfferOptions, V1CredentialProtocol } from '../src/modules/credentials' +import type { CreateOfferOptions, DefaultCredentialProtocols } from '../src/modules/credentials' import type { AgentMessage, AgentMessageReceivedEvent } from '@aries-framework/core' import { Subject } from 'rxjs' @@ -10,6 +10,15 @@ import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutbou import { Agent } from '../src/agent/Agent' import { Key } from '../src/crypto' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' + +import { + AgentEventTypes, + AriesFrameworkError, + AutoAcceptCredential, + CredentialState, + V1CredentialPreview, +} from '@aries-framework/core' + import { OutOfBandDidCommService } from '../src/modules/oob/domain/OutOfBandDidCommService' import { OutOfBandEventTypes } from '../src/modules/oob/domain/OutOfBandEvents' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' @@ -21,14 +30,6 @@ import { JsonEncoder } from '../src/utils' import { TestMessage } from './TestMessage' import { getAgentOptions, prepareForIssuance, waitForCredentialRecord } from './helpers' -import { - AgentEventTypes, - AriesFrameworkError, - AutoAcceptCredential, - CredentialState, - V1CredentialPreview, -} from '@aries-framework/core' - const faberAgentOptions = getAgentOptions('Faber Agent OOB', { endpoints: ['rxjs:faber'], }) @@ -57,7 +58,7 @@ describe('out of band', () => { let faberAgent: Agent let aliceAgent: Agent - let credentialTemplate: CreateOfferOptions<[V1CredentialProtocol]> + let credentialTemplate: CreateOfferOptions beforeAll(async () => { const faberMessages = new Subject() diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 4c19732005..02dec486ae 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -19,7 +19,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -34,7 +34,7 @@ "tsyringe": "^4.7.0" }, "devDependencies": { - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts index 9f94c7326c..8553af9295 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -1,5 +1,5 @@ -import type { IndySdkPool } from '../ledger' import type { IndyEndpointAttrib } from './didSovUtil' +import type { IndySdkPool } from '../ledger' import type { AgentContext, DidRegistrar, diff --git a/packages/indy-sdk/src/error/indyError.ts b/packages/indy-sdk/src/error/indyError.ts index 5d67cfdbf1..c5d23f6093 100644 --- a/packages/indy-sdk/src/error/indyError.ts +++ b/packages/indy-sdk/src/error/indyError.ts @@ -60,7 +60,7 @@ export const indyErrors = { 706: 'TransactionNotAllowedError', } as const -type IndyErrorValues = typeof indyErrors[keyof typeof indyErrors] +type IndyErrorValues = (typeof indyErrors)[keyof typeof indyErrors] export interface IndyError { name: 'IndyError' diff --git a/packages/node/package.json b/packages/node/package.json index 540489b8b6..09f32cc942 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -22,13 +22,14 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" }, "dependencies": { "@aries-framework/core": "0.3.2", + "@types/express": "^4.17.15", "express": "^4.17.1", "ffi-napi": "^4.0.3", "indy-sdk": "^1.16.0-dev-1636", @@ -37,13 +38,12 @@ "ws": "^7.5.3" }, "devDependencies": { - "@types/express": "^4.17.13", "@types/ffi-napi": "^4.0.5", - "@types/node": "^15.14.4", + "@types/node": "^16.11.7", "@types/node-fetch": "^2.5.10", "@types/ref-napi": "^3.0.4", "@types/ws": "^7.4.6", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index cac010d054..7b251a86b3 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -19,7 +19,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -34,7 +34,7 @@ "devDependencies": { "@aries-framework/node": "0.3.2", "reflect-metadata": "^0.1.13", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 01a769bea8..547a9a3f2c 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -18,7 +18,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -32,7 +32,7 @@ "devDependencies": { "@aries-framework/node": "0.3.2", "reflect-metadata": "^0.1.13", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/packages/question-answer/tests/question-answer.e2e.test.ts b/packages/question-answer/tests/question-answer.e2e.test.ts index e8d6d5a0c3..c2c35d8c2b 100644 --- a/packages/question-answer/tests/question-answer.e2e.test.ts +++ b/packages/question-answer/tests/question-answer.e2e.test.ts @@ -9,10 +9,10 @@ import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutbou import { getAgentOptions, makeConnection } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' -import { waitForQuestionAnswerRecord } from './helpers' - import { QuestionAnswerModule, QuestionAnswerRole, QuestionAnswerState } from '@aries-framework/question-answer' +import { waitForQuestionAnswerRecord } from './helpers' + const bobAgentOptions = getAgentOptions( 'Bob Question Answer', { diff --git a/packages/react-native/package.json b/packages/react-native/package.json index dab91dd4ec..e2b1b02691 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -18,7 +18,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -36,8 +36,8 @@ "react-native": "0.64.2", "react-native-fs": "^2.18.0", "react-native-get-random-values": "^1.7.0", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" }, "peerDependencies": { "indy-sdk-react-native": "^0.3.0", diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 191baf900f..2c4ad8faa9 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -18,7 +18,7 @@ }, "scripts": { "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", + "clean": "rimraf ./build", "compile": "tsc -p tsconfig.build.json", "prepublishOnly": "yarn run build", "test": "jest" @@ -30,7 +30,7 @@ "devDependencies": { "@aries-framework/node": "0.3.2", "reflect-metadata": "^0.1.13", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.0.7", + "typescript": "~4.9.4" } } diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 1b6cdb09cc..6f550435fc 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -6,10 +6,11 @@ import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' + const recipientAgentOptions = getAgentOptions('E2E Subject Recipient', { autoAcceptCredentials: AutoAcceptCredential.ContentApproved, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, diff --git a/tsconfig.build.json b/tsconfig.build.json index 3ff691c0e8..45d3c20c52 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -12,7 +12,9 @@ "allowSyntheticDefaultImports": true, "resolveJsonModule": true, "experimentalDecorators": true, - "emitDecoratorMetadata": true + "emitDecoratorMetadata": true, + // TODO: we should update code to assume errors are of type 'unknown' + "useUnknownInCatchVariables": false }, "exclude": ["node_modules", "build", "**/*.test.ts", "**/__tests__/*.ts", "**/__mocks__/*.ts", "**/build/**"] } diff --git a/yarn.lock b/yarn.lock index 5fb0819bd7..359ab25c51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,6 +10,50 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" +"@aries-framework/core@file:packages/core": + version "0.3.2" + dependencies: + "@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/indy-sdk" "1.16.24" + "@types/node-fetch" "^2.5.10" + "@types/ws" "^7.4.6" + abort-controller "^3.0.0" + bn.js "^5.2.0" + borc "^3.0.0" + buffer "^6.0.3" + class-transformer "0.5.1" + class-validator "0.13.1" + did-resolver "^3.1.3" + lru_map "^0.4.1" + luxon "^1.27.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.7.0" + uuid "^8.3.2" + varint "^6.0.0" + web-did-resolver "^2.0.8" + +"@aries-framework/node@file:packages/node": + version "0.3.2" + dependencies: + "@aries-framework/core" "0.3.2" + "@types/express" "^4.17.15" + express "^4.17.1" + ffi-napi "^4.0.3" + indy-sdk "^1.16.0-dev-1636" + node-fetch "^2.6.1" + ref-napi "^3.0.3" + ws "^7.5.3" + "@azure/core-asynciterator-polyfill@^1.0.0": version "1.0.2" resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" @@ -29,38 +73,38 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.0", "@babel/compat-data@^7.20.1": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.1.tgz#f2e6ef7790d8c8dbf03d379502dcc246dcce0b30" - integrity sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5": + version "7.20.10" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.10.tgz#9d92fa81b87542fff50e848ed585b4212c1d34ec" + integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.2.tgz#8dc9b1620a673f92d3624bd926dc49a52cf25b92" - integrity sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g== + version "7.20.12" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" + integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.2" - "@babel/helper-compilation-targets" "^7.20.0" - "@babel/helper-module-transforms" "^7.20.2" - "@babel/helpers" "^7.20.1" - "@babel/parser" "^7.20.2" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.2" + "@babel/generator" "^7.20.7" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helpers" "^7.20.7" + "@babel/parser" "^7.20.7" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.12" + "@babel/types" "^7.20.7" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.1" + json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.20.1", "@babel/generator@^7.20.2", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.20.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.4.tgz#4d9f8f0c30be75fd90a0562099a26e5839602ab8" - integrity sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA== +"@babel/generator@^7.20.7", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a" + integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw== dependencies: - "@babel/types" "^7.20.2" + "@babel/types" "^7.20.7" "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" @@ -79,36 +123,38 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" - integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" + integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== dependencies: - "@babel/compat-data" "^7.20.0" + "@babel/compat-data" "^7.20.5" "@babel/helper-validator-option" "^7.18.6" browserslist "^4.21.3" + lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.2.tgz#3c08a5b5417c7f07b5cf3dfb6dc79cbec682e8c2" - integrity sha512-k22GoYRAHPYr9I+Gvy2ZQlAe5mGy8BqWst2wRt8cwIufWTxrsVshhIBvYNqC80N0GSFWTsqRVexOtfzlgOEDvA== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.7": + version "7.20.12" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz#4349b928e79be05ed2d1643b20b99bb87c503819" + integrity sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" - "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.20.7" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.19.1" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-create-regexp-features-plugin@^7.18.6": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" - integrity sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz#5ea79b59962a09ec2acf20a963a01ab4d076ccca" + integrity sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.1.0" + regexpu-core "^5.2.1" "@babel/helper-define-polyfill-provider@^0.3.3": version "0.3.3" @@ -149,12 +195,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" - integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== +"@babel/helper-member-expression-to-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz#a6f26e919582275a93c3aa6594756d71b0bb7f05" + integrity sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw== dependencies: - "@babel/types" "^7.18.9" + "@babel/types" "^7.20.7" "@babel/helper-module-imports@^7.18.6": version "7.18.6" @@ -163,19 +209,19 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.19.6", "@babel/helper-module-transforms@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" - integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== +"@babel/helper-module-transforms@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" + integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" "@babel/helper-simple-access" "^7.20.2" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.2" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.10" + "@babel/types" "^7.20.7" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -189,25 +235,26 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78" - integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw== +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" + integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== dependencies: "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.20.7" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/traverse" "^7.19.1" - "@babel/types" "^7.19.0" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.7" + "@babel/types" "^7.20.7" -"@babel/helper-simple-access@^7.19.4", "@babel/helper-simple-access@^7.20.2": +"@babel/helper-simple-access@^7.20.2": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== dependencies: "@babel/types" "^7.20.2" -"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": version "7.20.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== @@ -236,14 +283,14 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== -"@babel/helpers@^7.20.1": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.1.tgz#2ab7a0fcb0a03b5bf76629196ed63c2d7311f4c9" - integrity sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg== +"@babel/helpers@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.7.tgz#04502ff0feecc9f20ecfaad120a18f011a8e6dce" + integrity sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA== dependencies: - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.0" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.7" + "@babel/types" "^7.20.7" "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" @@ -254,10 +301,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.20.1", "@babel/parser@^7.20.2": - version "7.20.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" - integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b" + integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": version "7.18.6" @@ -284,15 +331,15 @@ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz#a556f59d555f06961df1e572bb5eca864c84022d" - integrity sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" + integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== dependencies: - "@babel/compat-data" "^7.20.1" - "@babel/helper-compilation-targets" "^7.20.0" + "@babel/compat-data" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.7" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.20.1" + "@babel/plugin-transform-parameters" "^7.20.7" "@babel/plugin-proposal-optional-catch-binding@^7.0.0": version "7.18.6" @@ -303,12 +350,12 @@ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" - integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz#49f2b372519ab31728cc14115bb0998b15bfda55" + integrity sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-async-generators@^7.8.4": @@ -431,11 +478,11 @@ "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" - integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" + integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-block-scoped-functions@^7.0.0": version "7.18.6" @@ -445,38 +492,39 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.2.tgz#f59b1767e6385c663fd0bce655db6ca9c8b236ed" - integrity sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ== + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.11.tgz#9f5a3424bd112a3f32fe0cf9364fbb155cff262a" + integrity sha512-tA4N427a7fjf1P0/2I4ScsHGc5jcHPbb30xMbaTke2gxDuWpUfXDuX1FEymJwKk4tuGUvGcejAR6HdZVqmmPyw== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-classes@^7.0.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz#c0033cf1916ccf78202d04be4281d161f6709bb2" - integrity sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073" + integrity sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-compilation-targets" "^7.20.7" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" "@babel/helper-optimise-call-expression" "^7.18.6" "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.19.1" + "@babel/helper-replace-supers" "^7.20.7" "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" - integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" + integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/template" "^7.20.7" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz#c23741cfa44ddd35f5e53896e88c75331b8b2792" - integrity sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454" + integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -527,13 +575,13 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": - version "7.19.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz#25b32feef24df8038fc1ec56038917eacb0b730c" - integrity sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ== + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz#8cb23010869bf7669fd4b3098598b6b2be6dc607" + integrity sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw== dependencies: - "@babel/helper-module-transforms" "^7.19.6" - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-simple-access" "^7.19.4" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-simple-access" "^7.20.2" "@babel/plugin-transform-object-assign@^7.0.0": version "7.18.6" @@ -550,10 +598,10 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.1": - version "7.20.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.3.tgz#7b3468d70c3c5b62e46be0a47b6045d8590fb748" - integrity sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA== +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f" + integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -586,23 +634,23 @@ "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz#b3cbb7c3a00b92ec8ae1027910e331ba5c500eb9" - integrity sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.7.tgz#025d85a1935fd7e19dfdcb1b1d4df34d4da484f7" + integrity sha512-Tfq7qqD+tRj3EoDhY00nn2uP2hsRxgYGi5mLQ5TimKav0a9Lrpd4deE+fcLXU8zFYRjlKPHZhpCvfEA6qnBxqQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.19.0" + "@babel/types" "^7.20.7" "@babel/plugin-transform-regenerator@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" - integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" + integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - regenerator-transform "^0.15.0" + "@babel/helper-plugin-utils" "^7.20.2" + regenerator-transform "^0.15.1" "@babel/plugin-transform-runtime@^7.0.0": version "7.19.6" @@ -624,12 +672,12 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-spread@^7.0.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6" - integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" + integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-transform-sticky-regex@^7.0.0": version "7.18.6" @@ -646,11 +694,11 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typescript@^7.18.6", "@babel/plugin-transform-typescript@^7.5.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.2.tgz#91515527b376fc122ba83b13d70b01af8fe98f3f" - integrity sha512-jvS+ngBfrnTUBfOQq8NfGnSbF9BrqlR6hjJ2yVxMkmO5nL/cdifNbI30EfjRlN4g5wYWNnMPyj5Sa6R1pbLeag== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.7.tgz#673f49499cd810ae32a1ea5f3f8fab370987e055" + integrity sha512-m3wVKEvf6SoszD8pu4NZz3PvfKRCMgk6D6d0Qi9hNnlM5M6CFS92EgF4EiHVLKbU0r/r7ty1hg7NPZwE7WRbYw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.20.2" + "@babel/helper-create-class-features-plugin" "^7.20.7" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-typescript" "^7.20.0" @@ -692,41 +740,41 @@ source-map-support "^0.5.16" "@babel/runtime@^7.8.4": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" - integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd" + integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ== dependencies: - regenerator-runtime "^0.13.10" + regenerator-runtime "^0.13.11" -"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.3.3": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" - integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== +"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.10" - "@babel/types" "^7.18.10" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1", "@babel/traverse@^7.7.2": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.1.tgz#9b15ccbf882f6d107eeeecf263fbcdd208777ec8" - integrity sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.7", "@babel/traverse@^7.7.2": + version "7.20.12" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.12.tgz#7f0f787b3a67ca4475adef1f56cb94f6abd4a4b5" + integrity sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.1" + "@babel/generator" "^7.20.7" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.1" - "@babel/types" "^7.20.0" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842" - integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" + integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -2205,9 +2253,9 @@ "@hapi/hoek" "^9.0.0" "@sideway/formula@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" - integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== "@sideway/pinpoint@^2.0.0": version "2.0.0" @@ -2215,9 +2263,9 @@ integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== "@sinonjs/commons@^1.7.0": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.5.tgz#e280c94c95f206dcfd5aca00a43f2156b758c764" - integrity sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA== + version "1.8.6" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" + integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== dependencies: type-detect "4.0.8" @@ -2367,9 +2415,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.2.tgz#235bf339d17185bdec25e024ca19cce257cc7309" - integrity sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg== + version "7.18.3" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.3.tgz#dfc508a85781e5698d5b33443416b6268c4b3e8d" + integrity sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w== dependencies: "@babel/types" "^7.3.0" @@ -2396,9 +2444,11 @@ "@types/node" "*" "@types/cors@^2.8.10": - version "2.8.12" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" - integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== + version "2.8.13" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.13.tgz#b8ade22ba455a1b8cb3b5d3f35910fd204f84f94" + integrity sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA== + dependencies: + "@types/node" "*" "@types/eslint@^7.2.13": version "7.29.0" @@ -2418,29 +2468,29 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== -"@types/express-serve-static-core@^4.17.18": - version "4.17.31" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz#a1139efeab4e7323834bb0226e62ac019f474b2f" - integrity sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q== +"@types/express-serve-static-core@^4.17.31": + version "4.17.32" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz#93dda387f5516af616d8d3f05f2c4c79d81e1b82" + integrity sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" -"@types/express@^4.17.13": - version "4.17.14" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.14.tgz#143ea0557249bc1b3b54f15db4c81c3d4eb3569c" - integrity sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg== +"@types/express@^4.17.13", "@types/express@^4.17.15": + version "4.17.15" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.15.tgz#9290e983ec8b054b65a5abccb610411953d417ff" + integrity sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ== dependencies: "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.18" + "@types/express-serve-static-core" "^4.17.31" "@types/qs" "*" "@types/serve-static" "*" "@types/ffi-napi@^4.0.5": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@types/ffi-napi/-/ffi-napi-4.0.6.tgz#cd1c65cc9e701de664e640ccb17a2e823a674d44" - integrity sha512-yrBtqeVD1aeVo271jXVEo3iAtbzSGVGRssJv9W9JlUfg5Z5FgHJx2MV88GRwVATu/XWg6zyenW/cb1MNAuOtaQ== + version "4.0.7" + resolved "https://registry.yarnpkg.com/@types/ffi-napi/-/ffi-napi-4.0.7.tgz#b3a9beeae160c74adca801ca1c9defb1ec0a1a32" + integrity sha512-2CvLfgxCUUSj7qVab6/uFLyVpgVd2gEV4H/TQEHHn6kZTV8iTesz9uo0bckhwzsh71atutOv8P3JmvRX2ZvpZg== dependencies: "@types/node" "*" "@types/ref-napi" "*" @@ -2452,9 +2502,9 @@ integrity sha512-0sMBeFoqdGgdXoR/hgKYSWMpFufSpToosNsI2VgmkPqZJgeEXsXNu2hGr0FN401dBro2tNO5y2D6uw3UxVaxbg== "@types/graceful-fs@^4.1.2": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== + version "4.1.6" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== dependencies: "@types/node" "*" @@ -2499,7 +2549,7 @@ jest-diff "^26.0.0" pretty-format "^26.0.0" -"@types/json-schema@*", "@types/json-schema@^7.0.7": +"@types/json-schema@*", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== @@ -2537,10 +2587,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@^15.14.4": - version "15.14.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" - integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== +"@types/node@*", "@types/node@^16.11.7": + version "16.18.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.11.tgz#cbb15c12ca7c16c85a72b6bdc4d4b01151bb3cae" + integrity sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2558,9 +2608,9 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.1.tgz#dfd20e2dc35f027cdd6c1908e80a5ddc7499670e" - integrity sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow== + version "2.7.2" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" + integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== "@types/prop-types@*": version "15.7.5" @@ -2594,16 +2644,16 @@ csstype "^3.0.2" "@types/ref-napi@*", "@types/ref-napi@^3.0.4": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.5.tgz#8db441d381737af5c353d7dd89c7593b5f2080c8" - integrity sha512-u+L/RdwTuJes3pDypOVR/MtcqzoULu8Z8yulP6Tw5z7eXV1ba1llizNVFtI/m2iPfDy/dPPt+3ar1QCgonTzsw== + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.6.tgz#56f95b10e7698dced16e05b2bd10b6a46cf90f20" + integrity sha512-yLbSiZkLQB9Bv6m46+c4Gdv5Xmw34ehdUagQCfc88FvqHLamaGpYInHbFQ3+sawFonAQ0GDysQIEdZmSOmMh3A== dependencies: "@types/node" "*" "@types/ref-struct-di@*": - version "1.1.7" - resolved "https://registry.yarnpkg.com/@types/ref-struct-di/-/ref-struct-di-1.1.7.tgz#85e0149858a81a14f12f15ff31a6dffa42bab2d3" - integrity sha512-nnHR26qrCnQqxwHTv+rqzu/hGgDZl45TUs4bO6ZjpuC8/M2JoXFxk63xrWmAmqsLe55oxOgAWssyr3YHAMY89g== + version "1.1.8" + resolved "https://registry.yarnpkg.com/@types/ref-struct-di/-/ref-struct-di-1.1.8.tgz#df8cbf7b9bbbc03f476dcbe1958f92bf443f17d9" + integrity sha512-t5jwtHlEH6c3rgBRtMQTAtysROr1gWt/ZfcytolK+45dag747fUdgmZy/iQs5q41jinMnr62nxwI0Q8GkdK9TA== dependencies: "@types/ref-napi" "*" @@ -2612,6 +2662,11 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== +"@types/semver@^7.3.12": + version "7.3.13" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" + integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== + "@types/serve-static@*": version "1.15.0" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" @@ -2662,88 +2717,101 @@ integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== "@types/yargs@^15.0.0": - version "15.0.14" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" - integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== + version "15.0.15" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.15.tgz#e609a2b1ef9e05d90489c2f5f45bbfb2be092158" + integrity sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg== dependencies: "@types/yargs-parser" "*" "@types/yargs@^16.0.0": - version "16.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" - integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== + version "16.0.5" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.5.tgz#12cc86393985735a283e387936398c2f9e5f88e3" + integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ== dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^4.26.1": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" - integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg== +"@typescript-eslint/eslint-plugin@^5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.1.tgz#deee67e399f2cb6b4608c935777110e509d8018c" + integrity sha512-9nY5K1Rp2ppmpb9s9S2aBiF3xo5uExCehMDmYmmFqqyxgenbHJ3qbarcLt4ITgaD6r/2ypdlcFRdcuVPnks+fQ== dependencies: - "@typescript-eslint/experimental-utils" "4.33.0" - "@typescript-eslint/scope-manager" "4.33.0" - debug "^4.3.1" - functional-red-black-tree "^1.0.1" - ignore "^5.1.8" - regexpp "^3.1.0" - semver "^7.3.5" + "@typescript-eslint/scope-manager" "5.48.1" + "@typescript-eslint/type-utils" "5.48.1" + "@typescript-eslint/utils" "5.48.1" + debug "^4.3.4" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + regexpp "^3.2.0" + semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" - integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q== +"@typescript-eslint/parser@^5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.48.1.tgz#d0125792dab7e232035434ab8ef0658154db2f10" + integrity sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA== dependencies: - "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.33.0" - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/typescript-estree" "4.33.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" + "@typescript-eslint/scope-manager" "5.48.1" + "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/typescript-estree" "5.48.1" + debug "^4.3.4" -"@typescript-eslint/parser@^4.26.1": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" - integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== - dependencies: - "@typescript-eslint/scope-manager" "4.33.0" - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/typescript-estree" "4.33.0" - debug "^4.3.1" - -"@typescript-eslint/scope-manager@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" - integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== - dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" - -"@typescript-eslint/types@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" - integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== - -"@typescript-eslint/typescript-estree@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" - integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== - dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" - debug "^4.3.1" - globby "^11.0.3" - is-glob "^4.0.1" - semver "^7.3.5" +"@typescript-eslint/scope-manager@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz#39c71e4de639f5fe08b988005beaaf6d79f9d64d" + integrity sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ== + dependencies: + "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/visitor-keys" "5.48.1" + +"@typescript-eslint/type-utils@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz#5d94ac0c269a81a91ad77c03407cea2caf481412" + integrity sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ== + dependencies: + "@typescript-eslint/typescript-estree" "5.48.1" + "@typescript-eslint/utils" "5.48.1" + debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" - integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== +"@typescript-eslint/types@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.48.1.tgz#efd1913a9aaf67caf8a6e6779fd53e14e8587e14" + integrity sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg== + +"@typescript-eslint/typescript-estree@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz#9efa8ee2aa471c6ab62e649f6e64d8d121bc2056" + integrity sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA== dependencies: - "@typescript-eslint/types" "4.33.0" - eslint-visitor-keys "^2.0.0" + "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/visitor-keys" "5.48.1" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.48.1.tgz#20f2f4e88e9e2a0961cbebcb47a1f0f7da7ba7f9" + integrity sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA== + dependencies: + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.48.1" + "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/typescript-estree" "5.48.1" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz#79fd4fb9996023ef86849bf6f904f33eb6c8fccb" + integrity sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA== + dependencies: + "@typescript-eslint/types" "5.48.1" + eslint-visitor-keys "^3.3.0" "@unimodules/core@*": version "7.1.2" @@ -2871,9 +2939,9 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.11.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.2.tgz#aecb20b50607acf2569b6382167b65a96008bb78" - integrity sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg== + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -3052,7 +3120,7 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== -array-includes@^3.1.4: +array-includes@^3.1.6: version "3.1.6" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== @@ -3083,7 +3151,7 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== -array.prototype.flat@^1.2.5: +array.prototype.flat@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== @@ -3093,6 +3161,16 @@ array.prototype.flat@^1.2.5: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" +array.prototype.flatmap@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + array.prototype.reduce@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" @@ -3201,15 +3279,20 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + version "1.12.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" + integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== b64-lite@^1.3.1, b64-lite@^1.4.0: version "1.4.0" @@ -3406,9 +3489,9 @@ big-integer@1.6.x: integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== bignumber.js@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.0.tgz#8d340146107fe3a6cb8d40699643c302e8773b62" - integrity sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A== + version "9.1.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" + integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== bindings@^1.3.1: version "1.5.0" @@ -3663,9 +3746,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001400: - version "1.0.30001434" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz#ec1ec1cfb0a93a34a0600d37903853030520a4e5" - integrity sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA== + version "1.0.30001445" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz#cf2d4eb93f2bcdf0310de9dd6d18be271bc0b447" + integrity sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg== canonicalize@^1.0.1: version "1.0.8" @@ -3735,9 +3818,9 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.0.tgz#6d01b3696c59915b6ce057e4aa4adfc2fa25f5ef" - integrity sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog== + version "3.7.1" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.1.tgz#708a6cdae38915d597afdf3b145f2f8e1ff55f3f" + integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w== cjs-module-lexer@^1.0.0: version "1.2.2" @@ -4184,9 +4267,9 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.25.1: - version "3.26.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.26.1.tgz#0e710b09ebf689d719545ac36e49041850f943df" - integrity sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A== + version "3.27.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.27.1.tgz#b5695eb25c602d72b1d30cbfba3cb7e5e4cf0a67" + integrity sha512-Dg91JFeCDA17FKnneN7oCMz4BkQ4TcffkgHP4OWwp9yx3pi7ubqMDXXSacfNak1PQqjc95skyt+YBLHQJnkJwA== dependencies: browserslist "^4.21.4" @@ -4320,18 +4403,18 @@ dateformat@^3.0.0: integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== dayjs@^1.8.15: - version "1.11.6" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.6.tgz#2e79a226314ec3ec904e3ee1dd5a4f5e5b1c7afb" - integrity sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ== + version "1.11.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" + integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4364,11 +4447,11 @@ decamelize@^1.1.0, decamelize@^1.2.0: integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== decimal.js@^10.2.1: - version "10.4.2" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.2.tgz#0341651d1d997d86065a2ce3a441fbd0d8e8b98e" - integrity sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA== + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== -decode-uri-component@^0.2.0: +decode-uri-component@^0.2.0, decode-uri-component@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== @@ -4672,40 +4755,58 @@ errorhandler@^1.5.0: escape-html "~1.0.3" es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.20.4" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" - integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== + version "1.21.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" + integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== dependencies: + available-typed-arrays "^1.0.5" call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" function-bind "^1.1.1" function.prototype.name "^1.1.5" get-intrinsic "^1.1.3" get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" has "^1.0.3" has-property-descriptors "^1.0.0" + has-proto "^1.0.1" has-symbols "^1.0.3" - internal-slot "^1.0.3" + internal-slot "^1.0.4" + is-array-buffer "^3.0.1" is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" + is-typed-array "^1.1.10" is-weakref "^1.0.2" object-inspect "^1.12.2" object-keys "^1.1.1" object.assign "^4.1.4" regexp.prototype.flags "^1.4.3" safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" es-array-method-boxes-properly@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + es-shim-unscopables@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" @@ -4760,17 +4861,18 @@ escodegen@^2.0.0: source-map "~0.6.1" eslint-config-prettier@^8.3.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" - integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== + version "8.6.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz#dec1d29ab728f4fa63061774e1672ac4e363d207" + integrity sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA== -eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== +eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== dependencies: debug "^3.2.7" - resolve "^1.20.0" + is-core-module "^2.11.0" + resolve "^1.22.1" eslint-import-resolver-typescript@^2.4.0: version "2.7.1" @@ -4783,7 +4885,7 @@ eslint-import-resolver-typescript@^2.4.0: resolve "^1.22.0" tsconfig-paths "^3.14.1" -eslint-module-utils@^2.7.3: +eslint-module-utils@^2.7.4: version "2.7.4" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== @@ -4791,22 +4893,24 @@ eslint-module-utils@^2.7.3: debug "^3.2.7" eslint-plugin-import@^2.23.4: - version "2.26.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" - integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== + version "2.27.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.4.tgz#319c2f6f6580e1678d674a258ee5e981c10cc25b" + integrity sha512-Z1jVt1EGKia1X9CnBCkpAOhWy8FgQ7OmJ/IblEkT82yrFU/xJaxwujaTzLWqigewwynRQ9mmHfX9MtAfhxm0sA== dependencies: - array-includes "^3.1.4" - array.prototype.flat "^1.2.5" - debug "^2.6.9" + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.0" + debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.3" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" has "^1.0.3" - is-core-module "^2.8.1" + is-core-module "^2.11.0" is-glob "^4.0.3" minimatch "^3.1.2" - object.values "^1.1.5" - resolve "^1.22.0" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" tsconfig-paths "^3.14.1" eslint-plugin-prettier@^3.4.0: @@ -4848,6 +4952,11 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== +eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + eslint@^7.28.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" @@ -5168,9 +5277,9 @@ fast-text-encoding@^1.0.3: integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== dependencies: reusify "^1.0.4" @@ -5330,15 +5439,22 @@ flatted@^3.1.0: integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.193.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.193.0.tgz#8d705fc2d6b378a24bae189014f6f0320d040c4f" - integrity sha512-x7ZoArE1UO3Nk2rkq/KK/Tkp714QDMVzEsxIyK2+p7Alx+88LY7KgqmeQZuiAG8TCHucmYuHefbk3KsVFVjouA== + version "0.197.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.197.0.tgz#9a581ef7c0b1c3377b195cec0bbad794b88be67b" + integrity sha512-yhwkJPxH1JBg0aJunk/jVRy5p3UhVZBGkzL1hq/GK+GaBh6bKr2YKkv6gDuiufaw+i3pKWQgOLtD++1cvrgXLA== flow-parser@^0.121.0: version "0.121.0" resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.121.0.tgz#9f9898eaec91a9f7c323e9e992d81ab5c58e618f" integrity sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -5514,7 +5630,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== @@ -5668,13 +5784,20 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.18.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.18.0.tgz#fb224daeeb2bb7d254cd2c640f003528b8d0c1dc" - integrity sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A== + version "13.19.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" + integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== dependencies: type-fest "^0.20.2" -globby@^11.0.2, globby@^11.0.3: +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.0.2, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -5686,6 +5809,13 @@ globby@^11.0.2, globby@^11.0.3: merge2 "^1.4.1" slash "^3.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" @@ -5743,6 +5873,11 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" @@ -5888,6 +6023,11 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +husky@^7.0.1: + version "7.0.4" + resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" + integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== + iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -5919,10 +6059,10 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.8, ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +ignore@^5.2.0: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== image-size@^0.6.0: version "0.6.3" @@ -6039,12 +6179,12 @@ inquirer@^7.3.3: strip-ansi "^6.0.0" through "^2.3.6" -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== +internal-slot@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" + integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== dependencies: - get-intrinsic "^1.1.0" + get-intrinsic "^1.1.3" has "^1.0.3" side-channel "^1.0.4" @@ -6089,6 +6229,15 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-array-buffer@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" + integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -6114,7 +6263,7 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.2.7: +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== @@ -6126,7 +6275,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: +is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.9.0: version "2.11.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== @@ -6337,6 +6486,17 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" +is-typed-array@^1.1.10, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -7087,10 +7247,10 @@ json-text-sequence@~0.3.0: dependencies: "@sovpro/delimited-stream" "^1.1.0" -json5@2.x, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json5@2.x, json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== json5@^1.0.1: version "1.0.2" @@ -7262,15 +7422,10 @@ libnpmpublish@^4.0.0: semver "^7.1.3" ssri "^8.0.1" -libphonenumber-js@^1.10.14: - version "1.10.17" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.17.tgz#7efcfa3068fc076bc59a43a08a723ccd95308474" - integrity sha512-UQrNzsusSn5qaojdpWqporWRdpx6AGeb+egj64NrpYuyKHvnSH9jMp/1Dy3b/WnMyJA5zgV1yw//jC6J0dCXkw== - -libphonenumber-js@^1.9.7: - version "1.10.14" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.14.tgz#e29da7f539751f724ac54017a098e3c7ca23de94" - integrity sha512-McGS7GV/WjJ2KjfOGhJU1oJn29RYeo7Q+RpANRbUNMQ9gj5XArpbjurSuyYPTejFwbaUojstQ4XyWCrAzGOUXw== +libphonenumber-js@^1.10.14, libphonenumber-js@^1.9.7: + version "1.10.18" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.18.tgz#657c419071c8a02c638c0e80d9ee1232f152f280" + integrity sha512-NS4ZEgNhwbcPz1gfSXCGFnQm0xEiyTSPRthIuWytDzOiEG9xnZ2FbLyfJC4tI2BMAAXpoWbNxHYH75pa3Dq9og== lines-and-columns@^1.1.6: version "1.2.4" @@ -7410,6 +7565,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -7975,6 +8137,13 @@ minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: dependencies: yallist "^4.0.0" +minipass@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.0.0.tgz#7cebb0f9fa7d56f0c5b17853cbe28838a8dbbd3b" + integrity sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw== + dependencies: + yallist "^4.0.0" + minizlib@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" @@ -8087,6 +8256,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -8173,7 +8347,7 @@ node-dir@^0.1.17: dependencies: minimatch "^3.0.2" -node-fetch@2.6.7, node-fetch@^2.0, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -8188,10 +8362,17 @@ node-fetch@3.0.0-beta.9: data-uri-to-buffer "^3.0.1" fetch-blob "^2.1.1" +node-fetch@^2.0, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: + version "2.6.8" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.8.tgz#a68d30b162bc1d8fd71a367e81b997e1f4d4937e" + integrity sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg== + dependencies: + whatwg-url "^5.0.0" + node-gyp-build@^4.2.1: - version "4.5.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + version "4.6.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" + integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== node-gyp@^5.0.2: version "5.1.1" @@ -8264,9 +8445,9 @@ node-pre-gyp@0.17.0: tar "^4.4.13" node-releases@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" - integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== + version "2.0.8" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" + integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A== node-stream-zip@^1.9.1: version "1.15.0" @@ -8506,9 +8687,9 @@ object-copy@^0.1.0: kind-of "^3.0.3" object-inspect@^1.10.3, object-inspect@^1.12.2, object-inspect@^1.9.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== object-keys@^1.1.1: version "1.1.1" @@ -8549,7 +8730,7 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.5: +object.values@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== @@ -8989,9 +9170,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.8.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.0.tgz#c7df58393c9ba77d6fba3921ae01faf994fb9dc9" - integrity sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA== + version "2.8.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.3.tgz#ab697b1d3dd46fb4626fbe2f543afe0cc98d8632" + integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" @@ -9103,9 +9284,9 @@ pump@^3.0.0: once "^1.3.1" punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + version "2.2.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.2.0.tgz#2092cc57cd2582c38e4e7e8bb869dc8d3148bc74" + integrity sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw== pvtsutils@^1.3.2: version "1.3.2" @@ -9147,11 +9328,11 @@ query-string@^6.13.8: strict-uri-encode "^2.0.0" query-string@^7.0.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.1.tgz#754620669db978625a90f635f12617c271a088e1" - integrity sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w== + version "7.1.3" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" + integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg== dependencies: - decode-uri-component "^0.2.0" + decode-uri-component "^0.2.2" filter-obj "^1.1.0" split-on-first "^1.0.0" strict-uri-encode "^2.0.0" @@ -9197,9 +9378,9 @@ rc@^1.2.8: strip-json-comments "~2.0.1" react-devtools-core@^4.6.0: - version "4.26.1" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.26.1.tgz#2893fea58089be64c5356d5bd0eebda8d1bbf317" - integrity sha512-r1csa5n9nABVpSdAadwTG7K+SfgRJPc/Hdx89BkV5IlA1mEGgGi3ir630ST5D/xYlJQaY3VE75YGADgpNW7HIw== + version "4.27.1" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.1.tgz#167aa174383c65786cbb7e965a5b39c702f0a2d3" + integrity sha512-qXhcxxDWiFmFAOq48jts9YQYe1+wVoUXzJTlY4jbaATzyio6dd6CUGu3dXBhREeVgpZ+y4kg6vFJzIOZh6vY2w== dependencies: shell-quote "^1.6.1" ws "^7" @@ -9487,12 +9668,12 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.10, regenerator-runtime@^0.13.2: +regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.2: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regenerator-transform@^0.15.0: +regenerator-transform@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== @@ -9516,12 +9697,12 @@ regexp.prototype.flags@^1.4.3: define-properties "^1.1.3" functions-have-names "^1.2.2" -regexpp@^3.1.0: +regexpp@^3.1.0, regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regexpu-core@^5.1.0: +regexpu-core@^5.2.1: version "5.2.2" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.2.tgz#3e4e5d12103b64748711c3aad69934d7718e75fc" integrity sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw== @@ -9634,11 +9815,11 @@ resolve-url@^0.2.1: integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== + version "1.1.1" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" + integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -9690,13 +9871,18 @@ rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1: dependencies: glob "^7.1.3" -rimraf@^3.0.0, rimraf@^3.0.2, rimraf@~3.0.2: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" +rimraf@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.0.7.tgz#f438c7d6a2d5e5cca1d81e3904a48ac7b053a542" + integrity sha512-CUEDDrZvc0swDgVdXGiv3FcYYQMpJxjvSGt85Amj6yU+MCVWurrLCeLiJDdJPHCzNJnwuebBEdcO//eP11Xa7w== + rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" @@ -9734,9 +9920,9 @@ rxjs@^6.6.0: tslib "^1.9.0" rxjs@^7.2.0: - version "7.5.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39" - integrity sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA== + version "7.8.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" + integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== dependencies: tslib "^2.1.0" @@ -9811,7 +9997,7 @@ scheduler@^0.20.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== @@ -10266,7 +10452,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.trimend@^1.0.5: +string.prototype.trimend@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== @@ -10275,7 +10461,7 @@ string.prototype.trimend@^1.0.5: define-properties "^1.1.4" es-abstract "^1.20.4" -string.prototype.trimstart@^1.0.5: +string.prototype.trimstart@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== @@ -10444,13 +10630,13 @@ tar@^4.4.12, tar@^4.4.13: yallist "^3.1.1" tar@^6.0.2, tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: - version "6.1.12" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.12.tgz#3b742fb05669b55671fb769ab67a7791ea1a62e6" - integrity sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw== + version "6.1.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" + integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" - minipass "^3.0.0" + minipass "^4.0.0" minizlib "^2.1.1" mkdirp "^1.0.3" yallist "^4.0.0" @@ -10519,9 +10705,9 @@ throat@^5.0.0: integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== throat@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" - integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== + version "6.0.2" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" + integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== through2@^2.0.0, through2@^2.0.1: version "2.0.5" @@ -10675,7 +10861,7 @@ ts-typed-json@^0.3.2: resolved "https://registry.yarnpkg.com/ts-typed-json/-/ts-typed-json-0.3.2.tgz#f4f20f45950bae0a383857f7b0a94187eca1b56a" integrity sha512-Tdu3BWzaer7R5RvBIJcg9r8HrTZgpJmsX+1meXMJzYypbkj8NK2oJN0yvm4Dp/Iv6tzFa/L5jKRmEVTga6K3nA== -tsconfig-paths@^3.14.1, tsconfig-paths@^3.9.0: +tsconfig-paths@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== @@ -10685,6 +10871,15 @@ tsconfig-paths@^3.14.1, tsconfig-paths@^3.9.0: minimist "^1.2.6" strip-bom "^3.0.0" +tsconfig-paths@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz#4819f861eef82e6da52fb4af1e8c930a39ed979a" + integrity sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -10790,6 +10985,15 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -10802,10 +11006,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@~4.3.0: - version "4.3.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" - integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== +typescript@~4.9.4: + version "4.9.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" + integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== typical@^4.0.0: version "4.0.0" @@ -11204,6 +11408,18 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -11383,7 +11599,7 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@^3.0.0, yallist@^3.1.1: +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== From 4a572fedd7508aeba3e91f4d1d98b9a177941648 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 18 Jan 2023 18:21:40 +0000 Subject: [PATCH 014/139] chore(release): v0.3.3 (#1217) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 15 +++++++++ lerna.json | 2 +- packages/action-menu/CHANGELOG.md | 10 ++++++ packages/action-menu/package.json | 6 ++-- packages/anoncreds/package.json | 4 +-- packages/core/CHANGELOG.md | 11 +++++++ packages/core/package.json | 2 +- packages/indy-sdk/package.json | 6 ++-- packages/node/CHANGELOG.md | 6 ++++ packages/node/package.json | 4 +-- packages/openid4vc-client/package.json | 6 ++-- packages/question-answer/CHANGELOG.md | 10 ++++++ packages/question-answer/package.json | 8 ++--- packages/react-native/CHANGELOG.md | 10 ++++++ packages/react-native/package.json | 4 +-- packages/tenants/CHANGELOG.md | 6 ++++ packages/tenants/package.json | 6 ++-- yarn.lock | 44 -------------------------- 18 files changed, 92 insertions(+), 68 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50748153df..0d2a624f28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) +- **openid4vc-client:** set package to private ([#1210](https://github.com/hyperledger/aries-framework-javascript/issues/1210)) ([c697716](https://github.com/hyperledger/aries-framework-javascript/commit/c697716bf1837b9fef307f60ff97f01d3d926728)) + +### Features + +- add anoncreds package ([#1118](https://github.com/hyperledger/aries-framework-javascript/issues/1118)) ([adba83d](https://github.com/hyperledger/aries-framework-javascript/commit/adba83d8df176288083969f2c3f975bbfc1acd9c)) +- add minimal oidc-client package ([#1197](https://github.com/hyperledger/aries-framework-javascript/issues/1197)) ([b6f89f9](https://github.com/hyperledger/aries-framework-javascript/commit/b6f89f943dc4417626f868ac9f43a3d890ab62c6)) +- adding trust ping events and trust ping command ([#1182](https://github.com/hyperledger/aries-framework-javascript/issues/1182)) ([fd006f2](https://github.com/hyperledger/aries-framework-javascript/commit/fd006f262a91f901e7f8a9c6e6882ea178230005)) +- **anoncreds:** add anoncreds registry service ([#1204](https://github.com/hyperledger/aries-framework-javascript/issues/1204)) ([86647e7](https://github.com/hyperledger/aries-framework-javascript/commit/86647e7f55c9a362f6ab500538c4de2112e42206)) +- **indy-sdk:** add indy-sdk package ([#1200](https://github.com/hyperledger/aries-framework-javascript/issues/1200)) ([9933b35](https://github.com/hyperledger/aries-framework-javascript/commit/9933b35a6aa4524caef8a885e71b742cd0d7186b)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) ### Bug Fixes diff --git a/lerna.json b/lerna.json index 56d3a038a2..b8106b0a8a 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.3.2", + "version": "0.3.3", "useWorkspaces": true, "npmClient": "yarn", "command": { diff --git a/packages/action-menu/CHANGELOG.md b/packages/action-menu/CHANGELOG.md index c3167eb95d..a18e55cd11 100644 --- a/packages/action-menu/CHANGELOG.md +++ b/packages/action-menu/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) + +### Features + +- **indy-sdk:** add indy-sdk package ([#1200](https://github.com/hyperledger/aries-framework-javascript/issues/1200)) ([9933b35](https://github.com/hyperledger/aries-framework-javascript/commit/9933b35a6aa4524caef8a885e71b742cd0d7186b)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) **Note:** Version bump only for package @aries-framework/action-menu diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index e4b54a744a..9ee1173bdc 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/action-menu", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], @@ -24,10 +24,10 @@ "test": "jest" }, "dependencies": { + "@aries-framework/core": "0.3.3", "class-transformer": "0.5.1", "class-validator": "0.13.1", - "rxjs": "^7.2.0", - "@aries-framework/core": "0.3.2" + "rxjs": "^7.2.0" }, "devDependencies": { "reflect-metadata": "^0.1.13", diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 473c115b01..7947fe5642 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/anoncreds", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], @@ -25,7 +25,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.2" + "@aries-framework/core": "0.3.3" }, "devDependencies": { "rimraf": "^4.0.7", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 341ff76014..8f07830b5f 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) + +### Features + +- adding trust ping events and trust ping command ([#1182](https://github.com/hyperledger/aries-framework-javascript/issues/1182)) ([fd006f2](https://github.com/hyperledger/aries-framework-javascript/commit/fd006f262a91f901e7f8a9c6e6882ea178230005)) +- **indy-sdk:** add indy-sdk package ([#1200](https://github.com/hyperledger/aries-framework-javascript/issues/1200)) ([9933b35](https://github.com/hyperledger/aries-framework-javascript/commit/9933b35a6aa4524caef8a885e71b742cd0d7186b)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) ### Bug Fixes diff --git a/packages/core/package.json b/packages/core/package.json index 971091140e..8a54af7192 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/core", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 02dec486ae..e29cfe6020 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/indy-sdk", "main": "build/index", "types": "build/index", - "version": "0.2.5", + "version": "0.3.3", "private": true, "files": [ "build" @@ -25,8 +25,8 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "0.3.2", - "@aries-framework/core": "0.3.2", + "@aries-framework/anoncreds": "0.3.3", + "@aries-framework/core": "0.3.3", "@types/indy-sdk": "1.16.24", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index a3f6e278ce..32ecc62fb0 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) **Note:** Version bump only for package @aries-framework/node diff --git a/packages/node/package.json b/packages/node/package.json index 09f32cc942..665472421c 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/node", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build", "bin" @@ -28,7 +28,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.2", + "@aries-framework/core": "0.3.3", "@types/express": "^4.17.15", "express": "^4.17.1", "ffi-napi": "^4.0.3", diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index 7b251a86b3..3f7a671015 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/openid4vc-client", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], @@ -25,14 +25,14 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.2", + "@aries-framework/core": "0.3.3", "@sphereon/openid4vci-client": "^0.3.6", "class-transformer": "0.5.1", "class-validator": "0.13.1" }, "peerDependencies": {}, "devDependencies": { - "@aries-framework/node": "0.3.2", + "@aries-framework/node": "0.3.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/question-answer/CHANGELOG.md b/packages/question-answer/CHANGELOG.md index 216388c0ea..dc3f65cf84 100644 --- a/packages/question-answer/CHANGELOG.md +++ b/packages/question-answer/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) + +### Features + +- **indy-sdk:** add indy-sdk package ([#1200](https://github.com/hyperledger/aries-framework-javascript/issues/1200)) ([9933b35](https://github.com/hyperledger/aries-framework-javascript/commit/9933b35a6aa4524caef8a885e71b742cd0d7186b)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) **Note:** Version bump only for package @aries-framework/question-answer diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 547a9a3f2c..1640e283cd 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/question-answer", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], @@ -24,13 +24,13 @@ "test": "jest" }, "dependencies": { + "@aries-framework/core": "0.3.3", "class-transformer": "0.5.1", "class-validator": "0.13.1", - "rxjs": "^7.2.0", - "@aries-framework/core": "0.3.2" + "rxjs": "^7.2.0" }, "devDependencies": { - "@aries-framework/node": "0.3.2", + "@aries-framework/node": "0.3.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 092f793c6d..c0ff907f14 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) + +### Features + +- **indy-sdk:** add indy-sdk package ([#1200](https://github.com/hyperledger/aries-framework-javascript/issues/1200)) ([9933b35](https://github.com/hyperledger/aries-framework-javascript/commit/9933b35a6aa4524caef8a885e71b742cd0d7186b)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) **Note:** Version bump only for package @aries-framework/react-native diff --git a/packages/react-native/package.json b/packages/react-native/package.json index e2b1b02691..5eee471421 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/react-native", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], @@ -24,7 +24,7 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.2", + "@aries-framework/core": "0.3.3", "@azure/core-asynciterator-polyfill": "^1.0.0", "events": "^3.3.0" }, diff --git a/packages/tenants/CHANGELOG.md b/packages/tenants/CHANGELOG.md index a73651ba84..d3f33ffa92 100644 --- a/packages/tenants/CHANGELOG.md +++ b/packages/tenants/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18) + +### Bug Fixes + +- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444)) + ## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04) ### Bug Fixes diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 2c4ad8faa9..13f1f22438 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -2,7 +2,7 @@ "name": "@aries-framework/tenants", "main": "build/index", "types": "build/index", - "version": "0.3.2", + "version": "0.3.3", "files": [ "build" ], @@ -24,11 +24,11 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.2", + "@aries-framework/core": "0.3.3", "async-mutex": "^0.3.2" }, "devDependencies": { - "@aries-framework/node": "0.3.2", + "@aries-framework/node": "0.3.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/yarn.lock b/yarn.lock index 359ab25c51..c79dff1e1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,50 +10,6 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@aries-framework/core@file:packages/core": - version "0.3.2" - dependencies: - "@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/indy-sdk" "1.16.24" - "@types/node-fetch" "^2.5.10" - "@types/ws" "^7.4.6" - abort-controller "^3.0.0" - bn.js "^5.2.0" - borc "^3.0.0" - buffer "^6.0.3" - class-transformer "0.5.1" - class-validator "0.13.1" - did-resolver "^3.1.3" - lru_map "^0.4.1" - luxon "^1.27.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.7.0" - uuid "^8.3.2" - varint "^6.0.0" - web-did-resolver "^2.0.8" - -"@aries-framework/node@file:packages/node": - version "0.3.2" - dependencies: - "@aries-framework/core" "0.3.2" - "@types/express" "^4.17.15" - express "^4.17.1" - ffi-napi "^4.0.3" - indy-sdk "^1.16.0-dev-1636" - node-fetch "^2.6.1" - ref-napi "^3.0.3" - ws "^7.5.3" - "@azure/core-asynciterator-polyfill@^1.0.0": version "1.0.2" resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" From 25b2bcf81648100b572784e4489a288cc9da0557 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 21 Jan 2023 03:20:49 +0800 Subject: [PATCH 015/139] feat(cache): add caching interface (#1229) Signed-off-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 2 - packages/core/src/agent/AgentModules.ts | 2 + .../src/agent/__tests__/AgentModules.test.ts | 4 + packages/core/src/cache/CacheRecord.ts | 41 ----- packages/core/src/cache/CacheRepository.ts | 17 -- packages/core/src/cache/PersistedLruCache.ts | 75 --------- .../cache/__tests__/PersistedLruCache.test.ts | 73 -------- packages/core/src/cache/index.ts | 3 - packages/core/src/index.ts | 2 +- packages/core/src/modules/cache/Cache.ts | 7 + .../core/src/modules/cache/CacheModule.ts | 34 ++++ .../src/modules/cache/CacheModuleConfig.ts | 29 ++++ .../src/modules/cache/InMemoryLruCache.ts | 71 ++++++++ .../cache/__tests__/CacheModule.test.ts | 42 +++++ .../cache/__tests__/CacheModuleConfig.test.ts | 14 ++ .../cache/__tests__/InMemoryLruCache.test.ts | 43 +++++ packages/core/src/modules/cache/index.ts | 10 ++ .../SingleContextLruCacheRecord.ts | 41 +++++ .../SingleContextLruCacheRepository.ts | 17 ++ .../SingleContextStorageLruCache.ts | 158 ++++++++++++++++++ .../SingleContextStorageLruCache.test.ts | 91 ++++++++++ .../cache/singleContextLruCache/index.ts | 1 + .../ledger/__tests__/IndyPoolService.test.ts | 59 ++----- .../ledger/services/IndyPoolService.ts | 15 +- packages/indy-sdk/jest.config.ts | 2 +- .../indy-sdk/src/ledger/IndySdkPoolService.ts | 21 +-- .../__tests__/IndySdkPoolService.test.ts | 60 ++----- packages/indy-sdk/tests/setup.ts | 1 + 28 files changed, 607 insertions(+), 328 deletions(-) delete mode 100644 packages/core/src/cache/CacheRecord.ts delete mode 100644 packages/core/src/cache/CacheRepository.ts delete mode 100644 packages/core/src/cache/PersistedLruCache.ts delete mode 100644 packages/core/src/cache/__tests__/PersistedLruCache.test.ts delete mode 100644 packages/core/src/cache/index.ts create mode 100644 packages/core/src/modules/cache/Cache.ts create mode 100644 packages/core/src/modules/cache/CacheModule.ts create mode 100644 packages/core/src/modules/cache/CacheModuleConfig.ts create mode 100644 packages/core/src/modules/cache/InMemoryLruCache.ts create mode 100644 packages/core/src/modules/cache/__tests__/CacheModule.test.ts create mode 100644 packages/core/src/modules/cache/__tests__/CacheModuleConfig.test.ts create mode 100644 packages/core/src/modules/cache/__tests__/InMemoryLruCache.test.ts create mode 100644 packages/core/src/modules/cache/index.ts create mode 100644 packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts create mode 100644 packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRepository.ts create mode 100644 packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts create mode 100644 packages/core/src/modules/cache/singleContextLruCache/__tests__/SingleContextStorageLruCache.test.ts create mode 100644 packages/core/src/modules/cache/singleContextLruCache/index.ts create mode 100644 packages/indy-sdk/tests/setup.ts diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index a3a6b11ab1..2909c3536d 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -9,7 +9,6 @@ import type { Subscription } from 'rxjs' import { Subject } from 'rxjs' import { concatMap, takeUntil } from 'rxjs/operators' -import { CacheRepository } from '../cache' import { InjectionSymbols } from '../constants' import { SigningProviderToken } from '../crypto' import { JwsService } from '../crypto/JwsService' @@ -59,7 +58,6 @@ export class Agent extends BaseAge dependencyManager.registerSingleton(EnvelopeService) dependencyManager.registerSingleton(FeatureRegistry) dependencyManager.registerSingleton(JwsService) - dependencyManager.registerSingleton(CacheRepository) dependencyManager.registerSingleton(DidCommMessageRepository) dependencyManager.registerSingleton(StorageVersionRepository) dependencyManager.registerSingleton(StorageUpdateService) diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 4bb5cc4067..3f9512bdba 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -4,6 +4,7 @@ import type { IsAny } from '../types' import type { Constructor } from '../utils/mixins' import { BasicMessagesModule } from '../modules/basic-messages' +import { CacheModule } from '../modules/cache' import { ConnectionsModule } from '../modules/connections' import { CredentialsModule } from '../modules/credentials' import { DidsModule } from '../modules/dids' @@ -157,6 +158,7 @@ function getDefaultAgentModules(agentConfig: AgentConfig) { oob: () => new OutOfBandModule(), indy: () => new IndyModule(), w3cVc: () => new W3cVcModule(), + cache: () => new CacheModule(), } as const } diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts index cfb88ab7b0..60755c487e 100644 --- a/packages/core/src/agent/__tests__/AgentModules.test.ts +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -2,6 +2,7 @@ import type { Module } from '../../plugins' import { getAgentConfig } from '../../../tests/helpers' import { BasicMessagesModule } from '../../modules/basic-messages' +import { CacheModule } from '../../modules/cache' import { ConnectionsModule } from '../../modules/connections' import { CredentialsModule } from '../../modules/credentials' import { DidsModule } from '../../modules/dids' @@ -72,6 +73,7 @@ describe('AgentModules', () => { oob: expect.any(OutOfBandModule), indy: expect.any(IndyModule), w3cVc: expect.any(W3cVcModule), + cache: expect.any(CacheModule), }) }) @@ -96,6 +98,7 @@ describe('AgentModules', () => { oob: expect.any(OutOfBandModule), indy: expect.any(IndyModule), w3cVc: expect.any(W3cVcModule), + cache: expect.any(CacheModule), myModule, }) }) @@ -123,6 +126,7 @@ describe('AgentModules', () => { oob: expect.any(OutOfBandModule), indy: expect.any(IndyModule), w3cVc: expect.any(W3cVcModule), + cache: expect.any(CacheModule), myModule, }) }) diff --git a/packages/core/src/cache/CacheRecord.ts b/packages/core/src/cache/CacheRecord.ts deleted file mode 100644 index 26388d1706..0000000000 --- a/packages/core/src/cache/CacheRecord.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { RecordTags, TagsBase } from '../storage/BaseRecord' - -import { BaseRecord } from '../storage/BaseRecord' -import { uuid } from '../utils/uuid' - -export type CustomCacheTags = TagsBase -export type DefaultCacheTags = TagsBase - -export type CacheTags = RecordTags - -export interface CacheStorageProps { - id?: string - createdAt?: Date - tags?: CustomCacheTags - - entries: Array<{ key: string; value: unknown }> -} - -export class CacheRecord extends BaseRecord { - public entries!: Array<{ key: string; value: unknown }> - - public static readonly type = 'CacheRecord' - public readonly type = CacheRecord.type - - public constructor(props: CacheStorageProps) { - super() - - if (props) { - this.id = props.id ?? uuid() - this.createdAt = props.createdAt ?? new Date() - this.entries = props.entries - this._tags = props.tags ?? {} - } - } - - public getTags() { - return { - ...this._tags, - } - } -} diff --git a/packages/core/src/cache/CacheRepository.ts b/packages/core/src/cache/CacheRepository.ts deleted file mode 100644 index 3adb2e4fd2..0000000000 --- a/packages/core/src/cache/CacheRepository.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { EventEmitter } from '../agent/EventEmitter' -import { InjectionSymbols } from '../constants' -import { inject, injectable } from '../plugins' -import { Repository } from '../storage/Repository' -import { StorageService } from '../storage/StorageService' - -import { CacheRecord } from './CacheRecord' - -@injectable() -export class CacheRepository extends Repository { - public constructor( - @inject(InjectionSymbols.StorageService) storageService: StorageService, - eventEmitter: EventEmitter - ) { - super(CacheRecord, storageService, eventEmitter) - } -} diff --git a/packages/core/src/cache/PersistedLruCache.ts b/packages/core/src/cache/PersistedLruCache.ts deleted file mode 100644 index bb94c41bee..0000000000 --- a/packages/core/src/cache/PersistedLruCache.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { CacheRepository } from './CacheRepository' -import type { AgentContext } from '../agent' - -import { LRUMap } from 'lru_map' - -import { CacheRecord } from './CacheRecord' - -export class PersistedLruCache { - private cacheId: string - private limit: number - private _cache?: LRUMap - private cacheRepository: CacheRepository - - public constructor(cacheId: string, limit: number, cacheRepository: CacheRepository) { - this.cacheId = cacheId - this.limit = limit - this.cacheRepository = cacheRepository - } - - public async get(agentContext: AgentContext, key: string) { - const cache = await this.getCache(agentContext) - - return cache.get(key) - } - - public async set(agentContext: AgentContext, key: string, value: CacheValue) { - const cache = await this.getCache(agentContext) - - cache.set(key, value) - await this.persistCache(agentContext) - } - - private async getCache(agentContext: AgentContext) { - if (!this._cache) { - const cacheRecord = await this.fetchCacheRecord(agentContext) - this._cache = this.lruFromRecord(cacheRecord) - } - - return this._cache - } - - private lruFromRecord(cacheRecord: CacheRecord) { - return new LRUMap( - this.limit, - cacheRecord.entries.map((e) => [e.key, e.value as CacheValue]) - ) - } - - private async fetchCacheRecord(agentContext: AgentContext) { - let cacheRecord = await this.cacheRepository.findById(agentContext, this.cacheId) - - if (!cacheRecord) { - cacheRecord = new CacheRecord({ - id: this.cacheId, - entries: [], - }) - - await this.cacheRepository.save(agentContext, cacheRecord) - } - - return cacheRecord - } - - private async persistCache(agentContext: AgentContext) { - const cache = await this.getCache(agentContext) - - await this.cacheRepository.update( - agentContext, - new CacheRecord({ - entries: cache.toJSON(), - id: this.cacheId, - }) - ) - } -} diff --git a/packages/core/src/cache/__tests__/PersistedLruCache.test.ts b/packages/core/src/cache/__tests__/PersistedLruCache.test.ts deleted file mode 100644 index c7b893108d..0000000000 --- a/packages/core/src/cache/__tests__/PersistedLruCache.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { getAgentContext, mockFunction } from '../../../tests/helpers' -import { CacheRecord } from '../CacheRecord' -import { CacheRepository } from '../CacheRepository' -import { PersistedLruCache } from '../PersistedLruCache' - -jest.mock('../CacheRepository') -const CacheRepositoryMock = CacheRepository as jest.Mock - -const agentContext = getAgentContext() - -describe('PersistedLruCache', () => { - let cacheRepository: CacheRepository - let cache: PersistedLruCache - - beforeEach(() => { - cacheRepository = new CacheRepositoryMock() - mockFunction(cacheRepository.findById).mockResolvedValue(null) - - cache = new PersistedLruCache('cacheId', 2, cacheRepository) - }) - - it('should return the value from the persisted record', async () => { - const findMock = mockFunction(cacheRepository.findById).mockResolvedValue( - new CacheRecord({ - id: 'cacheId', - entries: [ - { - key: 'test', - value: 'somevalue', - }, - ], - }) - ) - - expect(await cache.get(agentContext, 'doesnotexist')).toBeUndefined() - expect(await cache.get(agentContext, 'test')).toBe('somevalue') - expect(findMock).toHaveBeenCalledWith(agentContext, 'cacheId') - }) - - it('should set the value in the persisted record', async () => { - const updateMock = mockFunction(cacheRepository.update).mockResolvedValue() - - await cache.set(agentContext, 'test', 'somevalue') - const [[, cacheRecord]] = updateMock.mock.calls - - expect(cacheRecord.entries.length).toBe(1) - expect(cacheRecord.entries[0].key).toBe('test') - expect(cacheRecord.entries[0].value).toBe('somevalue') - - expect(await cache.get(agentContext, 'test')).toBe('somevalue') - }) - - it('should remove least recently used entries if entries are added that exceed the limit', async () => { - // Set first value in cache, resolves fine - await cache.set(agentContext, 'one', 'valueone') - expect(await cache.get(agentContext, 'one')).toBe('valueone') - - // Set two more entries in the cache. Third item - // exceeds limit, so first item gets removed - await cache.set(agentContext, 'two', 'valuetwo') - await cache.set(agentContext, 'three', 'valuethree') - expect(await cache.get(agentContext, 'one')).toBeUndefined() - expect(await cache.get(agentContext, 'two')).toBe('valuetwo') - expect(await cache.get(agentContext, 'three')).toBe('valuethree') - - // Get two from the cache, meaning three will be removed first now - // because it is not recently used - await cache.get(agentContext, 'two') - await cache.set(agentContext, 'four', 'valuefour') - expect(await cache.get(agentContext, 'three')).toBeUndefined() - expect(await cache.get(agentContext, 'two')).toBe('valuetwo') - }) -}) diff --git a/packages/core/src/cache/index.ts b/packages/core/src/cache/index.ts deleted file mode 100644 index dab23e81d6..0000000000 --- a/packages/core/src/cache/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './PersistedLruCache' -export * from './CacheRecord' -export * from './CacheRepository' diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 5b2eaf1762..e2b1665a2a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -57,6 +57,7 @@ export * from './modules/routing' export * from './modules/oob' export * from './modules/dids' export * from './modules/vc' +export * from './modules/cache' export { JsonEncoder, JsonTransformer, isJsonObject, isValidJweStructure, TypedArrayEncoder, Buffer } from './utils' export * from './logger' export * from './error' @@ -65,7 +66,6 @@ export { parseMessageType, IsValidMessageType } from './utils/messageType' export type { Constructor } from './utils/mixins' export * from './agent/Events' export * from './crypto/' -export { PersistedLruCache, CacheRepository } from './cache' import { parseInvitationUrl } from './utils/parseInvitation' import { uuid } from './utils/uuid' diff --git a/packages/core/src/modules/cache/Cache.ts b/packages/core/src/modules/cache/Cache.ts new file mode 100644 index 0000000000..546e03925d --- /dev/null +++ b/packages/core/src/modules/cache/Cache.ts @@ -0,0 +1,7 @@ +import type { AgentContext } from '../../agent/context' + +export interface Cache { + get(agentContext: AgentContext, key: string): Promise + set(agentContext: AgentContext, key: string, value: CacheValue, expiresInSeconds?: number): Promise + remove(agentContext: AgentContext, key: string): Promise +} diff --git a/packages/core/src/modules/cache/CacheModule.ts b/packages/core/src/modules/cache/CacheModule.ts new file mode 100644 index 0000000000..c4d2ba0e5c --- /dev/null +++ b/packages/core/src/modules/cache/CacheModule.ts @@ -0,0 +1,34 @@ +import type { CacheModuleConfigOptions } from './CacheModuleConfig' +import type { DependencyManager, Module } from '../../plugins' +import type { Optional } from '../../utils' + +import { CacheModuleConfig } from './CacheModuleConfig' +import { SingleContextLruCacheRepository } from './singleContextLruCache/SingleContextLruCacheRepository' +import { SingleContextStorageLruCache } from './singleContextLruCache/SingleContextStorageLruCache' + +// CacheModuleOptions makes the credentialProtocols property optional from the config, as it will set it when not provided. +export type CacheModuleOptions = Optional + +export class CacheModule implements Module { + public readonly config: CacheModuleConfig + + public constructor(config?: CacheModuleOptions) { + this.config = new CacheModuleConfig({ + ...config, + cache: + config?.cache ?? + new SingleContextStorageLruCache({ + limit: 500, + }), + }) + } + + public register(dependencyManager: DependencyManager) { + dependencyManager.registerInstance(CacheModuleConfig, this.config) + + // Custom handling for when we're using the SingleContextStorageLruCache + if (this.config.cache instanceof SingleContextStorageLruCache) { + dependencyManager.registerSingleton(SingleContextLruCacheRepository) + } + } +} diff --git a/packages/core/src/modules/cache/CacheModuleConfig.ts b/packages/core/src/modules/cache/CacheModuleConfig.ts new file mode 100644 index 0000000000..a04b143dc8 --- /dev/null +++ b/packages/core/src/modules/cache/CacheModuleConfig.ts @@ -0,0 +1,29 @@ +import type { Cache } from './Cache' + +/** + * CacheModuleConfigOptions defines the interface for the options of the CacheModuleConfig class. + */ +export interface CacheModuleConfigOptions { + /** + * Implementation of the {@link Cache} interface. + * + * NOTE: Starting from AFJ 0.4.0 the default cache implementation will be {@link InMemoryLruCache} + * @default SingleContextStorageLruCache - with a limit of 500 + * + * + */ + cache: Cache +} + +export class CacheModuleConfig { + private options: CacheModuleConfigOptions + + public constructor(options: CacheModuleConfigOptions) { + this.options = options + } + + /** See {@link CacheModuleConfigOptions.cache} */ + public get cache() { + return this.options.cache + } +} diff --git a/packages/core/src/modules/cache/InMemoryLruCache.ts b/packages/core/src/modules/cache/InMemoryLruCache.ts new file mode 100644 index 0000000000..4a56cb97c5 --- /dev/null +++ b/packages/core/src/modules/cache/InMemoryLruCache.ts @@ -0,0 +1,71 @@ +import type { Cache } from './Cache' +import type { AgentContext } from '../../agent/context' + +import { LRUMap } from 'lru_map' + +export interface InMemoryLruCacheOptions { + /** The maximum number of entries allowed in the cache */ + limit: number +} + +/** + * In memory LRU cache. + * + * This cache can be used with multiple agent context instances, however all instances will share the same cache. + * If you need the cache to be isolated per agent context instance, make sure to use a different cache implementation. + */ +export class InMemoryLruCache implements Cache { + private readonly cache: LRUMap + + public constructor({ limit }: InMemoryLruCacheOptions) { + this.cache = new LRUMap(limit) + } + + public async get(agentContext: AgentContext, key: string) { + this.removeExpiredItems() + const item = this.cache.get(key) + + // Does not exist + if (!item) return null + + return item.value as CacheValue + } + + public async set( + agentContext: AgentContext, + key: string, + value: CacheValue, + expiresInSeconds?: number + ): Promise { + this.removeExpiredItems() + let expiresDate = undefined + + if (expiresInSeconds) { + expiresDate = new Date() + expiresDate.setSeconds(expiresDate.getSeconds() + expiresInSeconds) + } + + this.cache.set(key, { + expiresAt: expiresDate?.getTime(), + value, + }) + } + + public async remove(agentContext: AgentContext, key: string): Promise { + this.removeExpiredItems() + this.cache.delete(key) + } + + private removeExpiredItems() { + this.cache.forEach((value, key) => { + if (value.expiresAt && Date.now() > value.expiresAt) { + this.cache.delete(key) + } + }) + } +} + +interface CacheItem { + expiresAt?: number + value: unknown +} diff --git a/packages/core/src/modules/cache/__tests__/CacheModule.test.ts b/packages/core/src/modules/cache/__tests__/CacheModule.test.ts new file mode 100644 index 0000000000..fe38e2e139 --- /dev/null +++ b/packages/core/src/modules/cache/__tests__/CacheModule.test.ts @@ -0,0 +1,42 @@ +import { DependencyManager } from '../../../plugins/DependencyManager' +import { CacheModule } from '../CacheModule' +import { CacheModuleConfig } from '../CacheModuleConfig' +import { InMemoryLruCache } from '../InMemoryLruCache' +import { SingleContextStorageLruCache } from '../singleContextLruCache' +import { SingleContextLruCacheRepository } from '../singleContextLruCache/SingleContextLruCacheRepository' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +const dependencyManager = new DependencyManagerMock() + +describe('CacheModule', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + test('registers dependencies on the dependency manager', () => { + const cacheModule = new CacheModule({ + cache: new InMemoryLruCache({ limit: 1 }), + }) + cacheModule.register(dependencyManager) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(CacheModuleConfig, cacheModule.config) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(0) + }) + + test('registers cache repository on the dependency manager if the SingleContextStorageLruCache is used', () => { + const cacheModule = new CacheModule({ + cache: new SingleContextStorageLruCache({ limit: 1 }), + }) + cacheModule.register(dependencyManager) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(CacheModuleConfig, cacheModule.config) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SingleContextLruCacheRepository) + }) +}) diff --git a/packages/core/src/modules/cache/__tests__/CacheModuleConfig.test.ts b/packages/core/src/modules/cache/__tests__/CacheModuleConfig.test.ts new file mode 100644 index 0000000000..9cbc267122 --- /dev/null +++ b/packages/core/src/modules/cache/__tests__/CacheModuleConfig.test.ts @@ -0,0 +1,14 @@ +import { CacheModuleConfig } from '../CacheModuleConfig' +import { InMemoryLruCache } from '../InMemoryLruCache' + +describe('CacheModuleConfig', () => { + test('sets values', () => { + const cache = new InMemoryLruCache({ limit: 1 }) + + const config = new CacheModuleConfig({ + cache, + }) + + expect(config.cache).toEqual(cache) + }) +}) diff --git a/packages/core/src/modules/cache/__tests__/InMemoryLruCache.test.ts b/packages/core/src/modules/cache/__tests__/InMemoryLruCache.test.ts new file mode 100644 index 0000000000..aa802575c4 --- /dev/null +++ b/packages/core/src/modules/cache/__tests__/InMemoryLruCache.test.ts @@ -0,0 +1,43 @@ +import { getAgentContext } from '../../../../tests/helpers' +import { InMemoryLruCache } from '../InMemoryLruCache' + +const agentContext = getAgentContext() + +describe('InMemoryLruCache', () => { + let cache: InMemoryLruCache + + beforeEach(() => { + cache = new InMemoryLruCache({ limit: 2 }) + }) + + it('should set, get and remove a value', async () => { + expect(await cache.get(agentContext, 'item')).toBeNull() + + await cache.set(agentContext, 'item', 'somevalue') + expect(await cache.get(agentContext, 'item')).toBe('somevalue') + + await cache.remove(agentContext, 'item') + expect(await cache.get(agentContext, 'item')).toBeNull() + }) + + it('should remove least recently used entries if entries are added that exceed the limit', async () => { + // Set first value in cache, resolves fine + await cache.set(agentContext, 'one', 'valueone') + expect(await cache.get(agentContext, 'one')).toBe('valueone') + + // Set two more entries in the cache. Third item + // exceeds limit, so first item gets removed + await cache.set(agentContext, 'two', 'valuetwo') + await cache.set(agentContext, 'three', 'valuethree') + expect(await cache.get(agentContext, 'one')).toBeNull() + expect(await cache.get(agentContext, 'two')).toBe('valuetwo') + expect(await cache.get(agentContext, 'three')).toBe('valuethree') + + // Get two from the cache, meaning three will be removed first now + // because it is not recently used + await cache.get(agentContext, 'two') + await cache.set(agentContext, 'four', 'valuefour') + expect(await cache.get(agentContext, 'three')).toBeNull() + expect(await cache.get(agentContext, 'two')).toBe('valuetwo') + }) +}) diff --git a/packages/core/src/modules/cache/index.ts b/packages/core/src/modules/cache/index.ts new file mode 100644 index 0000000000..5b5d932671 --- /dev/null +++ b/packages/core/src/modules/cache/index.ts @@ -0,0 +1,10 @@ +// Module +export { CacheModule, CacheModuleOptions } from './CacheModule' +export { CacheModuleConfig } from './CacheModuleConfig' + +// Cache +export { Cache } from './Cache' + +// Cache Implementations +export { InMemoryLruCache, InMemoryLruCacheOptions } from './InMemoryLruCache' +export { SingleContextStorageLruCache, SingleContextStorageLruCacheOptions } from './singleContextLruCache' diff --git a/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts b/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts new file mode 100644 index 0000000000..0016ed3d8e --- /dev/null +++ b/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts @@ -0,0 +1,41 @@ +import type { TagsBase } from '../../../storage/BaseRecord' + +import { BaseRecord } from '../../../storage/BaseRecord' +import { uuid } from '../../../utils/uuid' + +export interface SingleContextLruCacheItem { + value: unknown + expiresAt?: number +} + +export interface SingleContextLruCacheProps { + id?: string + createdAt?: Date + tags?: TagsBase + + entries: Map +} + +export class SingleContextLruCacheRecord extends BaseRecord { + public entries!: Map + + public static readonly type = 'SingleContextLruCacheRecord' + public readonly type = SingleContextLruCacheRecord.type + + public constructor(props: SingleContextLruCacheProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.entries = props.entries + this._tags = props.tags ?? {} + } + } + + public getTags() { + return { + ...this._tags, + } + } +} diff --git a/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRepository.ts b/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRepository.ts new file mode 100644 index 0000000000..dab71b9761 --- /dev/null +++ b/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRepository.ts @@ -0,0 +1,17 @@ +import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { SingleContextLruCacheRecord } from './SingleContextLruCacheRecord' + +@injectable() +export class SingleContextLruCacheRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(SingleContextLruCacheRecord, storageService, eventEmitter) + } +} diff --git a/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts b/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts new file mode 100644 index 0000000000..72498db91a --- /dev/null +++ b/packages/core/src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts @@ -0,0 +1,158 @@ +import type { SingleContextLruCacheItem } from './SingleContextLruCacheRecord' +import type { AgentContext } from '../../../agent/context' +import type { Cache } from '../Cache' + +import { LRUMap } from 'lru_map' + +import { AriesFrameworkError } from '../../../error' + +import { SingleContextLruCacheRecord } from './SingleContextLruCacheRecord' +import { SingleContextLruCacheRepository } from './SingleContextLruCacheRepository' + +const CONTEXT_STORAGE_LRU_CACHE_ID = 'CONTEXT_STORAGE_LRU_CACHE_ID' + +export interface SingleContextStorageLruCacheOptions { + /** The maximum number of entries allowed in the cache */ + limit: number +} + +/** + * Cache that leverages the storage associated with the agent context to store cache records. + * It will keep an in-memory cache of the records to avoid hitting the storage on every read request. + * Therefor this cache is meant to be used with a single instance of the agent. + * + * Due to keeping an in-memory copy of the cache, it is also not meant to be used with multiple + * agent context instances (meaning multi-tenancy), as they will overwrite the in-memory cache. + * + * However, this means the cache is not meant for usage with multiple instances. + */ +export class SingleContextStorageLruCache implements Cache { + private limit: number + private _cache?: LRUMap + private _contextCorrelationId?: string + + public constructor({ limit }: SingleContextStorageLruCacheOptions) { + this.limit = limit + } + + public async get(agentContext: AgentContext, key: string) { + this.assertContextCorrelationId(agentContext) + + const cache = await this.getCache(agentContext) + this.removeExpiredItems(cache) + + const item = cache.get(key) + + // Does not exist + if (!item) return null + + // Expired + if (item.expiresAt && Date.now() > item.expiresAt) { + cache.delete(key) + await this.persistCache(agentContext) + return null + } + + return item.value as CacheValue + } + + public async set( + agentContext: AgentContext, + key: string, + value: CacheValue, + expiresInSeconds?: number + ): Promise { + this.assertContextCorrelationId(agentContext) + + let expiresDate = undefined + + if (expiresInSeconds) { + expiresDate = new Date() + expiresDate.setSeconds(expiresDate.getSeconds() + expiresInSeconds) + } + + const cache = await this.getCache(agentContext) + this.removeExpiredItems(cache) + + cache.set(key, { + expiresAt: expiresDate?.getTime(), + value, + }) + await this.persistCache(agentContext) + } + + public async remove(agentContext: AgentContext, key: string): Promise { + this.assertContextCorrelationId(agentContext) + + const cache = await this.getCache(agentContext) + this.removeExpiredItems(cache) + cache.delete(key) + + await this.persistCache(agentContext) + } + + private async getCache(agentContext: AgentContext) { + if (!this._cache) { + const cacheRecord = await this.fetchCacheRecord(agentContext) + this._cache = this.lruFromRecord(cacheRecord) + } + + return this._cache + } + + private lruFromRecord(cacheRecord: SingleContextLruCacheRecord) { + return new LRUMap(this.limit, cacheRecord.entries.entries()) + } + + private async fetchCacheRecord(agentContext: AgentContext) { + const cacheRepository = agentContext.dependencyManager.resolve(SingleContextLruCacheRepository) + let cacheRecord = await cacheRepository.findById(agentContext, CONTEXT_STORAGE_LRU_CACHE_ID) + + if (!cacheRecord) { + cacheRecord = new SingleContextLruCacheRecord({ + id: CONTEXT_STORAGE_LRU_CACHE_ID, + entries: new Map(), + }) + + await cacheRepository.save(agentContext, cacheRecord) + } + + return cacheRecord + } + + private removeExpiredItems(cache: LRUMap) { + cache.forEach((value, key) => { + if (value.expiresAt && Date.now() > value.expiresAt) { + cache.delete(key) + } + }) + } + + private async persistCache(agentContext: AgentContext) { + const cacheRepository = agentContext.dependencyManager.resolve(SingleContextLruCacheRepository) + const cache = await this.getCache(agentContext) + + await cacheRepository.update( + agentContext, + new SingleContextLruCacheRecord({ + entries: new Map(cache.toJSON().map(({ key, value }) => [key, value])), + id: CONTEXT_STORAGE_LRU_CACHE_ID, + }) + ) + } + + /** + * Asserts this class is not used with multiple agent context instances. + */ + private assertContextCorrelationId(agentContext: AgentContext) { + if (!this._contextCorrelationId) { + this._contextCorrelationId = agentContext.contextCorrelationId + } + + if (this._contextCorrelationId !== agentContext.contextCorrelationId) { + throw new AriesFrameworkError( + 'SingleContextStorageLruCache can not be used with multiple agent context instances. Register a custom cache implementation in the CacheModule.' + ) + } + } +} diff --git a/packages/core/src/modules/cache/singleContextLruCache/__tests__/SingleContextStorageLruCache.test.ts b/packages/core/src/modules/cache/singleContextLruCache/__tests__/SingleContextStorageLruCache.test.ts new file mode 100644 index 0000000000..2251b9b854 --- /dev/null +++ b/packages/core/src/modules/cache/singleContextLruCache/__tests__/SingleContextStorageLruCache.test.ts @@ -0,0 +1,91 @@ +import { getAgentContext, mockFunction } from '../../../../../tests/helpers' +import { SingleContextLruCacheRecord } from '../SingleContextLruCacheRecord' +import { SingleContextLruCacheRepository } from '../SingleContextLruCacheRepository' +import { SingleContextStorageLruCache } from '../SingleContextStorageLruCache' + +jest.mock('../SingleContextLruCacheRepository') +const SingleContextLruCacheRepositoryMock = + SingleContextLruCacheRepository as jest.Mock + +const cacheRepository = new SingleContextLruCacheRepositoryMock() +const agentContext = getAgentContext({ + registerInstances: [[SingleContextLruCacheRepository, cacheRepository]], +}) + +describe('SingleContextLruCache', () => { + let cache: SingleContextStorageLruCache + + beforeEach(() => { + mockFunction(cacheRepository.findById).mockResolvedValue(null) + cache = new SingleContextStorageLruCache({ limit: 2 }) + }) + + it('should return the value from the persisted record', async () => { + const findMock = mockFunction(cacheRepository.findById).mockResolvedValue( + new SingleContextLruCacheRecord({ + id: 'CONTEXT_STORAGE_LRU_CACHE_ID', + entries: new Map([ + [ + 'test', + { + value: 'somevalue', + }, + ], + ]), + }) + ) + + expect(await cache.get(agentContext, 'doesnotexist')).toBeNull() + expect(await cache.get(agentContext, 'test')).toBe('somevalue') + expect(findMock).toHaveBeenCalledWith(agentContext, 'CONTEXT_STORAGE_LRU_CACHE_ID') + }) + + it('should set the value in the persisted record', async () => { + const updateMock = mockFunction(cacheRepository.update).mockResolvedValue() + + await cache.set(agentContext, 'test', 'somevalue') + const [[, cacheRecord]] = updateMock.mock.calls + + expect(cacheRecord.entries.size).toBe(1) + + const [[key, item]] = cacheRecord.entries.entries() + expect(key).toBe('test') + expect(item.value).toBe('somevalue') + + expect(await cache.get(agentContext, 'test')).toBe('somevalue') + }) + + it('should remove least recently used entries if entries are added that exceed the limit', async () => { + // Set first value in cache, resolves fine + await cache.set(agentContext, 'one', 'valueone') + expect(await cache.get(agentContext, 'one')).toBe('valueone') + + // Set two more entries in the cache. Third item + // exceeds limit, so first item gets removed + await cache.set(agentContext, 'two', 'valuetwo') + await cache.set(agentContext, 'three', 'valuethree') + expect(await cache.get(agentContext, 'one')).toBeNull() + expect(await cache.get(agentContext, 'two')).toBe('valuetwo') + expect(await cache.get(agentContext, 'three')).toBe('valuethree') + + // Get two from the cache, meaning three will be removed first now + // because it is not recently used + await cache.get(agentContext, 'two') + await cache.set(agentContext, 'four', 'valuefour') + expect(await cache.get(agentContext, 'three')).toBeNull() + expect(await cache.get(agentContext, 'two')).toBe('valuetwo') + }) + + it('should throw an error if used with multiple context correlation ids', async () => { + // No issue, first call with an agentContext + await cache.get(agentContext, 'test') + + const secondAgentContext = getAgentContext({ + contextCorrelationId: 'another', + }) + + expect(cache.get(secondAgentContext, 'test')).rejects.toThrowError( + 'SingleContextStorageLruCache can not be used with multiple agent context instances. Register a custom cache implementation in the CacheModule.' + ) + }) +}) diff --git a/packages/core/src/modules/cache/singleContextLruCache/index.ts b/packages/core/src/modules/cache/singleContextLruCache/index.ts new file mode 100644 index 0000000000..4d01549062 --- /dev/null +++ b/packages/core/src/modules/cache/singleContextLruCache/index.ts @@ -0,0 +1 @@ +export { SingleContextStorageLruCache, SingleContextStorageLruCacheOptions } from './SingleContextStorageLruCache' diff --git a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts index 073d08686f..b34d2b6fcf 100644 --- a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts +++ b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts @@ -1,26 +1,23 @@ import type { AgentContext } from '../../../agent' +import type { Cache } from '../../cache' import type { IndyPoolConfig } from '../IndyPool' import type { CachedDidResponse } from '../services/IndyPoolService' import { Subject } from 'rxjs' import { NodeFileSystem } from '../../../../../node/src/NodeFileSystem' -import { agentDependencies, getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' -import { CacheRecord } from '../../../cache' -import { CacheRepository } from '../../../cache/CacheRepository' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../tests/helpers' import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { IndyWallet } from '../../../wallet/IndyWallet' +import { CacheModuleConfig, InMemoryLruCache } from '../../cache' import { LedgerError } from '../error/LedgerError' import { LedgerNotConfiguredError } from '../error/LedgerNotConfiguredError' import { LedgerNotFoundError } from '../error/LedgerNotFoundError' -import { DID_POOL_CACHE_ID, IndyPoolService } from '../services/IndyPoolService' +import { IndyPoolService } from '../services/IndyPoolService' import { getDidResponsesForDid } from './didResponses' -jest.mock('../../../cache/CacheRepository') -const CacheRepositoryMock = CacheRepository as jest.Mock - const pools: IndyPoolConfig[] = [ { id: 'sovrinMain', @@ -66,11 +63,11 @@ describe('IndyPoolService', () => { let agentContext: AgentContext let wallet: IndyWallet let poolService: IndyPoolService - let cacheRepository: CacheRepository + let cache: Cache beforeAll(async () => { wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) - agentContext = getAgentContext() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) @@ -80,16 +77,11 @@ describe('IndyPoolService', () => { }) beforeEach(async () => { - cacheRepository = new CacheRepositoryMock() - mockFunction(cacheRepository.findById).mockResolvedValue(null) - - poolService = new IndyPoolService( - cacheRepository, - agentDependencies, - config.logger, - new Subject(), - new NodeFileSystem() - ) + cache = new InMemoryLruCache({ limit: 200 }) + agentContext = getAgentContext({ + registerInstances: [[CacheModuleConfig, new CacheModuleConfig({ cache })]], + }) + poolService = new IndyPoolService(agentDependencies, config.logger, new Subject(), new NodeFileSystem()) poolService.setPools(pools) }) @@ -242,20 +234,7 @@ describe('IndyPoolService', () => { poolId: expectedPool.id, } - const cachedEntries = [ - { - key: did, - value: didResponse, - }, - ] - - mockFunction(cacheRepository.findById).mockResolvedValue( - new CacheRecord({ - id: DID_POOL_CACHE_ID, - entries: cachedEntries, - }) - ) - + await cache.set(agentContext, `IndySdkPoolService:${did}`, didResponse) const { pool } = await poolService.getPoolForDid(agentContext, did) expect(pool.config.id).toBe(pool.id) @@ -268,15 +247,6 @@ describe('IndyPoolService', () => { sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', }) - mockFunction(cacheRepository.findById).mockResolvedValue( - new CacheRecord({ - id: DID_POOL_CACHE_ID, - entries: [], - }) - ) - - const spy = mockFunction(cacheRepository.update).mockResolvedValue() - poolService.pools.forEach((pool, index) => { const spy = jest.spyOn(pool, 'submitReadRequest') spy.mockImplementationOnce(responses[index]) @@ -287,10 +257,7 @@ describe('IndyPoolService', () => { expect(pool.config.id).toBe('sovrinBuilder') expect(pool.config.indyNamespace).toBe('sovrin:builder') - const cacheRecord = spy.mock.calls[0][1] - expect(cacheRecord.entries.length).toBe(1) - expect(cacheRecord.entries[0].key).toBe(did) - expect(cacheRecord.entries[0].value).toEqual({ + expect(await cache.get(agentContext, `IndySdkPoolService:${did}`)).toEqual({ nymResponse: { did, verkey: '~M9kv2Ez61cur7X39DXWh8W', diff --git a/packages/core/src/modules/ledger/services/IndyPoolService.ts b/packages/core/src/modules/ledger/services/IndyPoolService.ts index dd5095b08d..172d1febd1 100644 --- a/packages/core/src/modules/ledger/services/IndyPoolService.ts +++ b/packages/core/src/modules/ledger/services/IndyPoolService.ts @@ -5,7 +5,6 @@ import type { default as Indy, LedgerReadReplyResponse, LedgerRequest, LedgerWri import { Subject } from 'rxjs' import { AgentDependencies } from '../../../agent/AgentDependencies' -import { CacheRepository, PersistedLruCache } from '../../../cache' import { InjectionSymbols } from '../../../constants' import { IndySdkError } from '../../../error/IndySdkError' import { Logger } from '../../../logger/Logger' @@ -15,17 +14,17 @@ import { isSelfCertifiedDid } from '../../../utils/did' import { isIndyError } from '../../../utils/indyError' import { allSettled, onlyFulfilled, onlyRejected } from '../../../utils/promises' import { assertIndyWallet } from '../../../wallet/util/assertIndyWallet' +import { CacheModuleConfig } from '../../cache' import { IndyPool } from '../IndyPool' import { LedgerError } from '../error/LedgerError' import { LedgerNotConfiguredError } from '../error/LedgerNotConfiguredError' import { LedgerNotFoundError } from '../error/LedgerNotFoundError' -export const DID_POOL_CACHE_ID = 'DID_POOL_CACHE' -export const DID_POOL_CACHE_LIMIT = 500 export interface CachedDidResponse { nymResponse: Indy.GetNymResponse poolId: string } + @injectable() export class IndyPoolService { public pools: IndyPool[] = [] @@ -34,10 +33,8 @@ export class IndyPoolService { private agentDependencies: AgentDependencies private stop$: Subject private fileSystem: FileSystem - private didCache: PersistedLruCache public constructor( - cacheRepository: CacheRepository, @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, @inject(InjectionSymbols.Logger) logger: Logger, @inject(InjectionSymbols.Stop$) stop$: Subject, @@ -48,8 +45,6 @@ export class IndyPoolService { this.agentDependencies = agentDependencies this.fileSystem = fileSystem this.stop$ = stop$ - - this.didCache = new PersistedLruCache(DID_POOL_CACHE_ID, DID_POOL_CACHE_LIMIT, cacheRepository) } public setPools(poolConfigs: IndyPoolConfig[]) { @@ -104,7 +99,9 @@ export class IndyPoolService { ) } - const cachedNymResponse = await this.didCache.get(agentContext, did) + const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + + const cachedNymResponse = await cache.get(agentContext, `IndySdkPoolService:${did}`) const pool = this.pools.find((pool) => pool.id === cachedNymResponse?.poolId) // If we have the nym response with associated pool in the cache, we'll use that @@ -151,7 +148,7 @@ export class IndyPoolService { value = productionOrNonProduction[0].value } - await this.didCache.set(agentContext, did, { + await cache.set(agentContext, `IndySdkPoolService:${did}`, { nymResponse: value.did, poolId: value.pool.id, }) diff --git a/packages/indy-sdk/jest.config.ts b/packages/indy-sdk/jest.config.ts index c7c5196637..55c67d70a6 100644 --- a/packages/indy-sdk/jest.config.ts +++ b/packages/indy-sdk/jest.config.ts @@ -8,7 +8,7 @@ const config: Config.InitialOptions = { ...base, name: packageJson.name, displayName: packageJson.name, - // setupFilesAfterEnv: ['./tests/setup.ts'], + setupFilesAfterEnv: ['./tests/setup.ts'], } export default config diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index 773b7db2cc..9d237fd336 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -2,15 +2,7 @@ import type { AcceptanceMechanisms, AuthorAgreement, IndySdkPoolConfig } from '. import type { AgentContext } from '@aries-framework/core' import type { GetNymResponse, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' -import { - InjectionSymbols, - Logger, - injectable, - inject, - FileSystem, - CacheRepository, - PersistedLruCache, -} from '@aries-framework/core' +import { CacheModuleConfig, InjectionSymbols, Logger, injectable, inject, FileSystem } from '@aries-framework/core' import { Subject } from 'rxjs' import { IndySdkError, isIndyError } from '../error' @@ -22,8 +14,6 @@ import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' import { IndySdkPool } from './IndySdkPool' import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from './error' -export const INDY_SDK_DID_POOL_CACHE_ID = 'INDY_SDK_DID_POOL_CACHE' -export const INDY_SDK_DID_POOL_CACHE_LIMIT = 500 export interface CachedDidResponse { nymResponse: GetNymResponse poolId: string @@ -36,10 +26,8 @@ export class IndySdkPoolService { private indySdk: IndySdk private stop$: Subject private fileSystem: FileSystem - private didCache: PersistedLruCache public constructor( - cacheRepository: CacheRepository, indySdk: IndySdk, @inject(InjectionSymbols.Logger) logger: Logger, @inject(InjectionSymbols.Stop$) stop$: Subject, @@ -49,8 +37,6 @@ export class IndySdkPoolService { this.indySdk = indySdk this.fileSystem = fileSystem this.stop$ = stop$ - - this.didCache = new PersistedLruCache(INDY_SDK_DID_POOL_CACHE_ID, INDY_SDK_DID_POOL_CACHE_LIMIT, cacheRepository) } public setPools(poolConfigs: IndySdkPoolConfig[]) { @@ -90,7 +76,8 @@ export class IndySdkPoolService { ) } - const cachedNymResponse = await this.didCache.get(agentContext, did) + const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + const cachedNymResponse = await cache.get(agentContext, `IndySdkPoolService:${did}`) const pool = this.pools.find((pool) => pool.id === cachedNymResponse?.poolId) // If we have the nym response with associated pool in the cache, we'll use that @@ -137,7 +124,7 @@ export class IndySdkPoolService { value = productionOrNonProduction[0].value } - await this.didCache.set(agentContext, did, { + await cache.set(agentContext, `IndySdkPoolService:${did}`, { nymResponse: value.did, poolId: value.pool.id, }) diff --git a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts index 74debe2656..2ef487e566 100644 --- a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts +++ b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts @@ -1,22 +1,22 @@ import type { IndySdkPoolConfig } from '../IndySdkPool' import type { CachedDidResponse } from '../IndySdkPoolService' -import type { AgentContext } from '@aries-framework/core' - -import { SigningProviderRegistry, AriesFrameworkError } from '@aries-framework/core' +import type { AgentContext, Cache } from '@aries-framework/core' + +import { + CacheModuleConfig, + InMemoryLruCache, + SigningProviderRegistry, + AriesFrameworkError, +} from '@aries-framework/core' import { Subject } from 'rxjs' -import { CacheRecord } from '../../../../core/src/cache' -import { CacheRepository } from '../../../../core/src/cache/CacheRepository' import { getDidResponsesForDid } from '../../../../core/src/modules/ledger/__tests__/didResponses' -import { agentDependencies, getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { NodeFileSystem } from '../../../../node/src/NodeFileSystem' import { IndySdkWallet } from '../../wallet/IndySdkWallet' -import { INDY_SDK_DID_POOL_CACHE_ID, IndySdkPoolService } from '../IndySdkPoolService' +import { IndySdkPoolService } from '../IndySdkPoolService' import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from '../error' -jest.mock('../../../../core/src/cache/CacheRepository') -const CacheRepositoryMock = CacheRepository as jest.Mock - const pools: IndySdkPoolConfig[] = [ { id: 'sovrinMain', @@ -62,11 +62,10 @@ describe('IndySdkPoolService', () => { let agentContext: AgentContext let wallet: IndySdkWallet let poolService: IndySdkPoolService - let cacheRepository: CacheRepository + let cache: Cache beforeAll(async () => { wallet = new IndySdkWallet(config.agentDependencies.indy, config.logger, new SigningProviderRegistry([])) - agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) @@ -76,11 +75,11 @@ describe('IndySdkPoolService', () => { }) beforeEach(async () => { - cacheRepository = new CacheRepositoryMock() - mockFunction(cacheRepository.findById).mockResolvedValue(null) - + cache = new InMemoryLruCache({ limit: 200 }) + agentContext = getAgentContext({ + registerInstances: [[CacheModuleConfig, new CacheModuleConfig({ cache })]], + }) poolService = new IndySdkPoolService( - cacheRepository, agentDependencies.indy, config.logger, new Subject(), @@ -226,20 +225,7 @@ describe('IndySdkPoolService', () => { poolId: expectedPool.id, } - const cachedEntries = [ - { - key: did, - value: didResponse, - }, - ] - - mockFunction(cacheRepository.findById).mockResolvedValue( - new CacheRecord({ - id: INDY_SDK_DID_POOL_CACHE_ID, - entries: cachedEntries, - }) - ) - + await cache.set(agentContext, `IndySdkPoolService:${did}`, didResponse) const { pool } = await poolService.getPoolForDid(agentContext, did) expect(pool.config.id).toBe(pool.id) @@ -252,15 +238,6 @@ describe('IndySdkPoolService', () => { sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', }) - mockFunction(cacheRepository.findById).mockResolvedValue( - new CacheRecord({ - id: INDY_SDK_DID_POOL_CACHE_ID, - entries: [], - }) - ) - - const spy = mockFunction(cacheRepository.update).mockResolvedValue() - poolService.pools.forEach((pool, index) => { const spy = jest.spyOn(pool, 'submitReadRequest') spy.mockImplementationOnce(responses[index]) @@ -271,10 +248,7 @@ describe('IndySdkPoolService', () => { expect(pool.config.id).toBe('sovrinBuilder') expect(pool.config.indyNamespace).toBe('sovrin:builder') - const cacheRecord = spy.mock.calls[0][1] - expect(cacheRecord.entries.length).toBe(1) - expect(cacheRecord.entries[0].key).toBe(did) - expect(cacheRecord.entries[0].value).toEqual({ + expect(await cache.get(agentContext, `IndySdkPoolService:${did}`)).toEqual({ nymResponse: { did, verkey: '~M9kv2Ez61cur7X39DXWh8W', diff --git a/packages/indy-sdk/tests/setup.ts b/packages/indy-sdk/tests/setup.ts new file mode 100644 index 0000000000..719a473b6e --- /dev/null +++ b/packages/indy-sdk/tests/setup.ts @@ -0,0 +1 @@ +jest.setTimeout(10000) From 0f6d2312471efab20f560782c171434f907b6b9d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 21 Jan 2023 04:07:21 +0800 Subject: [PATCH 016/139] feat(proofs): sort credentials based on revocation (#1225) Signed-off-by: Timo Glastra --- .../formats/indy/IndyProofFormatService.ts | 63 ++++++++++--------- .../sortRequestedCredentials.test.ts | 48 ++++++++++++++ .../indy/util/sortRequestedCredentials.ts | 33 ++++++++++ 3 files changed, 115 insertions(+), 29 deletions(-) create mode 100644 packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts create mode 100644 packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts index ecdb78f358..9730e6aa8d 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts @@ -62,6 +62,7 @@ import { import { ProofRequest } from './models/ProofRequest' import { RequestedCredentials } from './models/RequestedCredentials' import { RetrievedCredentials } from './models/RetrievedCredentials' +import { sortRequestedCredentials } from './util/sortRequestedCredentials' @scoped(Lifecycle.ContainerScoped) export class IndyProofFormatService extends ProofFormatService { @@ -424,22 +425,24 @@ export class IndyProofFormatService extends ProofFormatService { }) } - retrievedCredentials.requestedAttributes[referent] = await Promise.all( - credentialMatch.map(async (credential: IndyCredential) => { - const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { - proofRequest, - requestedItem: requestedAttribute, - credential, + retrievedCredentials.requestedAttributes[referent] = sortRequestedCredentials( + await Promise.all( + credentialMatch.map(async (credential: IndyCredential) => { + const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { + proofRequest, + requestedItem: requestedAttribute, + credential, + }) + + return new RequestedAttribute({ + credentialId: credential.credentialInfo.referent, + revealed: true, + credentialInfo: credential.credentialInfo, + timestamp: deltaTimestamp, + revoked, + }) }) - - return new RequestedAttribute({ - credentialId: credential.credentialInfo.referent, - revealed: true, - credentialInfo: credential.credentialInfo, - timestamp: deltaTimestamp, - revoked, - }) - }) + ) ) // We only attach revoked state if non-revocation is requested. So if revoked is true it means @@ -454,21 +457,23 @@ export class IndyProofFormatService extends ProofFormatService { for (const [referent, requestedPredicate] of proofRequest.requestedPredicates.entries()) { const credentials = await this.getCredentialsForProofRequest(agentContext, proofRequest, referent) - retrievedCredentials.requestedPredicates[referent] = await Promise.all( - credentials.map(async (credential) => { - const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { - proofRequest, - requestedItem: requestedPredicate, - credential, + retrievedCredentials.requestedPredicates[referent] = sortRequestedCredentials( + await Promise.all( + credentials.map(async (credential) => { + const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { + proofRequest, + requestedItem: requestedPredicate, + credential, + }) + + return new RequestedPredicate({ + credentialId: credential.credentialInfo.referent, + credentialInfo: credential.credentialInfo, + timestamp: deltaTimestamp, + revoked, + }) }) - - return new RequestedPredicate({ - credentialId: credential.credentialInfo.referent, - credentialInfo: credential.credentialInfo, - timestamp: deltaTimestamp, - revoked, - }) - }) + ) ) // We only attach revoked state if non-revocation is requested. So if revoked is true it means diff --git a/packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts b/packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts new file mode 100644 index 0000000000..117fe2b898 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts @@ -0,0 +1,48 @@ +import { RequestedAttribute } from '../../models' +import { sortRequestedCredentials } from '../sortRequestedCredentials' + +const credentials = [ + new RequestedAttribute({ + credentialId: '1', + revealed: true, + revoked: true, + }), + new RequestedAttribute({ + credentialId: '2', + revealed: true, + revoked: undefined, + }), + new RequestedAttribute({ + credentialId: '3', + revealed: true, + revoked: false, + }), + new RequestedAttribute({ + credentialId: '4', + revealed: true, + revoked: false, + }), + new RequestedAttribute({ + credentialId: '5', + revealed: true, + revoked: true, + }), + new RequestedAttribute({ + credentialId: '6', + revealed: true, + revoked: undefined, + }), +] + +describe('sortRequestedCredentials', () => { + test('sorts the credentials', () => { + expect(sortRequestedCredentials(credentials)).toEqual([ + credentials[1], + credentials[5], + credentials[2], + credentials[3], + credentials[0], + credentials[4], + ]) + }) +}) diff --git a/packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts b/packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts new file mode 100644 index 0000000000..2db1deb0b9 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts @@ -0,0 +1,33 @@ +import type { RequestedAttribute, RequestedPredicate } from '../models' + +/** + * Sort requested attributes and predicates by `revoked` status. The order is: + * - first credentials with `revoked` set to undefined, this means no revocation status is needed for the credentials + * - then credentials with `revoked` set to false, this means the credentials are not revoked + * - then credentials with `revoked` set to true, this means the credentials are revoked + */ +export function sortRequestedCredentials | Array>( + credentials: Requested +) { + const staySame = 0 + const credentialGoUp = -1 + const credentialGoDown = 1 + + // Clone as sort is in place + const credentialsClone = [...credentials] + + return credentialsClone.sort((credential, compareTo) => { + // Nothing needs to happen if values are the same + if (credential.revoked === compareTo.revoked) return staySame + + // Undefined always is at the top + if (credential.revoked === undefined) return credentialGoUp + if (compareTo.revoked === undefined) return credentialGoDown + + // Then revoked + if (credential.revoked === false) return credentialGoUp + + // It means that compareTo is false and credential is true + return credentialGoDown + }) +} From b6ae94825696034e51969e70d405513a9ffe84f7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 26 Jan 2023 12:06:45 +0100 Subject: [PATCH 017/139] chore: deprecate injectionContainer on agent (#1241) Signed-off-by: Timo Glastra --- .../tests/v2.ldproof.credentials.propose-offerBbs.test.ts | 2 +- packages/core/src/agent/BaseAgent.ts | 3 +++ .../v2.ldproof.connectionless-credentials.test.ts | 2 +- .../__tests__/v2.ldproof.credentials-auto-accept.test.ts | 4 ++-- .../v2.ldproof.credentials.propose-offerED25519.test.ts | 3 +-- .../src/modules/dids/__tests__/dids-registrar.e2e.test.ts | 2 +- .../protocol/v1/__tests__/indy-proof-negotiation.test.ts | 8 ++++---- .../protocol/v1/__tests__/indy-proof-presentation.test.ts | 4 ++-- .../protocol/v1/__tests__/indy-proof-proposal.test.ts | 2 +- .../protocol/v1/__tests__/indy-proof-request.test.ts | 4 ++-- .../protocol/v2/__tests__/indy-proof-negotiation.test.ts | 8 ++++---- .../protocol/v2/__tests__/indy-proof-presentation.test.ts | 4 ++-- .../protocol/v2/__tests__/indy-proof-proposal.test.ts | 2 +- .../protocol/v2/__tests__/indy-proof-request.test.ts | 4 ++-- packages/core/src/storage/migration/__tests__/0.1.test.ts | 8 ++++---- packages/core/src/storage/migration/__tests__/0.2.test.ts | 6 +++--- packages/core/src/storage/migration/__tests__/0.3.test.ts | 2 +- .../core/src/storage/migration/__tests__/backup.test.ts | 4 ++-- packages/core/tests/v1-indy-proofs.test.ts | 6 +++--- packages/core/tests/v2-indy-proofs.test.ts | 2 +- packages/node/src/transport/WsInboundTransport.ts | 2 +- tests/transport/SubjectOutboundTransport.ts | 2 +- 22 files changed, 43 insertions(+), 41 deletions(-) diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index 4569b44631..997c73c18b 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -35,7 +35,7 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { 'Faber Agent Credentials LD BBS+', 'Alice Agent Credentials LD BBS+' )) - wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) await wallet.createKey({ keyType: KeyType.Ed25519, seed }) const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, seed }) diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 606b586a67..5d47157f59 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -194,6 +194,9 @@ export abstract class BaseAgent { aliceAgent.events .observable(CredentialEventTypes.CredentialStateChanged) .subscribe(aliceReplay) - wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index a9bf76f0f5..ad08852a17 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -43,7 +43,7 @@ describe('credentials', () => { AutoAcceptCredential.Always )) - wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, @@ -142,7 +142,7 @@ describe('credentials', () => { 'alice agent: content-approved v2 jsonld', AutoAcceptCredential.ContentApproved )) - wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 3d4d757554..c8f9a64d20 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -65,7 +65,7 @@ describe('credentials', () => { 'Faber Agent Credentials LD', 'Alice Agent Credentials LD' )) - wallet = faberAgent.injectionContainer.resolve(InjectionSymbols.Wallet) + wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: inputDocAsJson, @@ -312,7 +312,6 @@ describe('credentials', () => { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - // didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const offerMessage = await didCommMessageRepository.findAgentMessage(faberAgent.context, { diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index f6d6763a4f..9599d06c92 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -177,7 +177,7 @@ describe('dids', () => { const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) const indyDid = indyDidFromPublicKeyBase58(ed25519PublicKeyBase58) - const wallet = agent.injectionContainer.resolve(InjectionSymbols.Wallet) + const wallet = agent.dependencyManager.resolve(InjectionSymbols.Wallet) // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain, @typescript-eslint/no-non-null-assertion const submitterDid = `did:sov:${wallet.publicDid?.did!}` diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts index 19134854a8..ee7b481cbb 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts @@ -66,7 +66,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) let proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -159,7 +159,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) let request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -212,7 +212,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -266,7 +266,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts index 975a6aed43..8b32bbe14c 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts @@ -60,7 +60,7 @@ describe('Present Proof', () => { faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -118,7 +118,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts index e2b2df04ff..606f1e7ff8 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts @@ -58,7 +58,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts index 130f5cf04a..c3bccd9f9b 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts @@ -60,7 +60,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -120,7 +120,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts index 7ebe83cb32..a337eca475 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts @@ -69,7 +69,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) let proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -188,7 +188,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) let request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -241,7 +241,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -319,7 +319,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts index 08d5978d80..a8f2d6a531 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts @@ -66,7 +66,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberPresentationRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -118,7 +118,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await alicePresentationRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts index 0aed8af01c..39a29df125 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts @@ -59,7 +59,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberPresentationRecord = await faberPresentationRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberPresentationRecord.id, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts index d30a0c02b9..47a697821f 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts @@ -62,7 +62,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberPresentationRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -114,7 +114,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await alicePresentationRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index 139b73024a..ad2dd0b837 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -48,7 +48,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { @@ -110,7 +110,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { @@ -174,7 +174,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { @@ -242,7 +242,7 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index b67e361855..08ed9dce64 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -46,7 +46,7 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { @@ -119,7 +119,7 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) // We need to manually initialize the wallet as we're using the in memory wallet service // When we call agent.initialize() it will create the wallet and store the current framework @@ -170,7 +170,7 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) // We need to manually initialize the wallet as we're using the in memory wallet service // When we call agent.initialize() it will create the wallet and store the current framework diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index a7ec0f6adb..b797fc7c97 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -43,7 +43,7 @@ describe('UpdateAssistant | v0.3 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index b1efe9b580..73aba5823f 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -81,7 +81,7 @@ describe('UpdateAssistant | Backup', () => { // Expect an update is needed expect(await updateAssistant.isUpToDate()).toBe(false) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) // Backup should not exist before update expect(await fileSystem.exists(backupPath)).toBe(false) @@ -128,7 +128,7 @@ describe('UpdateAssistant | Backup', () => { }, ]) - const fileSystem = agent.injectionContainer.resolve(InjectionSymbols.FileSystem) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) // Backup should not exist before update expect(await fileSystem.exists(backupPath)).toBe(false) diff --git a/packages/core/tests/v1-indy-proofs.test.ts b/packages/core/tests/v1-indy-proofs.test.ts index 81b523d659..440da6a5d4 100644 --- a/packages/core/tests/v1-indy-proofs.test.ts +++ b/packages/core/tests/v1-indy-proofs.test.ts @@ -71,7 +71,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for a presentation proposal from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -390,7 +390,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, @@ -615,7 +615,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/core/tests/v2-indy-proofs.test.ts b/packages/core/tests/v2-indy-proofs.test.ts index 9be131c559..f54f6da15e 100644 --- a/packages/core/tests/v2-indy-proofs.test.ts +++ b/packages/core/tests/v2-indy-proofs.test.ts @@ -78,7 +78,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for a presentation proposal from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository) + didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, diff --git a/packages/node/src/transport/WsInboundTransport.ts b/packages/node/src/transport/WsInboundTransport.ts index 2fa23c6168..0ccda783ba 100644 --- a/packages/node/src/transport/WsInboundTransport.ts +++ b/packages/node/src/transport/WsInboundTransport.ts @@ -58,7 +58,7 @@ export class WsInboundTransport implements InboundTransport { } private listenOnWebSocketMessages(agent: Agent, socket: WebSocket, session: TransportSession) { - const messageReceiver = agent.injectionContainer.resolve(MessageReceiver) + const messageReceiver = agent.dependencyManager.resolve(MessageReceiver) // eslint-disable-next-line @typescript-eslint/no-explicit-any socket.addEventListener('message', async (event: any) => { diff --git a/tests/transport/SubjectOutboundTransport.ts b/tests/transport/SubjectOutboundTransport.ts index 16868df737..44f64555af 100644 --- a/tests/transport/SubjectOutboundTransport.ts +++ b/tests/transport/SubjectOutboundTransport.ts @@ -29,7 +29,7 @@ export class SubjectOutboundTransport implements OutboundTransport { } public async sendMessage(outboundPackage: OutboundPackage) { - const messageReceiver = this.agent.injectionContainer.resolve(MessageReceiver) + const messageReceiver = this.agent.dependencyManager.resolve(MessageReceiver) this.logger.debug(`Sending outbound message to endpoint ${outboundPackage.endpoint}`, { endpoint: outboundPackage.endpoint, }) From e8d6ac31a8e18847d99d7998bd7658439e48875b Mon Sep 17 00:00:00 2001 From: Victor Anene <62852943+Vickysomtee@users.noreply.github.com> Date: Thu, 26 Jan 2023 20:51:18 +0100 Subject: [PATCH 018/139] feat(indy-vdr): add indy-vdr package and indy vdr pool (#1160) work funded by the Government of Ontario Signed-off-by: Victor Anene --- packages/core/tests/helpers.ts | 3 + packages/indy-vdr/README.md | 35 +++ packages/indy-vdr/jest.config.ts | 14 ++ packages/indy-vdr/package.json | 36 +++ packages/indy-vdr/src/error/IndyVdrError.ts | 7 + .../src/error/IndyVdrNotConfiguredError.ts | 7 + .../indy-vdr/src/error/IndyVdrNotFound.ts | 7 + packages/indy-vdr/src/error/index.ts | 3 + packages/indy-vdr/src/index.ts | 6 + packages/indy-vdr/src/pool/IndyVdrPool.ts | 184 +++++++++++++++ .../indy-vdr/src/pool/IndyVdrPoolService.ts | 209 ++++++++++++++++ packages/indy-vdr/src/pool/index.ts | 1 + packages/indy-vdr/src/utils/did.ts | 61 +++++ packages/indy-vdr/src/utils/promises.ts | 44 ++++ .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 223 ++++++++++++++++++ packages/indy-vdr/tests/setup.ts | 4 + packages/indy-vdr/tsconfig.build.json | 7 + packages/indy-vdr/tsconfig.json | 6 + yarn.lock | 96 +++++++- 19 files changed, 952 insertions(+), 1 deletion(-) create mode 100644 packages/indy-vdr/README.md create mode 100644 packages/indy-vdr/jest.config.ts create mode 100644 packages/indy-vdr/package.json create mode 100644 packages/indy-vdr/src/error/IndyVdrError.ts create mode 100644 packages/indy-vdr/src/error/IndyVdrNotConfiguredError.ts create mode 100644 packages/indy-vdr/src/error/IndyVdrNotFound.ts create mode 100644 packages/indy-vdr/src/error/index.ts create mode 100644 packages/indy-vdr/src/index.ts create mode 100644 packages/indy-vdr/src/pool/IndyVdrPool.ts create mode 100644 packages/indy-vdr/src/pool/IndyVdrPoolService.ts create mode 100644 packages/indy-vdr/src/pool/index.ts create mode 100644 packages/indy-vdr/src/utils/did.ts create mode 100644 packages/indy-vdr/src/utils/promises.ts create mode 100644 packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts create mode 100644 packages/indy-vdr/tests/setup.ts create mode 100644 packages/indy-vdr/tsconfig.build.json create mode 100644 packages/indy-vdr/tsconfig.json diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 9e57c21943..9718a60975 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -23,6 +23,7 @@ import type { Awaited } from '../src/types' import type { CredDef, Schema } from 'indy-sdk' import type { Observable } from 'rxjs' +import { readFileSync } from 'fs' import path from 'path' import { firstValueFrom, ReplaySubject, Subject } from 'rxjs' import { catchError, filter, map, timeout } from 'rxjs/operators' @@ -83,6 +84,8 @@ export const genesisPath = process.env.GENESIS_TXN_PATH ? path.resolve(process.env.GENESIS_TXN_PATH) : path.join(__dirname, '../../../network/genesis/local-genesis.txn') +export const genesisTransactions = readFileSync(genesisPath).toString('utf-8') + export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' const taaVersion = (process.env.TEST_AGENT_TAA_VERSION ?? '1') as `${number}.${number}` | `${number}` const taaAcceptanceMechanism = process.env.TEST_AGENT_TAA_ACCEPTANCE_MECHANISM ?? 'accept' diff --git a/packages/indy-vdr/README.md b/packages/indy-vdr/README.md new file mode 100644 index 0000000000..310b38a4f9 --- /dev/null +++ b/packages/indy-vdr/README.md @@ -0,0 +1,35 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript - Indy Verifiable Data Registry (Indy-Vdr)

+

+ License + typescript + @aries-framework/anoncreds version + +

+
+ +### Installation + +### Quick start + +### Example of usage diff --git a/packages/indy-vdr/jest.config.ts b/packages/indy-vdr/jest.config.ts new file mode 100644 index 0000000000..55c67d70a6 --- /dev/null +++ b/packages/indy-vdr/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json new file mode 100644 index 0000000000..32c8689d5d --- /dev/null +++ b/packages/indy-vdr/package.json @@ -0,0 +1,36 @@ +{ + "name": "@aries-framework/indy-vdr", + "main": "build/index", + "types": "build/index", + "version": "0.3.3", + "private": true, + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/indy-vdr", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/indy-vdr" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "0.3.3", + "indy-vdr-test-shared": "^0.1.3" + }, + "devDependencies": { + "indy-vdr-test-nodejs": "^0.1.3", + "rimraf": "~4.0.7", + "typescript": "~4.9.4" + } +} diff --git a/packages/indy-vdr/src/error/IndyVdrError.ts b/packages/indy-vdr/src/error/IndyVdrError.ts new file mode 100644 index 0000000000..501f428640 --- /dev/null +++ b/packages/indy-vdr/src/error/IndyVdrError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +export class IndyVdrError extends AriesFrameworkError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/indy-vdr/src/error/IndyVdrNotConfiguredError.ts b/packages/indy-vdr/src/error/IndyVdrNotConfiguredError.ts new file mode 100644 index 0000000000..75cf40c9f6 --- /dev/null +++ b/packages/indy-vdr/src/error/IndyVdrNotConfiguredError.ts @@ -0,0 +1,7 @@ +import { IndyVdrError } from './IndyVdrError' + +export class IndyVdrNotConfiguredError extends IndyVdrError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/indy-vdr/src/error/IndyVdrNotFound.ts b/packages/indy-vdr/src/error/IndyVdrNotFound.ts new file mode 100644 index 0000000000..00b1b94c47 --- /dev/null +++ b/packages/indy-vdr/src/error/IndyVdrNotFound.ts @@ -0,0 +1,7 @@ +import { IndyVdrError } from './IndyVdrError' + +export class IndyVdrNotFoundError extends IndyVdrError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/indy-vdr/src/error/index.ts b/packages/indy-vdr/src/error/index.ts new file mode 100644 index 0000000000..f062bfbed0 --- /dev/null +++ b/packages/indy-vdr/src/error/index.ts @@ -0,0 +1,3 @@ +export * from './IndyVdrError' +export * from './IndyVdrNotFound' +export * from './IndyVdrNotConfiguredError' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts new file mode 100644 index 0000000000..8a5ca6c21a --- /dev/null +++ b/packages/indy-vdr/src/index.ts @@ -0,0 +1,6 @@ +try { + // eslint-disable-next-line import/no-extraneous-dependencies + require('indy-vdr-test-nodejs') +} catch (error) { + throw new Error('Error registering nodejs bindings for Indy VDR') +} diff --git a/packages/indy-vdr/src/pool/IndyVdrPool.ts b/packages/indy-vdr/src/pool/IndyVdrPool.ts new file mode 100644 index 0000000000..6958d882ab --- /dev/null +++ b/packages/indy-vdr/src/pool/IndyVdrPool.ts @@ -0,0 +1,184 @@ +import type { Logger, AgentContext, Key } from '@aries-framework/core' +import type { IndyVdrRequest, IndyVdrPool as indyVdrPool } from 'indy-vdr-test-shared' + +import { TypedArrayEncoder } from '@aries-framework/core' +import { + GetTransactionAuthorAgreementRequest, + GetAcceptanceMechanismsRequest, + PoolCreate, + indyVdr, +} from 'indy-vdr-test-shared' + +import { IndyVdrError } from '../error' + +export interface TransactionAuthorAgreement { + version?: `${number}.${number}` | `${number}` + acceptanceMechanism: string +} + +export interface AuthorAgreement { + digest: string + version: string + text: string + ratification_ts: number + acceptanceMechanisms: AcceptanceMechanisms +} + +export interface AcceptanceMechanisms { + aml: Record + amlContext: string + version: string +} + +export interface IndyVdrPoolConfig { + genesisTransactions: string + isProduction: boolean + indyNamespace: string + transactionAuthorAgreement?: TransactionAuthorAgreement +} + +export class IndyVdrPool { + private _pool?: indyVdrPool + private logger: Logger + private poolConfig: IndyVdrPoolConfig + public authorAgreement?: AuthorAgreement | null + + public constructor(poolConfig: IndyVdrPoolConfig, logger: Logger) { + this.logger = logger + this.poolConfig = poolConfig + } + + public get indyNamespace(): string { + return this.poolConfig.indyNamespace + } + + public get config() { + return this.poolConfig + } + + public async connect() { + this._pool = new PoolCreate({ + parameters: { + transactions: this.config.genesisTransactions, + }, + }) + + return this.pool.handle + } + + private get pool(): indyVdrPool { + if (!this._pool) { + throw new IndyVdrError('Pool is not connected. Make sure to call .connect() first') + } + + return this._pool + } + + public close() { + if (!this.pool) { + throw new IndyVdrError("Can't close pool. Pool is not connected") + } + + // FIXME: this method doesn't work?? + // this.pool.close() + } + + public async submitWriteRequest( + agentContext: AgentContext, + request: Request, + signingKey: Key + ) { + await this.appendTaa(request) + + const signature = await agentContext.wallet.sign({ + data: TypedArrayEncoder.fromString(request.signatureInput), + key: signingKey, + }) + + request.setSignature({ + signature, + }) + + return await this.pool.submitRequest(request) + } + + public async submitReadRequest(request: Request) { + return await this.pool.submitRequest(request) + } + + private async appendTaa(request: IndyVdrRequest) { + const authorAgreement = await this.getTransactionAuthorAgreement() + const poolTaa = this.config.transactionAuthorAgreement + + // If ledger does not have TAA, we can just send request + if (authorAgreement == null) { + return request + } + + // Ledger has taa but user has not specified which one to use + if (!poolTaa) { + throw new IndyVdrError( + `Please, specify a transaction author agreement with version and acceptance mechanism. ${JSON.stringify( + authorAgreement + )}` + ) + } + + // Throw an error if the pool doesn't have the specified version and acceptance mechanism + if ( + authorAgreement.version !== poolTaa.version || + !authorAgreement.acceptanceMechanisms.aml[poolTaa.acceptanceMechanism] + ) { + // Throw an error with a helpful message + const errMessage = `Unable to satisfy matching TAA with mechanism ${JSON.stringify( + poolTaa.acceptanceMechanism + )} and version ${poolTaa.version} in pool.\n Found ${JSON.stringify( + authorAgreement.acceptanceMechanisms.aml + )} and version ${authorAgreement.version} in pool.` + throw new IndyVdrError(errMessage) + } + + const acceptance = indyVdr.prepareTxnAuthorAgreementAcceptance({ + text: authorAgreement.text, + version: authorAgreement.version, + taaDigest: authorAgreement.digest, + time: Math.floor(new Date().getTime() / 1000), + acceptanceMechanismType: poolTaa.acceptanceMechanism, + }) + + request.setTransactionAuthorAgreementAcceptance({ acceptance }) + } + + private async getTransactionAuthorAgreement(): Promise { + // TODO Replace this condition with memoization + if (this.authorAgreement !== undefined) { + return this.authorAgreement + } + + const taaRequest = new GetTransactionAuthorAgreementRequest({}) + const taaResponse = await this.submitReadRequest(taaRequest) + + const acceptanceMechanismRequest = new GetAcceptanceMechanismsRequest({}) + const acceptanceMechanismResponse = await this.submitReadRequest(acceptanceMechanismRequest) + + const taaData = taaResponse.result.data + + // TAA can be null + if (taaData == null) { + this.authorAgreement = null + return null + } + + // If TAA is not null, we can be sure AcceptanceMechanisms is also not null + const authorAgreement = taaData as Omit + + // FIME: remove cast when https://github.com/hyperledger/indy-vdr/pull/142 is released + const acceptanceMechanisms = acceptanceMechanismResponse.result.data as unknown as AcceptanceMechanisms + this.authorAgreement = { + ...authorAgreement, + acceptanceMechanisms, + } + + return this.authorAgreement + } +} diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts new file mode 100644 index 0000000000..2cc1b5a206 --- /dev/null +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -0,0 +1,209 @@ +import type { IndyVdrPoolConfig } from './IndyVdrPool' +import type { AgentContext } from '@aries-framework/core' +import type { GetNymResponse } from 'indy-vdr-test-shared' + +import { Logger, InjectionSymbols, injectable, inject, CacheModuleConfig } from '@aries-framework/core' +import { GetNymRequest } from 'indy-vdr-test-shared' + +import { IndyVdrError, IndyVdrNotFoundError, IndyVdrNotConfiguredError } from '../error' +import { isSelfCertifiedDid, DID_INDY_REGEX } from '../utils/did' +import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' + +import { IndyVdrPool } from './IndyVdrPool' + +export interface CachedDidResponse { + nymResponse: { + did: string + verkey: string + } + indyNamespace: string +} +@injectable() +export class IndyVdrPoolService { + public pools: IndyVdrPool[] = [] + private logger: Logger + + public constructor(@inject(InjectionSymbols.Logger) logger: Logger) { + this.logger = logger + } + + public setPools(poolConfigs: IndyVdrPoolConfig[]) { + this.pools = poolConfigs.map((poolConfig) => new IndyVdrPool(poolConfig, this.logger)) + } + + /** + * Create connections to all ledger pools + */ + public async connectToPools() { + const handleArray: number[] = [] + // Sequentially connect to pools so we don't use up too many resources connecting in parallel + for (const pool of this.pools) { + this.logger.debug(`Connecting to pool: ${pool.indyNamespace}`) + const poolHandle = await pool.connect() + this.logger.debug(`Finished connection to pool: ${pool.indyNamespace}`) + handleArray.push(poolHandle) + } + return handleArray + } + + /** + * Get the most appropriate pool for the given did. + * If the did is a qualified indy did, the pool will be determined based on the namespace. + * If it is a legacy unqualified indy did, the pool will be determined based on the algorithm as described in this document: + * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit + */ + public async getPoolForDid(agentContext: AgentContext, did: string): Promise { + // Check if the did starts with did:indy + const match = did.match(DID_INDY_REGEX) + + if (match) { + const [, namespace] = match + + const pool = this.getPoolForNamespace(namespace) + + if (pool) return pool + + throw new IndyVdrError(`Pool for indy namespace '${namespace}' not found`) + } else { + return await this.getPoolForLegacyDid(agentContext, did) + } + } + + private async getPoolForLegacyDid(agentContext: AgentContext, did: string): Promise { + const pools = this.pools + + if (pools.length === 0) { + throw new IndyVdrNotConfiguredError( + "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + ) + } + + const didCache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + + const cachedNymResponse = await didCache.get(agentContext, `IndyVdrPoolService:${did}`) + const pool = this.pools.find((pool) => pool.indyNamespace === cachedNymResponse?.indyNamespace) + + // If we have the nym response with associated pool in the cache, we'll use that + if (cachedNymResponse && pool) { + this.logger.trace(`Found ledger id '${pool.indyNamespace}' for did '${did}' in cache`) + return pool + } + + const { successful, rejected } = await this.getSettledDidResponsesFromPools(did, pools) + + if (successful.length === 0) { + const allNotFound = rejected.every((e) => e.reason instanceof IndyVdrNotFoundError) + const rejectedOtherThanNotFound = rejected.filter((e) => !(e.reason instanceof IndyVdrNotFoundError)) + + // All ledgers returned response that the did was not found + if (allNotFound) { + throw new IndyVdrNotFoundError(`Did '${did}' not found on any of the ledgers (total ${this.pools.length}).`) + } + + // one or more of the ledgers returned an unknown error + throw new IndyVdrError( + `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers`, + { cause: rejectedOtherThanNotFound[0].reason } + ) + } + + // If there are self certified DIDs we always prefer it over non self certified DIDs + // We take the first self certifying DID as we take the order in the + // indyLedgers config as the order of preference of ledgers + let value = successful.find((response) => + isSelfCertifiedDid(response.value.did.nymResponse.did, response.value.did.nymResponse.verkey) + )?.value + + if (!value) { + // Split between production and nonProduction ledgers. If there is at least one + // successful response from a production ledger, only keep production ledgers + // otherwise we only keep the non production ledgers. + const production = successful.filter((s) => s.value.pool.config.isProduction) + const nonProduction = successful.filter((s) => !s.value.pool.config.isProduction) + const productionOrNonProduction = production.length >= 1 ? production : nonProduction + + // We take the first value as we take the order in the indyLedgers config as + // the order of preference of ledgers + value = productionOrNonProduction[0].value + } + + await didCache.set(agentContext, did, { + nymResponse: { + did: value.did.nymResponse.did, + verkey: value.did.nymResponse.verkey, + }, + indyNamespace: value.did.indyNamespace, + }) + return value.pool + } + + private async getSettledDidResponsesFromPools(did: string, pools: IndyVdrPool[]) { + this.logger.trace(`Retrieving did '${did}' from ${pools.length} ledgers`) + const didResponses = await allSettled(pools.map((pool) => this.getDidFromPool(did, pool))) + + const successful = onlyFulfilled(didResponses) + this.logger.trace(`Retrieved ${successful.length} responses from ledgers for did '${did}'`) + + const rejected = onlyRejected(didResponses) + + return { + rejected, + successful, + } + } + + /** + * Get the most appropriate pool for the given indyNamespace + */ + public getPoolForNamespace(indyNamespace: string) { + if (this.pools.length === 0) { + throw new IndyVdrNotConfiguredError( + "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + ) + } + + const pool = this.pools.find((pool) => pool.indyNamespace === indyNamespace) + + if (!pool) { + throw new IndyVdrError(`No ledgers found for IndyNamespace '${indyNamespace}'.`) + } + + return pool + } + + private async getDidFromPool(did: string, pool: IndyVdrPool): Promise { + try { + this.logger.trace(`Get public did '${did}' from ledger '${pool.indyNamespace}'`) + const request = await new GetNymRequest({ dest: did }) + + this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.indyNamespace}'`) + const response = await pool.submitReadRequest(request) + + if (!response.result.data) { + throw new IndyVdrNotFoundError(`Did ${did} not found on indy pool with namespace ${pool.indyNamespace}`) + } + + const result = JSON.parse(response.result.data) + + this.logger.trace(`Retrieved did '${did}' from ledger '${pool.indyNamespace}'`, result) + + return { + did: result, + pool, + response, + } + } catch (error) { + this.logger.trace(`Error retrieving did '${did}' from ledger '${pool.indyNamespace}'`, { + error, + did, + }) + throw error + } + } +} + +export interface PublicDidRequest { + did: CachedDidResponse + pool: IndyVdrPool + response: GetNymResponse +} diff --git a/packages/indy-vdr/src/pool/index.ts b/packages/indy-vdr/src/pool/index.ts new file mode 100644 index 0000000000..1e1f1b52f8 --- /dev/null +++ b/packages/indy-vdr/src/pool/index.ts @@ -0,0 +1 @@ +export * from './IndyVdrPool' diff --git a/packages/indy-vdr/src/utils/did.ts b/packages/indy-vdr/src/utils/did.ts new file mode 100644 index 0000000000..9cda8ee95d --- /dev/null +++ b/packages/indy-vdr/src/utils/did.ts @@ -0,0 +1,61 @@ +/** + * Based on DidUtils implementation in Aries Framework .NET + * @see: https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Utils/DidUtils.cs + * + * Some context about full verkeys versus abbreviated verkeys: + * A standard verkey is 32 bytes, and by default in Indy the DID is chosen as the first 16 bytes of that key, before base58 encoding. + * An abbreviated verkey replaces the first 16 bytes of the verkey with ~ when it matches the DID. + * + * When a full verkey is used to register on the ledger, this is stored as a full verkey on the ledger and also returned from the ledger as a full verkey. + * The same applies to an abbreviated verkey. If an abbreviated verkey is used to register on the ledger, this is stored as an abbreviated verkey on the ledger and also returned from the ledger as an abbreviated verkey. + * + * For this reason we need some methods to check whether verkeys are full or abbreviated, so we can align this with `indy.abbreviateVerkey` + * + * Aries Framework .NET also abbreviates verkey before sending to ledger: + * https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Ledger/DefaultLedgerService.cs#L139-L147 + */ + +import { TypedArrayEncoder } from '@aries-framework/core' + +export const DID_INDY_REGEX = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)):([1-9A-HJ-NP-Za-km-z]{21,22})$/ +export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ + +/** + * Check whether the did is a self certifying did. If the verkey is abbreviated this method + * will always return true. Make sure that the verkey you pass in this method belongs to the + * did passed in + * + * @return Boolean indicating whether the did is self certifying + */ +export function isSelfCertifiedDid(did: string, verkey: string): boolean { + // If the verkey is Abbreviated, it means the full verkey + // is the did + the verkey + if (isAbbreviatedVerkey(verkey)) { + return true + } + + const didFromVerkey = indyDidFromPublicKeyBase58(verkey) + + if (didFromVerkey === did) { + return true + } + + return false +} + +export function indyDidFromPublicKeyBase58(publicKeyBase58: string): string { + const buffer = TypedArrayEncoder.fromBase58(publicKeyBase58) + + const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + + return did +} + +/** + * Check a base58 encoded string against a regex expression to determine if it is a valid abbreviated verkey + * @param verkey Base58 encoded string representation of an abbreviated verkey + * @returns Boolean indicating if the string is a valid abbreviated verkey + */ +export function isAbbreviatedVerkey(verkey: string): boolean { + return ABBREVIATED_VERKEY_REGEX.test(verkey) +} diff --git a/packages/indy-vdr/src/utils/promises.ts b/packages/indy-vdr/src/utils/promises.ts new file mode 100644 index 0000000000..0e843d73b5 --- /dev/null +++ b/packages/indy-vdr/src/utils/promises.ts @@ -0,0 +1,44 @@ +// This file polyfills the allSettled method introduced in ESNext + +export type AllSettledFulfilled = { + status: 'fulfilled' + value: T +} + +export type AllSettledRejected = { + status: 'rejected' + // eslint-disable-next-line @typescript-eslint/no-explicit-any + reason: any +} + +export function allSettled(promises: Promise[]) { + return Promise.all( + promises.map((p) => + p + .then( + (value) => + ({ + status: 'fulfilled', + value, + } as AllSettledFulfilled) + ) + .catch( + (reason) => + ({ + status: 'rejected', + reason, + } as AllSettledRejected) + ) + ) + ) +} + +export function onlyFulfilled(entries: Array | AllSettledRejected>) { + // We filter for only the rejected values, so we can safely cast the type + return entries.filter((e) => e.status === 'fulfilled') as AllSettledFulfilled[] +} + +export function onlyRejected(entries: Array | AllSettledRejected>) { + // We filter for only the rejected values, so we can safely cast the type + return entries.filter((e) => e.status === 'rejected') as AllSettledRejected[] +} diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts new file mode 100644 index 0000000000..5920344527 --- /dev/null +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -0,0 +1,223 @@ +import type { Key } from '@aries-framework/core' + +import { IndyWallet, KeyType, SigningProviderRegistry, TypedArrayEncoder } from '@aries-framework/core' +import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from 'indy-vdr-test-shared' + +import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import testLogger from '../../core/tests/logger' +import { IndyVdrPool } from '../src/pool' +import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' + +const indyVdrPoolService = new IndyVdrPoolService(testLogger) +const wallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) +const agentConfig = getAgentConfig('IndyVdrPoolService') +const agentContext = getAgentContext({ wallet, agentConfig }) + +const config = { + isProduction: false, + genesisTransactions, + indyNamespace: `pool:localtest`, + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, +} as const + +let signerKey: Key + +indyVdrPoolService.setPools([config]) + +describe('IndyVdrPoolService', () => { + beforeAll(async () => { + await indyVdrPoolService.connectToPools() + + if (agentConfig.walletConfig) { + await wallet.createAndOpen(agentConfig.walletConfig) + } + + signerKey = await wallet.createKey({ seed: '000000000000000000000000Trustee9', keyType: KeyType.Ed25519 }) + }) + + afterAll(async () => { + for (const pool of indyVdrPoolService.pools) { + pool.close() + } + + await wallet.delete() + }) + + describe('DIDs', () => { + test('can get a pool based on the namespace', async () => { + const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') + expect(pool).toBeInstanceOf(IndyVdrPool) + expect(pool.config).toEqual(config) + }) + + test('can resolve a did using the pool', async () => { + const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') + + const request = new GetNymRequest({ + dest: 'TL1EaPFCZ8Si5aUrqScBDt', + }) + + const response = await pool.submitReadRequest(request) + + expect(response).toMatchObject({ + op: 'REPLY', + result: { + dest: 'TL1EaPFCZ8Si5aUrqScBDt', + type: '105', + data: expect.any(String), + identifier: 'LibindyDid111111111111', + reqId: expect.any(Number), + seqNo: expect.any(Number), + txnTime: expect.any(Number), + state_proof: expect.any(Object), + }, + }) + + expect(JSON.parse(response.result.data as string)).toMatchObject({ + dest: 'TL1EaPFCZ8Si5aUrqScBDt', + identifier: 'V4SGRU86Z58d6TV7PBUe6f', + role: '0', + seqNo: expect.any(Number), + txnTime: expect.any(Number), + verkey: '~43X4NhAFqREffK7eWdKgFH', + }) + }) + + test('can write a did using the pool', async () => { + const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') + + // prepare the DID we are going to write to the ledger + const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) + const buffer = TypedArrayEncoder.fromBase58(key.publicKeyBase58) + const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + + const request = new NymRequest({ + dest: did, + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + verkey: key.publicKeyBase58, + }) + + const response = await pool.submitWriteRequest(agentContext, request, signerKey) + + expect(response).toMatchObject({ + op: 'REPLY', + result: { + txn: { + protocolVersion: 2, + metadata: expect.any(Object), + data: expect.any(Object), + type: '1', + }, + ver: '1', + rootHash: expect.any(String), + txnMetadata: expect.any(Object), + }, + }) + }) + }) + + describe('Schemas & credential Definition', () => { + test('can write a schema using the pool', async () => { + const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') + + const dynamicVersion = `1.${Math.random() * 100}` + + const schemaRequest = new SchemaRequest({ + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + schema: { + id: 'test-schema-id', + name: 'test-schema', + ver: '1.0', + version: dynamicVersion, + attrNames: ['first_name', 'last_name', 'age'], + }, + }) + + const schemaResponse = await pool.submitWriteRequest(agentContext, schemaRequest, signerKey) + + expect(schemaResponse).toMatchObject({ + op: 'REPLY', + result: { + ver: '1', + txn: { + metadata: expect.any(Object), + type: '101', + data: { + data: { + attr_names: expect.arrayContaining(['age', 'last_name', 'first_name']), + name: 'test-schema', + version: dynamicVersion, + }, + }, + }, + }, + }) + + const credentialDefinitionRequest = new CredentialDefinitionRequest({ + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + credentialDefinition: { + ver: '1.0', + id: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.result.txnMetadata.seqNo}:TAG`, + // must be string version of the schema seqNo + schemaId: `${schemaResponse.result.txnMetadata.seqNo}`, + type: 'CL', + tag: 'TAG', + value: { + primary: { + n: '95671911213029889766246243339609567053285242961853979532076192834533577534909796042025401129640348836502648821408485216223269830089771714177855160978214805993386076928594836829216646288195127289421136294309746871614765411402917891972999085287429566166932354413679994469616357622976775651506242447852304853465380257226445481515631782793575184420720296120464167257703633829902427169144462981949944348928086406211627174233811365419264314148304536534528344413738913277713548403058098093453580992173145127632199215550027527631259565822872315784889212327945030315062879193999012349220118290071491899498795367403447663354833', + s: '1573939820553851804028472930351082111827449763317396231059458630252708273163050576299697385049087601314071156646675105028237105229428440185022593174121924731226634356276616495327358864865629675802738680754755949997611920669823449540027707876555408118172529688443208301403297680159171306000341239398135896274940688268460793682007115152428685521865921925309154307574955324973580144009271977076586453011938089159885164705002797196738438392179082905738155386545935208094240038135576042886730802817809757582039362798495805441520744154270346780731494125065136433163757697326955962282840631850597919384092584727207908978907', + r: { + master_secret: + '51468326064458249697956272807708948542001661888325200180968238787091473418947480867518174106588127385097619219536294589148765074804124925845579871788369264160902401097166484002617399484700234182426993061977152961670486891123188739266793651668791365808983166555735631354925174224786218771453042042304773095663181121735652667614424198057134974727791329623974680096491276337756445057223988781749506082654194307092164895251308088903000573135447235553684949564809677864522417041639512933806794232354223826262154508950271949764583849083972967642587778197779127063591201123312548182885603427440981731822883101260509710567731', + last_name: + '35864556460959997092903171610228165251001245539613587319116151716453114432309327039517115215674024166920383445379522674504803469517283236033110568676156285676664363558333716898161685255450536856645604857714925836474250821415182026707218622134953915013803750771185050002646661004119778318524426368842019753903741998256374803456282688037624993010626333853831264356355867746685055670790915539230702546586615988121383960277550317876816983602795121749533628953449405383896799464872758725899520173321672584180060465965090049734285011738428381648013150818429882628144544132356242262467090140003979917439514443707537952643217', + first_name: + '26405366527417391838431479783966663952336302347775179063968690502492620867161212873635806190080000833725932174641667734138216137047349915190546601368424742647800764149890590518336588437317392528514313749533980651547425554257026971104775208127915118918084350210726664749850578299247705298976657301433446491575776774836993110356033664644761593799921221474617858131678955318702706530853801195330271860527250931569815553226145458665481867408279941785848264018364216087471931232367137301987457054918438087686484522112532447779498424748261678616461026788516567300969886029412198319909977473167405879110243445062391837349387', + age: '19865805272519696320755573045337531955436490760876870776207490804137339344112305203631892390827288264857621916650098902064979838987400911652887344763586495880167030031364467726355103327059673023946234460960685398768709062405377107912774045508870580108596597470880834205563197111550140867466625683117333370595295321833757429488192170551320637065066368716366317421169802474954914904380304190861641082310805418122837214965865969459724848071006870574514215255412289237027267424055400593307112849859757094597401668252862525566316402695830217450073667487951799749275437192883439584518905943435472478496028380016245355151988', + }, + rctxt: + '17146114573198643698878017247599007910707723139165264508694101989891626297408755744139587708989465136799243292477223763665064840330721616213638280284119891715514951989022398510785960099708705561761504012512387129498731093386014964896897751536856287377064154297370092339714578039195258061017640952790913108285519632654466006255438773382930416822756630391947263044087385305540191237328903426888518439803354213792647775798033294505898635058814132665832000734168261793545453678083703704122695006541391598116359796491845268631009298069826949515604008666680160398698425061157356267086946953480945396595351944425658076127674', + z: '57056568014385132434061065334124327103768023932445648883765905576432733866307137325457775876741578717650388638737098805750938053855430851133826479968450532729423746605371536096355616166421996729493639634413002114547787617999178137950004782677177313856876420539744625174205603354705595789330008560775613287118432593300023801651460885523314713996258581986238928077688246511704050386525431448517516821261983193275502089060128363906909778842476516981025598807378338053788433033754999771876361716562378445777250912525673660842724168260417083076824975992327559199634032439358787956784395443246565622469187082767614421691234', + }, + }, + }, + }) + + const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, signerKey) + + expect(response).toMatchObject({ + op: 'REPLY', + result: { + ver: '1', + txn: { + metadata: expect.any(Object), + type: '102', + data: { + data: { + primary: { + r: { + last_name: + '35864556460959997092903171610228165251001245539613587319116151716453114432309327039517115215674024166920383445379522674504803469517283236033110568676156285676664363558333716898161685255450536856645604857714925836474250821415182026707218622134953915013803750771185050002646661004119778318524426368842019753903741998256374803456282688037624993010626333853831264356355867746685055670790915539230702546586615988121383960277550317876816983602795121749533628953449405383896799464872758725899520173321672584180060465965090049734285011738428381648013150818429882628144544132356242262467090140003979917439514443707537952643217', + first_name: + '26405366527417391838431479783966663952336302347775179063968690502492620867161212873635806190080000833725932174641667734138216137047349915190546601368424742647800764149890590518336588437317392528514313749533980651547425554257026971104775208127915118918084350210726664749850578299247705298976657301433446491575776774836993110356033664644761593799921221474617858131678955318702706530853801195330271860527250931569815553226145458665481867408279941785848264018364216087471931232367137301987457054918438087686484522112532447779498424748261678616461026788516567300969886029412198319909977473167405879110243445062391837349387', + age: '19865805272519696320755573045337531955436490760876870776207490804137339344112305203631892390827288264857621916650098902064979838987400911652887344763586495880167030031364467726355103327059673023946234460960685398768709062405377107912774045508870580108596597470880834205563197111550140867466625683117333370595295321833757429488192170551320637065066368716366317421169802474954914904380304190861641082310805418122837214965865969459724848071006870574514215255412289237027267424055400593307112849859757094597401668252862525566316402695830217450073667487951799749275437192883439584518905943435472478496028380016245355151988', + master_secret: + '51468326064458249697956272807708948542001661888325200180968238787091473418947480867518174106588127385097619219536294589148765074804124925845579871788369264160902401097166484002617399484700234182426993061977152961670486891123188739266793651668791365808983166555735631354925174224786218771453042042304773095663181121735652667614424198057134974727791329623974680096491276337756445057223988781749506082654194307092164895251308088903000573135447235553684949564809677864522417041639512933806794232354223826262154508950271949764583849083972967642587778197779127063591201123312548182885603427440981731822883101260509710567731', + }, + z: '57056568014385132434061065334124327103768023932445648883765905576432733866307137325457775876741578717650388638737098805750938053855430851133826479968450532729423746605371536096355616166421996729493639634413002114547787617999178137950004782677177313856876420539744625174205603354705595789330008560775613287118432593300023801651460885523314713996258581986238928077688246511704050386525431448517516821261983193275502089060128363906909778842476516981025598807378338053788433033754999771876361716562378445777250912525673660842724168260417083076824975992327559199634032439358787956784395443246565622469187082767614421691234', + rctxt: + '17146114573198643698878017247599007910707723139165264508694101989891626297408755744139587708989465136799243292477223763665064840330721616213638280284119891715514951989022398510785960099708705561761504012512387129498731093386014964896897751536856287377064154297370092339714578039195258061017640952790913108285519632654466006255438773382930416822756630391947263044087385305540191237328903426888518439803354213792647775798033294505898635058814132665832000734168261793545453678083703704122695006541391598116359796491845268631009298069826949515604008666680160398698425061157356267086946953480945396595351944425658076127674', + n: '95671911213029889766246243339609567053285242961853979532076192834533577534909796042025401129640348836502648821408485216223269830089771714177855160978214805993386076928594836829216646288195127289421136294309746871614765411402917891972999085287429566166932354413679994469616357622976775651506242447852304853465380257226445481515631782793575184420720296120464167257703633829902427169144462981949944348928086406211627174233811365419264314148304536534528344413738913277713548403058098093453580992173145127632199215550027527631259565822872315784889212327945030315062879193999012349220118290071491899498795367403447663354833', + s: '1573939820553851804028472930351082111827449763317396231059458630252708273163050576299697385049087601314071156646675105028237105229428440185022593174121924731226634356276616495327358864865629675802738680754755949997611920669823449540027707876555408118172529688443208301403297680159171306000341239398135896274940688268460793682007115152428685521865921925309154307574955324973580144009271977076586453011938089159885164705002797196738438392179082905738155386545935208094240038135576042886730802817809757582039362798495805441520744154270346780731494125065136433163757697326955962282840631850597919384092584727207908978907', + }, + }, + signature_type: 'CL', + ref: schemaResponse.result.txnMetadata.seqNo, + tag: 'TAG', + }, + }, + }, + }) + }) + }) +}) diff --git a/packages/indy-vdr/tests/setup.ts b/packages/indy-vdr/tests/setup.ts new file mode 100644 index 0000000000..ce7749d25e --- /dev/null +++ b/packages/indy-vdr/tests/setup.ts @@ -0,0 +1,4 @@ +// Needed to register indy-vdr node bindings +import '../src/index' + +jest.setTimeout(20000) diff --git a/packages/indy-vdr/tsconfig.build.json b/packages/indy-vdr/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/indy-vdr/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/indy-vdr/tsconfig.json b/packages/indy-vdr/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/indy-vdr/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/yarn.lock b/yarn.lock index c79dff1e1c..30954778a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1795,6 +1795,21 @@ semver "^7.3.5" tar "^6.1.11" +"@mapbox/node-pre-gyp@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c" + integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + "@mattrglobal/bbs-signatures@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.0.0.tgz#8ff272c6d201aadab7e08bd84dbfd6e0d48ba12d" @@ -3087,6 +3102,14 @@ array-includes@^3.1.6: get-intrinsic "^1.1.3" is-string "^1.0.7" +array-index@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-index/-/array-index-1.0.0.tgz#ec56a749ee103e4e08c790b9c353df16055b97f9" + integrity sha512-jesyNbBkLQgGZMSwA1FanaFjalb1mZUGxGeUEkSDidzgrbjBGhvizJkaItdhkt8eIHFOJC7nDsrXk+BaehTdRw== + dependencies: + debug "^2.2.0" + es6-symbol "^3.0.2" + array-map@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.1.tgz#d1bf3cc8813a7daaa335e5c8eb21d9d06230c1a7" @@ -4327,6 +4350,14 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + dargs@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" @@ -4779,6 +4810,32 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.62" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" + integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + next-tick "^1.1.0" + +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.0.2, es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -5138,6 +5195,13 @@ express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" +ext@^1.1.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -6075,6 +6139,23 @@ indy-sdk@^1.16.0-dev-1636: nan "^2.11.1" node-gyp "^8.0.0" +indy-vdr-test-nodejs@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/indy-vdr-test-nodejs/-/indy-vdr-test-nodejs-0.1.3.tgz#97eaf38b1035bfabcd772a8399f23d766dfd493e" + integrity sha512-E6r86QGbswa+hBgMJKVWJycqvvmOgepFMDaAvuZQtxQK1Z2gghco6m/9EOAPYaJRs0MMEEhzUGhvtSpCzeZ6sg== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.10" + ffi-napi "^4.0.3" + indy-vdr-test-shared "0.1.3" + ref-array-di "^1.2.2" + ref-napi "^3.0.3" + ref-struct-di "^1.1.1" + +indy-vdr-test-shared@0.1.3, indy-vdr-test-shared@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/indy-vdr-test-shared/-/indy-vdr-test-shared-0.1.3.tgz#3b5ee9492ebc3367a027670aa9686c493de5929c" + integrity sha512-fdgV388zi3dglu49kqrV+i40w+18uJkv96Tk4nziLdP280SLnZKKnIRAiq11Hj8aHpnZmwMloyQCsIyQZDZk2g== + infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -8281,6 +8362,11 @@ neon-cli@0.8.2: validate-npm-package-license "^3.0.4" validate-npm-package-name "^3.0.0" +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -9590,6 +9676,14 @@ reduce-flatten@^2.0.0: resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== +ref-array-di@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ref-array-di/-/ref-array-di-1.2.2.tgz#ceee9d667d9c424b5a91bb813457cc916fb1f64d" + integrity sha512-jhCmhqWa7kvCVrWhR/d7RemkppqPUdxEil1CtTtm7FkZV8LcHHCK3Or9GinUiFP5WY3k0djUkMvhBhx49Jb2iA== + dependencies: + array-index "^1.0.0" + debug "^3.1.0" + "ref-napi@^2.0.1 || ^3.0.2", ref-napi@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/ref-napi/-/ref-napi-3.0.3.tgz#e259bfc2bbafb3e169e8cd9ba49037dd00396b22" @@ -9600,7 +9694,7 @@ reduce-flatten@^2.0.0: node-addon-api "^3.0.0" node-gyp-build "^4.2.1" -ref-struct-di@^1.1.0: +ref-struct-di@^1.1.0, ref-struct-di@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ref-struct-di/-/ref-struct-di-1.1.1.tgz#5827b1d3b32372058f177547093db1fe1602dc10" integrity sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g== From 13f374079262168f90ec7de7c3393beb9651295c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 29 Jan 2023 16:32:55 +0100 Subject: [PATCH 019/139] feat(anoncreds): add legacy indy credential format (#1220) Signed-off-by: Timo Glastra --- packages/anoncreds/package.json | 5 +- .../src/formats/AnonCredsCredentialFormat.ts | 89 +++ .../src/formats/LegacyIndyCredentialFormat.ts | 67 ++ .../LegacyIndyCredentialFormatService.ts | 608 ++++++++++++++++++ .../LegacyIndyCredentialFormatService.test.ts | 224 +++++++ packages/anoncreds/src/models/exchange.ts | 41 +- packages/anoncreds/src/models/internal.ts | 27 +- .../src/services/AnonCredsHolderService.ts | 10 +- .../services/AnonCredsHolderServiceOptions.ts | 31 +- .../src/services/AnonCredsIssuerService.ts | 2 + .../services/AnonCredsIssuerServiceOptions.ts | 4 +- .../src/services/AnonCredsVerifierService.ts | 2 + .../registry/AnonCredsRegistryService.ts | 2 +- .../registry/CredentialDefinitionOptions.ts | 6 +- .../registry/RevocationListOptions.ts | 2 +- .../RevocationRegistryDefinitionOptions.ts | 2 +- .../src/services/registry/SchemaOptions.ts | 6 +- .../AnonCredsRegistryService.test.ts | 8 +- .../src/utils/__tests__/credential.test.ts | 225 +++++++ packages/anoncreds/src/utils/credential.ts | 200 ++++++ packages/anoncreds/src/utils/metadata.ts | 29 + .../tests/InMemoryAnonCredsRegistry.ts | 155 +++++ packages/core/src/index.ts | 4 + .../formats/indy/IndyCredentialFormat.ts | 5 - .../core/src/modules/credentials/index.ts | 1 + packages/core/src/storage/Metadata.ts | 14 +- packages/core/tests/helpers.ts | 9 +- .../services/IndySdkAnonCredsRegistry.ts | 56 +- .../services/IndySdkHolderService.ts | 35 +- .../services/IndySdkIssuerService.ts | 16 +- .../services/IndySdkRevocationService.ts | 23 +- .../services/IndySdkUtilitiesService.ts | 65 -- .../services/IndySdkVerifierService.ts | 4 +- .../indy-sdk/src/anoncreds/utils/proverDid.ts | 12 + .../indy-sdk/src/anoncreds/utils/tails.ts | 45 ++ yarn.lock | 2 +- 36 files changed, 1839 insertions(+), 197 deletions(-) create mode 100644 packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts create mode 100644 packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts create mode 100644 packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts create mode 100644 packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts create mode 100644 packages/anoncreds/src/utils/__tests__/credential.test.ts create mode 100644 packages/anoncreds/src/utils/credential.ts create mode 100644 packages/anoncreds/src/utils/metadata.ts create mode 100644 packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts delete mode 100644 packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/proverDid.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/tails.ts diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 7947fe5642..de4e294a54 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -25,9 +25,12 @@ "test": "jest" }, "dependencies": { - "@aries-framework/core": "0.3.3" + "@aries-framework/core": "0.3.3", + "@aries-framework/node": "0.3.3", + "bn.js": "^5.2.1" }, "devDependencies": { + "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts new file mode 100644 index 0000000000..fd6ebf7fcb --- /dev/null +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts @@ -0,0 +1,89 @@ +import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models' +import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachment } from '@aries-framework/core' + +/** + * This defines the module payload for calling CredentialsApi.createProposal + * or CredentialsApi.negotiateOffer + */ +export interface AnonCredsProposeCredentialFormat { + schemaIssuerId?: string + schemaId?: string + schemaName?: string + schemaVersion?: string + + credentialDefinitionId?: string + issuerId?: string + + attributes?: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] + + // Kept for backwards compatibility + schemaIssuerDid?: string + issuerDid?: string +} + +/** + * This defines the module payload for calling CredentialsApi.acceptProposal + */ +export interface AnonCredsAcceptProposalFormat { + credentialDefinitionId?: string + attributes?: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] +} + +/** + * This defines the module payload for calling CredentialsApi.acceptOffer. No options are available for this + * method, so it's an empty object + */ +export type AnonCredsAcceptOfferFormat = Record + +/** + * This defines the module payload for calling CredentialsApi.offerCredential + * or CredentialsApi.negotiateProposal + */ +export interface AnonCredsOfferCredentialFormat { + credentialDefinitionId: string + attributes: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] +} + +/** + * This defines the module payload for calling CredentialsApi.acceptRequest. No options are available for this + * method, so it's an empty object + */ +export type AnonCredsAcceptRequestFormat = Record + +export interface AnonCredsCredentialFormat extends CredentialFormat { + formatKey: 'anoncreds' + credentialRecordType: 'anoncreds' + credentialFormats: { + createProposal: AnonCredsProposeCredentialFormat + acceptProposal: AnonCredsAcceptProposalFormat + createOffer: AnonCredsOfferCredentialFormat + acceptOffer: AnonCredsAcceptOfferFormat + createRequest: never // cannot start from createRequest + acceptRequest: AnonCredsAcceptRequestFormat + } + // TODO: update to new RFC once available + // Format data is based on RFC 0592 + // https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments + formatData: { + proposal: { + schema_issuer_id?: string + schema_name?: string + schema_version?: string + schema_id?: string + + cred_def_id?: string + issuer_id?: string + + // TODO: we don't necessarily need to include these in the AnonCreds Format RFC + // as it's a new one and we can just forbid the use of legacy properties + schema_issuer_did?: string + issuer_did?: string + } + offer: AnonCredsCredentialOffer + request: AnonCredsCredentialRequest + credential: AnonCredsCredential + } +} diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts new file mode 100644 index 0000000000..ce9be1e3eb --- /dev/null +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts @@ -0,0 +1,67 @@ +import type { + AnonCredsAcceptOfferFormat, + AnonCredsAcceptProposalFormat, + AnonCredsAcceptRequestFormat, + AnonCredsOfferCredentialFormat, +} from './AnonCredsCredentialFormat' +import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models' +import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachment } from '@aries-framework/core' + +/** + * This defines the module payload for calling CredentialsApi.createProposal + * or CredentialsApi.negotiateOffer + * + * NOTE: This doesn't include the `issuerId` and `schemaIssuerId` properties that are present in the newer format. + */ +export interface LegacyIndyProposeCredentialFormat { + schemaIssuerDid?: string + schemaId?: string + schemaName?: string + schemaVersion?: string + + credentialDefinitionId?: string + issuerDid?: string + + attributes?: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] +} + +export interface LegacyIndyCredentialRequest extends AnonCredsCredentialRequest { + // prover_did is optional in AnonCreds credential request, but required in legacy format + prover_did: string +} + +export interface LegacyIndyCredentialFormat extends CredentialFormat { + formatKey: 'indy' + + // The stored type is the same as the anoncreds credential service + credentialRecordType: 'anoncreds' + + // credential formats are the same as the AnonCreds credential format + credentialFormats: { + // The createProposal interface is different between the interfaces + createProposal: LegacyIndyProposeCredentialFormat + acceptProposal: AnonCredsAcceptProposalFormat + createOffer: AnonCredsOfferCredentialFormat + acceptOffer: AnonCredsAcceptOfferFormat + createRequest: never // cannot start from createRequest + acceptRequest: AnonCredsAcceptRequestFormat + } + + // Format data is based on RFC 0592 + // https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments + formatData: { + proposal: { + schema_name?: string + schema_issuer_did?: string + schema_version?: string + schema_id?: string + + cred_def_id?: string + issuer_did?: string + } + offer: AnonCredsCredentialOffer + request: LegacyIndyCredentialRequest + credential: AnonCredsCredential + } +} diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts new file mode 100644 index 0000000000..e1fd945937 --- /dev/null +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -0,0 +1,608 @@ +import type { LegacyIndyCredentialFormat } from './LegacyIndyCredentialFormat' +import type { + AnonCredsCredential, + AnonCredsCredentialOffer, + AnonCredsCredentialRequest, + AnonCredsCredentialRequestMetadata, +} from '../models' +import type { AnonCredsIssuerService, AnonCredsHolderService, GetRevocationRegistryDefinitionReturn } from '../services' +import type { AnonCredsCredentialMetadata } from '../utils/metadata' +import type { + CredentialFormatService, + AgentContext, + FormatCreateProposalOptions, + FormatCreateProposalReturn, + FormatProcessOptions, + FormatAcceptProposalOptions, + FormatCreateOfferReturn, + FormatCreateOfferOptions, + FormatAcceptOfferOptions, + CredentialFormatCreateReturn, + FormatAcceptRequestOptions, + FormatProcessCredentialOptions, + FormatAutoRespondProposalOptions, + FormatAutoRespondOfferOptions, + FormatAutoRespondRequestOptions, + FormatAutoRespondCredentialOptions, + CredentialExchangeRecord, + CredentialPreviewAttributeOptions, + LinkedAttachment, +} from '@aries-framework/core' + +import { + CredentialFormatSpec, + AriesFrameworkError, + IndyCredPropose, + JsonTransformer, + Attachment, + CredentialPreviewAttribute, + AttachmentData, + JsonEncoder, + utils, + MessageValidator, + CredentialProblemReportError, + CredentialProblemReportReason, +} from '@aries-framework/core' + +import { AnonCredsError } from '../error' +import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' +import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' +import { + convertAttributesToCredentialValues, + assertCredentialValuesMatch, + checkCredentialValuesMatch, + assertAttributesMatch, + createAndLinkAttachmentsToPreview, +} from '../utils/credential' +import { AnonCredsCredentialMetadataKey, AnonCredsCredentialRequestMetadataKey } from '../utils/metadata' + +const INDY_CRED_ABSTRACT = 'hlindy/cred-abstract@v2.0' +const INDY_CRED_REQUEST = 'hlindy/cred-req@v2.0' +const INDY_CRED_FILTER = 'hlindy/cred-filter@v2.0' +const INDY_CRED = 'hlindy/cred@v2.0' + +export class LegacyIndyCredentialFormatService implements CredentialFormatService { + /** formatKey is the key used when calling agent.credentials.xxx with credentialFormats.indy */ + public readonly formatKey = 'indy' as const + + /** + * credentialRecordType is the type of record that stores the credential. It is stored in the credential + * record binding in the credential exchange record. + */ + public readonly credentialRecordType = 'anoncreds' as const + + /** + * Create a {@link AttachmentFormats} object dependent on the message type. + * + * @param options The object containing all the options for the proposed credential + * @returns object containing associated attachment, format and optionally the credential preview + * + */ + public async createProposal( + agentContext: AgentContext, + { credentialFormats, credentialRecord }: FormatCreateProposalOptions + ): Promise { + const format = new CredentialFormatSpec({ + format: INDY_CRED_FILTER, + }) + + const indyFormat = credentialFormats.indy + + if (!indyFormat) { + throw new AriesFrameworkError('Missing indy payload in createProposal') + } + + // We want all properties except for `attributes` and `linkedAttachments` attributes. + // The easiest way is to destructure and use the spread operator. But that leaves the other properties unused + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { attributes, linkedAttachments, ...indyCredentialProposal } = indyFormat + + const proposal = new IndyCredPropose(indyCredentialProposal) + + try { + MessageValidator.validateSync(proposal) + } catch (error) { + throw new AriesFrameworkError(`Invalid proposal supplied: ${indyCredentialProposal} in Indy Format Service`) + } + + const proposalJson = JsonTransformer.toJSON(proposal) + const attachment = this.getFormatData(proposalJson, format.attachId) + + const { previewAttributes } = this.getCredentialLinkedAttachments( + indyFormat.attributes, + indyFormat.linkedAttachments + ) + + // Set the metadata + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + schemaId: proposal.schemaId, + credentialDefinitionId: proposal.credentialDefinitionId, + }) + + return { format, attachment, previewAttributes } + } + + public async processProposal(agentContext: AgentContext, { attachment }: FormatProcessOptions): Promise { + const proposalJson = attachment.getDataAsJson() + + // fromJSON also validates + JsonTransformer.fromJSON(proposalJson, IndyCredPropose) + } + + public async acceptProposal( + agentContext: AgentContext, + { + attachId, + credentialFormats, + credentialRecord, + proposalAttachment, + }: FormatAcceptProposalOptions + ): Promise { + const indyFormat = credentialFormats?.indy + + const credentialProposal = JsonTransformer.fromJSON(proposalAttachment.getDataAsJson(), IndyCredPropose) + + const credentialDefinitionId = indyFormat?.credentialDefinitionId ?? credentialProposal.credentialDefinitionId + + const attributes = indyFormat?.attributes ?? credentialRecord.credentialAttributes + + if (!credentialDefinitionId) { + throw new AriesFrameworkError( + 'No credentialDefinitionId in proposal or provided as input to accept proposal method.' + ) + } + + if (!attributes) { + throw new AriesFrameworkError('No attributes in proposal or provided as input to accept proposal method.') + } + + const { format, attachment, previewAttributes } = await this.createIndyOffer(agentContext, { + credentialRecord, + attachId, + attributes, + credentialDefinitionId, + linkedAttachments: indyFormat?.linkedAttachments, + }) + + return { format, attachment, previewAttributes } + } + + /** + * Create a credential attachment format for a credential request. + * + * @param options The object containing all the options for the credential offer + * @returns object containing associated attachment, formats and offersAttach elements + * + */ + public async createOffer( + agentContext: AgentContext, + { credentialFormats, credentialRecord, attachId }: FormatCreateOfferOptions + ): Promise { + const indyFormat = credentialFormats.indy + + if (!indyFormat) { + throw new AriesFrameworkError('Missing indy credentialFormat data') + } + + const { format, attachment, previewAttributes } = await this.createIndyOffer(agentContext, { + credentialRecord, + attachId, + attributes: indyFormat.attributes, + credentialDefinitionId: indyFormat.credentialDefinitionId, + linkedAttachments: indyFormat.linkedAttachments, + }) + + return { format, attachment, previewAttributes } + } + + public async processOffer(agentContext: AgentContext, { attachment, credentialRecord }: FormatProcessOptions) { + agentContext.config.logger.debug(`Processing indy credential offer for credential record ${credentialRecord.id}`) + + const credOffer = attachment.getDataAsJson() + + if (!credOffer.schema_id || !credOffer.cred_def_id) { + throw new CredentialProblemReportError('Invalid credential offer', { + problemCode: CredentialProblemReportReason.IssuanceAbandoned, + }) + } + } + + public async acceptOffer( + agentContext: AgentContext, + { credentialRecord, attachId, offerAttachment }: FormatAcceptOfferOptions + ): Promise { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentialOffer = offerAttachment.getDataAsJson() + + // Get credential definition + const registry = registryService.getRegistryForIdentifier(agentContext, credentialOffer.cred_def_id) + const { credentialDefinition, resolutionMetadata } = await registry.getCredentialDefinition( + agentContext, + credentialOffer.cred_def_id + ) + + if (!credentialDefinition) { + throw new AnonCredsError( + `Unable to retrieve credential definition with id ${credentialOffer.cred_def_id}: ${resolutionMetadata.error} ${resolutionMetadata.message}` + ) + } + + const { credentialRequest, credentialRequestMetadata } = await holderService.createCredentialRequest(agentContext, { + credentialOffer, + credentialDefinition, + }) + + credentialRecord.metadata.set( + AnonCredsCredentialRequestMetadataKey, + credentialRequestMetadata + ) + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + credentialDefinitionId: credentialOffer.cred_def_id, + schemaId: credentialOffer.schema_id, + }) + + const format = new CredentialFormatSpec({ + attachId, + format: INDY_CRED_REQUEST, + }) + + const attachment = this.getFormatData(credentialRequest, format.attachId) + return { format, attachment } + } + + /** + * Starting from a request is not supported for indy credentials, this method only throws an error. + */ + public async createRequest(): Promise { + throw new AriesFrameworkError('Starting from a request is not supported for indy credentials') + } + + /** + * We don't have any models to validate an indy request object, for now this method does nothing + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async processRequest(agentContext: AgentContext, options: FormatProcessOptions): Promise { + // not needed for Indy + } + + public async acceptRequest( + agentContext: AgentContext, + { + credentialRecord, + attachId, + offerAttachment, + requestAttachment, + }: FormatAcceptRequestOptions + ): Promise { + // Assert credential attributes + const credentialAttributes = credentialRecord.credentialAttributes + if (!credentialAttributes) { + throw new CredentialProblemReportError( + `Missing required credential attribute values on credential record with id ${credentialRecord.id}`, + { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + ) + } + + const anonCredsIssuerService = + agentContext.dependencyManager.resolve(AnonCredsIssuerServiceSymbol) + + const credentialOffer = offerAttachment?.getDataAsJson() + if (!credentialOffer) throw new AriesFrameworkError('Missing indy credential offer in createCredential') + + const credentialRequest = requestAttachment.getDataAsJson() + if (!credentialRequest) throw new AriesFrameworkError('Missing indy credential request in createCredential') + + const { credential, credentialRevocationId } = await anonCredsIssuerService.createCredential(agentContext, { + credentialOffer, + credentialRequest, + credentialValues: convertAttributesToCredentialValues(credentialAttributes), + }) + + if (credential.rev_reg_id) { + credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { + credentialRevocationId: credentialRevocationId, + revocationRegistryId: credential.rev_reg_id, + }) + } + + const format = new CredentialFormatSpec({ + attachId, + format: INDY_CRED, + }) + + const attachment = this.getFormatData(credential, format.attachId) + return { format, attachment } + } + + /** + * Processes an incoming credential - retrieve metadata, retrieve payload and store it in the Indy wallet + * @param options the issue credential message wrapped inside this object + * @param credentialRecord the credential exchange record for this credential + */ + public async processCredential( + agentContext: AgentContext, + { credentialRecord, attachment }: FormatProcessCredentialOptions + ): Promise { + const credentialRequestMetadata = credentialRecord.metadata.get( + AnonCredsCredentialRequestMetadataKey + ) + + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + if (!credentialRequestMetadata) { + throw new CredentialProblemReportError( + `Missing required request metadata for credential with id ${credentialRecord.id}`, + { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + ) + } + + if (!credentialRecord.credentialAttributes) { + throw new AriesFrameworkError( + 'Missing credential attributes on credential record. Unable to check credential attributes' + ) + } + + const anonCredsCredential = attachment.getDataAsJson() + + const credentialDefinitionResult = await registryService + .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) + .getCredentialDefinition(agentContext, anonCredsCredential.cred_def_id) + if (!credentialDefinitionResult.credentialDefinition) { + throw new AriesFrameworkError( + `Unable to resolve credential definition ${anonCredsCredential.cred_def_id}: ${credentialDefinitionResult.resolutionMetadata.error} ${credentialDefinitionResult.resolutionMetadata.message}` + ) + } + + // Resolve revocation registry if credential is revocable + let revocationRegistryResult: null | GetRevocationRegistryDefinitionReturn = null + if (anonCredsCredential.rev_reg_id) { + revocationRegistryResult = await registryService + .getRegistryForIdentifier(agentContext, anonCredsCredential.rev_reg_id) + .getRevocationRegistryDefinition(agentContext, anonCredsCredential.rev_reg_id) + + if (!revocationRegistryResult.revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Unable to resolve revocation registry definition ${anonCredsCredential.rev_reg_id}: ${revocationRegistryResult.resolutionMetadata.error} ${revocationRegistryResult.resolutionMetadata.message}` + ) + } + } + + // assert the credential values match the offer values + const recordCredentialValues = convertAttributesToCredentialValues(credentialRecord.credentialAttributes) + assertCredentialValuesMatch(anonCredsCredential.values, recordCredentialValues) + + const credentialId = await anonCredsHolderService.storeCredential(agentContext, { + credentialId: utils.uuid(), + credentialRequestMetadata, + credential: anonCredsCredential, + credentialDefinitionId: credentialDefinitionResult.credentialDefinitionId, + credentialDefinition: credentialDefinitionResult.credentialDefinition, + revocationRegistry: revocationRegistryResult?.revocationRegistryDefinition + ? { + definition: revocationRegistryResult.revocationRegistryDefinition, + id: revocationRegistryResult.revocationRegistryDefinitionId, + } + : undefined, + }) + + // If the credential is revocable, store the revocation identifiers in the credential record + if (anonCredsCredential.rev_reg_id) { + const credential = await anonCredsHolderService.getCredential(agentContext, { credentialId }) + + credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { + credentialRevocationId: credential.credentialRevocationId, + revocationRegistryId: anonCredsCredential.rev_reg_id, + }) + } + + credentialRecord.credentials.push({ + credentialRecordType: this.credentialRecordType, + credentialRecordId: credentialId, + }) + } + + public supportsFormat(format: string): boolean { + const supportedFormats = [INDY_CRED_ABSTRACT, INDY_CRED_REQUEST, INDY_CRED_FILTER, INDY_CRED] + + return supportedFormats.includes(format) + } + + /** + * Gets the attachment object for a given attachId. We need to get out the correct attachId for + * indy and then find the corresponding attachment (if there is one) + * @param formats the formats object containing the attachId + * @param messageAttachments the attachments containing the payload + * @returns The Attachment if found or undefined + * + */ + public getAttachment(formats: CredentialFormatSpec[], messageAttachments: Attachment[]): Attachment | undefined { + const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachId) + const supportedAttachment = messageAttachments.find((attachment) => supportedAttachmentIds.includes(attachment.id)) + + return supportedAttachment + } + + public async deleteCredentialById(agentContext: AgentContext, credentialRecordId: string): Promise { + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + await anonCredsHolderService.deleteCredential(agentContext, credentialRecordId) + } + + public shouldAutoRespondToProposal( + agentContext: AgentContext, + { offerAttachment, proposalAttachment }: FormatAutoRespondProposalOptions + ) { + const credentialProposalJson = proposalAttachment.getDataAsJson() + const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) + + const credentialOfferJson = offerAttachment.getDataAsJson() + + // We want to make sure the credential definition matches. + // TODO: If no credential definition is present on the proposal, we could check whether the other fields + // of the proposal match with the credential definition id. + return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id + } + + public shouldAutoRespondToOffer( + agentContext: AgentContext, + { offerAttachment, proposalAttachment }: FormatAutoRespondOfferOptions + ) { + const credentialProposalJson = proposalAttachment.getDataAsJson() + const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) + + const credentialOfferJson = offerAttachment.getDataAsJson() + + // We want to make sure the credential definition matches. + // TODO: If no credential definition is present on the proposal, we could check whether the other fields + // of the proposal match with the credential definition id. + return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id + } + + public shouldAutoRespondToRequest( + agentContext: AgentContext, + { offerAttachment, requestAttachment }: FormatAutoRespondRequestOptions + ) { + const credentialOfferJson = offerAttachment.getDataAsJson() + const credentialRequestJson = requestAttachment.getDataAsJson() + + return credentialOfferJson.cred_def_id == credentialRequestJson.cred_def_id + } + + public shouldAutoRespondToCredential( + agentContext: AgentContext, + { credentialRecord, requestAttachment, credentialAttachment }: FormatAutoRespondCredentialOptions + ) { + const credentialJson = credentialAttachment.getDataAsJson() + const credentialRequestJson = requestAttachment.getDataAsJson() + + // make sure the credential definition matches + if (credentialJson.cred_def_id !== credentialRequestJson.cred_def_id) return false + + // If we don't have any attributes stored we can't compare so always return false. + if (!credentialRecord.credentialAttributes) return false + const attributeValues = convertAttributesToCredentialValues(credentialRecord.credentialAttributes) + + // check whether the values match the values in the record + return checkCredentialValuesMatch(attributeValues, credentialJson.values) + } + + private async createIndyOffer( + agentContext: AgentContext, + { + credentialRecord, + attachId, + credentialDefinitionId, + attributes, + linkedAttachments, + }: { + credentialDefinitionId: string + credentialRecord: CredentialExchangeRecord + attachId?: string + attributes: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] + } + ): Promise { + const anonCredsIssuerService = + agentContext.dependencyManager.resolve(AnonCredsIssuerServiceSymbol) + + // if the proposal has an attachment Id use that, otherwise the generated id of the formats object + const format = new CredentialFormatSpec({ + attachId: attachId, + format: INDY_CRED_ABSTRACT, + }) + + const offer = await anonCredsIssuerService.createCredentialOffer(agentContext, { + credentialDefinitionId, + }) + + const { previewAttributes } = this.getCredentialLinkedAttachments(attributes, linkedAttachments) + if (!previewAttributes) { + throw new AriesFrameworkError('Missing required preview attributes for indy offer') + } + + await this.assertPreviewAttributesMatchSchemaAttributes(agentContext, offer, previewAttributes) + + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + schemaId: offer.schema_id, + credentialDefinitionId: offer.cred_def_id, + }) + + const attachment = this.getFormatData(offer, format.attachId) + + return { format, attachment, previewAttributes } + } + + private async assertPreviewAttributesMatchSchemaAttributes( + agentContext: AgentContext, + offer: AnonCredsCredentialOffer, + attributes: CredentialPreviewAttribute[] + ): Promise { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const registry = registryService.getRegistryForIdentifier(agentContext, offer.schema_id) + + const schemaResult = await registry.getSchema(agentContext, offer.schema_id) + + if (!schemaResult.schema) { + throw new AriesFrameworkError( + `Unable to resolve schema ${offer.schema_id} from registry: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` + ) + } + + assertAttributesMatch(schemaResult.schema, attributes) + } + + /** + * Get linked attachments for indy format from a proposal message. This allows attachments + * to be copied across to old style credential records + * + * @param options ProposeCredentialOptions object containing (optionally) the linked attachments + * @return array of linked attachments or undefined if none present + */ + private getCredentialLinkedAttachments( + attributes?: CredentialPreviewAttributeOptions[], + linkedAttachments?: LinkedAttachment[] + ): { + attachments?: Attachment[] + previewAttributes?: CredentialPreviewAttribute[] + } { + if (!linkedAttachments && !attributes) { + return {} + } + + let previewAttributes = attributes?.map((attribute) => new CredentialPreviewAttribute(attribute)) ?? [] + let attachments: Attachment[] | undefined + + if (linkedAttachments) { + // there are linked attachments so transform into the attribute field of the CredentialPreview object for + // this proposal + previewAttributes = createAndLinkAttachmentsToPreview(linkedAttachments, previewAttributes) + attachments = linkedAttachments.map((linkedAttachment) => linkedAttachment.attachment) + } + + return { attachments, previewAttributes } + } + + /** + * Returns an object of type {@link Attachment} for use in credential exchange messages. + * It looks up the correct format identifier and encodes the data as a base64 attachment. + * + * @param data The data to include in the attach object + * @param id the attach id from the formats component of the message + */ + public getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ + id, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(data), + }), + }) + + return attachment + } +} diff --git a/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts b/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts new file mode 100644 index 0000000000..7e1e1909da --- /dev/null +++ b/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts @@ -0,0 +1,224 @@ +import { + CredentialState, + CredentialExchangeRecord, + SigningProviderRegistry, + KeyType, + CredentialPreviewAttribute, +} from '@aries-framework/core' +import * as indySdk from 'indy-sdk' + +import { getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { + IndySdkHolderService, + IndySdkIssuerService, + IndySdkVerifierService, + IndySdkWallet, +} from '../../../../indy-sdk/src' +import { IndySdkRevocationService } from '../../../../indy-sdk/src/anoncreds/services/IndySdkRevocationService' +import { indyDidFromPublicKeyBase58 } from '../../../../indy-sdk/src/utils/did' +import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' +import { AnonCredsModuleConfig } from '../../AnonCredsModuleConfig' +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, +} from '../../services' +import { AnonCredsRegistryService } from '../../services/registry/AnonCredsRegistryService' +import { LegacyIndyCredentialFormatService } from '../LegacyIndyCredentialFormatService' + +const registry = new InMemoryAnonCredsRegistry() +const anonCredsModuleConfig = new AnonCredsModuleConfig({ + registries: [registry], +}) + +const agentConfig = getAgentConfig('LegacyIndyCredentialFormatServiceTest') +const anonCredsRevocationService = new IndySdkRevocationService(indySdk) +const anonCredsVerifierService = new IndySdkVerifierService(indySdk) +const anonCredsHolderService = new IndySdkHolderService(anonCredsRevocationService, indySdk) +const anonCredsIssuerService = new IndySdkIssuerService(indySdk) +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const agentContext = getAgentContext({ + registerInstances: [ + [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], + [AnonCredsHolderServiceSymbol, anonCredsHolderService], + [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + [AnonCredsRegistryService, new AnonCredsRegistryService()], + [AnonCredsModuleConfig, anonCredsModuleConfig], + ], + agentConfig, + wallet, +}) + +const indyCredentialFormatService = new LegacyIndyCredentialFormatService() + +describe('LegacyIndyCredentialFormatService', () => { + beforeEach(async () => { + await wallet.createAndOpen(agentConfig.walletConfig) + }) + + afterEach(async () => { + await wallet.delete() + }) + + test('issuance flow starting from proposal without negotiation and without revocation', async () => { + // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) + const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) + const indyDid = indyDidFromPublicKeyBase58(key.publicKeyBase58) + + const schema = await anonCredsIssuerService.createSchema(agentContext, { + attrNames: ['name', 'age'], + issuerId: indyDid, + name: 'Employee Credential', + version: '1.0.0', + }) + + const { schemaState, schemaMetadata } = await registry.registerSchema(agentContext, { + schema, + options: {}, + }) + + const credentialDefinition = await anonCredsIssuerService.createCredentialDefinition( + agentContext, + { + issuerId: indyDid, + schemaId: schemaState.schemaId as string, + schema, + tag: 'Employee Credential', + supportRevocation: false, + }, + { + // Need to pass this as the indy-sdk MUST have the seqNo + indyLedgerSchemaSeqNo: schemaMetadata.indyLedgerSeqNo as number, + } + ) + + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { + credentialDefinition, + options: {}, + }) + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + const holderCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalSent, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const issuerCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalReceived, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const credentialAttributes = [ + new CredentialPreviewAttribute({ + name: 'name', + value: 'John', + }), + new CredentialPreviewAttribute({ + name: 'age', + value: '25', + }), + ] + + // Holder creates proposal + holderCredentialRecord.credentialAttributes = credentialAttributes + const { attachment: proposalAttachment } = await indyCredentialFormatService.createProposal(agentContext, { + credentialRecord: holderCredentialRecord, + credentialFormats: { + indy: { + attributes: credentialAttributes, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + }, + }) + + // Issuer processes and accepts proposal + await indyCredentialFormatService.processProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: proposalAttachment, + }) + // Set attributes on the credential record, this is normally done by the protocol service + issuerCredentialRecord.credentialAttributes = credentialAttributes + const { attachment: offerAttachment } = await indyCredentialFormatService.acceptProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + proposalAttachment: proposalAttachment, + }) + + // Holder processes and accepts offer + await indyCredentialFormatService.processOffer(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: offerAttachment, + }) + const { attachment: requestAttachment } = await indyCredentialFormatService.acceptOffer(agentContext, { + credentialRecord: holderCredentialRecord, + offerAttachment, + }) + + // Issuer processes and accepts request + await indyCredentialFormatService.processRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: requestAttachment, + }) + const { attachment: credentialAttachment } = await indyCredentialFormatService.acceptRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + requestAttachment, + offerAttachment, + }) + + // Holder processes and accepts credential + await indyCredentialFormatService.processCredential(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: credentialAttachment, + requestAttachment, + }) + + expect(holderCredentialRecord.credentials).toEqual([ + { credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String) }, + ]) + + const credentialId = holderCredentialRecord.credentials[0].credentialRecordId + const anonCredsCredential = await anonCredsHolderService.getCredential(agentContext, { + credentialId, + }) + + expect(anonCredsCredential).toEqual({ + credentialId, + attributes: { + age: '25', + name: 'John', + }, + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + revocationRegistryId: null, + credentialRevocationId: null, + }) + + expect(holderCredentialRecord.metadata.data).toEqual({ + '_anonCreds/anonCredsCredential': { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + '_anonCreds/anonCredsCredentialRequest': { + master_secret_blinding_data: expect.any(Object), + master_secret_name: expect.any(String), + nonce: expect.any(String), + }, + }) + + expect(issuerCredentialRecord.metadata.data).toEqual({ + '_anonCreds/anonCredsCredential': { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + }) + }) +}) diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index bd30979a86..40713b227d 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -1,11 +1,22 @@ -// TODO: Maybe we can make this a bit more specific? -export type WalletQuery = Record +interface AnonCredsProofRequestRestriction { + schema_id?: string + schema_issuer_id?: string + schema_name?: string + schema_version?: string + issuer_id?: string + cred_def_id?: string + rev_reg_id?: string + + // Deprecated, but kept for backwards compatibility with legacy indy anoncreds implementations + schema_issuer_did?: string + issuer_did?: string -export interface ReferentWalletQuery { - [key: string]: WalletQuery + // the following keys can be used for every `attribute name` in credential. + [key: `attr::${string}::marker`]: '1' | '0' + [key: `attr::${string}::value`]: string } -export interface NonRevokedInterval { +export interface AnonCredsNonRevokedInterval { from?: number to?: number } @@ -18,16 +29,16 @@ export interface AnonCredsCredentialOffer { } export interface AnonCredsCredentialRequest { - // TODO: Why is this needed? It is just used as context in Ursa, can be any string. Should we remove it? - // Should we not make it did related? - prover_did: string + // prover_did is deprecated, however it is kept for backwards compatibility with legacy anoncreds implementations + prover_did?: string cred_def_id: string blinded_ms: Record blinded_ms_correctness_proof: Record nonce: string } -export interface CredValue { +export type AnonCredsCredentialValues = Record +export interface AnonCredsCredentialValue { raw: string encoded: string // Raw value as number in string } @@ -36,7 +47,7 @@ export interface AnonCredsCredential { schema_id: string cred_def_id: string rev_reg_id?: string - values: Record + values: Record signature: unknown signature_correctness_proof: unknown } @@ -91,8 +102,8 @@ export interface AnonCredsProofRequest { { name?: string names?: string[] - restrictions?: WalletQuery[] - non_revoked?: NonRevokedInterval + restrictions?: AnonCredsProofRequestRestriction[] + non_revoked?: AnonCredsNonRevokedInterval } > requested_predicates: Record< @@ -101,10 +112,10 @@ export interface AnonCredsProofRequest { name: string p_type: '>=' | '>' | '<=' | '<' p_value: number - restrictions?: WalletQuery[] - non_revoked?: NonRevokedInterval + restrictions?: AnonCredsProofRequestRestriction[] + non_revoked?: AnonCredsNonRevokedInterval } > - non_revoked?: NonRevokedInterval + non_revoked?: AnonCredsNonRevokedInterval ver?: '1.0' | '2.0' } diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index c838dcf865..27d476ebb3 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -1,5 +1,5 @@ -export interface CredentialInfo { - referent: string +export interface AnonCredsCredentialInfo { + credentialId: string attributes: { [key: string]: string } @@ -9,23 +9,32 @@ export interface CredentialInfo { credentialRevocationId?: string | undefined } -export interface RequestedAttribute { +export interface AnonCredsRequestedAttribute { credentialId: string timestamp?: number revealed: boolean - credentialInfo: CredentialInfo + credentialInfo: AnonCredsCredentialInfo revoked?: boolean } -export interface RequestedPredicate { +export interface AnonCredsRequestedPredicate { credentialId: string timestamp?: number - credentialInfo: CredentialInfo + credentialInfo: AnonCredsCredentialInfo revoked?: boolean } -export interface RequestedCredentials { - requestedAttributes?: Record - requestedPredicates?: Record +export interface AnonCredsRequestedCredentials { + requestedAttributes?: Record + requestedPredicates?: Record selfAttestedAttributes: Record } + +export interface AnonCredsCredentialRequestMetadata { + master_secret_blinding_data: { + v_prime: string + vr_prime: string | null + } + master_secret_name: string + nonce: string +} diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts index 4991dbca1f..a7c0dcb22e 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderService.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -7,10 +7,12 @@ import type { GetCredentialsForProofRequestOptions, GetCredentialsForProofRequestReturn, } from './AnonCredsHolderServiceOptions' -import type { CredentialInfo } from '../models' +import type { AnonCredsCredentialInfo } from '../models' import type { AnonCredsProof } from '../models/exchange' import type { AgentContext } from '@aries-framework/core' +export const AnonCredsHolderServiceSymbol = Symbol('AnonCredsHolderService') + export interface AnonCredsHolderService { createProof(agentContext: AgentContext, options: CreateProofOptions): Promise storeCredential( @@ -19,8 +21,10 @@ export interface AnonCredsHolderService { metadata?: Record ): Promise - // TODO: indy has different return types for the credential - getCredential(agentContext: AgentContext, options: GetCredentialOptions): Promise + // TODO: this doesn't actually return the credential, as the indy-sdk doesn't support that + // We could come up with a hack (as we've received the credential at one point), but for + // now I think it's not that much of an issue + getCredential(agentContext: AgentContext, options: GetCredentialOptions): Promise createCredentialRequest( agentContext: AgentContext, diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index 3de66df703..728482ff33 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -1,11 +1,14 @@ -import type { CredentialInfo, RequestedCredentials } from '../models' +import type { + AnonCredsCredentialInfo, + AnonCredsCredentialRequestMetadata, + AnonCredsRequestedCredentials, +} from '../models' import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest, AnonCredsProofRequest, - NonRevokedInterval, - ReferentWalletQuery, + AnonCredsNonRevokedInterval, } from '../models/exchange' import type { AnonCredsCredentialDefinition, @@ -14,14 +17,14 @@ import type { AnonCredsSchema, } from '../models/registry' -export interface AttributeInfo { +export interface AnonCredsAttributeInfo { name?: string names?: string[] } export interface CreateProofOptions { proofRequest: AnonCredsProofRequest - requestedCredentials: RequestedCredentials + requestedCredentials: AnonCredsRequestedCredentials schemas: { [schemaId: string]: AnonCredsSchema } @@ -41,8 +44,7 @@ export interface CreateProofOptions { } export interface StoreCredentialOptions { - // TODO: what is in credential request metadata? - credentialRequestMetadata: Record + credentialRequestMetadata: AnonCredsCredentialRequestMetadata credential: AnonCredsCredential credentialDefinition: AnonCredsCredentialDefinition credentialDefinitionId: string @@ -57,6 +59,12 @@ export interface GetCredentialOptions { credentialId: string } +// TODO: Maybe we can make this a bit more specific? +export type WalletQuery = Record +export interface ReferentWalletQuery { + [referent: string]: WalletQuery +} + export interface GetCredentialsForProofRequestOptions { proofRequest: AnonCredsProofRequest attributeReferent: string @@ -66,19 +74,16 @@ export interface GetCredentialsForProofRequestOptions { } export type GetCredentialsForProofRequestReturn = Array<{ - credentialInfo: CredentialInfo - interval?: NonRevokedInterval + credentialInfo: AnonCredsCredentialInfo + interval?: AnonCredsNonRevokedInterval }> export interface CreateCredentialRequestOptions { - // TODO: Why is this needed? It is just used as context in Ursa, can be any string. Should we remove it? - // Should we not make it did related? (related to comment in AnonCredsCredentialRequest) - holderDid: string credentialOffer: AnonCredsCredentialOffer credentialDefinition: AnonCredsCredentialDefinition } export interface CreateCredentialRequestReturn { credentialRequest: AnonCredsCredentialRequest - credentialRequestMetadata: Record + credentialRequestMetadata: AnonCredsCredentialRequestMetadata } diff --git a/packages/anoncreds/src/services/AnonCredsIssuerService.ts b/packages/anoncreds/src/services/AnonCredsIssuerService.ts index 0f34d300ef..41cb4ebf9f 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerService.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerService.ts @@ -9,6 +9,8 @@ import type { AnonCredsCredentialOffer } from '../models/exchange' import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../models/registry' import type { AgentContext } from '@aries-framework/core' +export const AnonCredsIssuerServiceSymbol = Symbol('AnonCredsIssuerService') + export interface AnonCredsIssuerService { createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise diff --git a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts index e3bb8dcdfb..58d6cd9048 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts @@ -2,7 +2,7 @@ import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest, - CredValue, + AnonCredsCredentialValues, } from '../models/exchange' import type { AnonCredsSchema } from '../models/registry' @@ -29,7 +29,7 @@ export interface CreateCredentialOfferOptions { export interface CreateCredentialOptions { credentialOffer: AnonCredsCredentialOffer credentialRequest: AnonCredsCredentialRequest - credentialValues: Record + credentialValues: AnonCredsCredentialValues revocationRegistryId?: string // TODO: should this just be the tails file instead of a path? tailsFilePath?: string diff --git a/packages/anoncreds/src/services/AnonCredsVerifierService.ts b/packages/anoncreds/src/services/AnonCredsVerifierService.ts index ec68021817..00e2a5670d 100644 --- a/packages/anoncreds/src/services/AnonCredsVerifierService.ts +++ b/packages/anoncreds/src/services/AnonCredsVerifierService.ts @@ -1,5 +1,7 @@ import type { VerifyProofOptions } from './AnonCredsVerifierServiceOptions' +export const AnonCredsVerifierServiceSymbol = Symbol('AnonCredsVerifierService') + export interface AnonCredsVerifierService { // TODO: do we want to extend the return type with more info besides a boolean. // If the value is false it would be nice to have some extra contexts about why it failed diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts index 8ee8eb4b50..a860d1e8f5 100644 --- a/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts @@ -13,7 +13,7 @@ import { AnonCredsError } from '../../error' */ @injectable() export class AnonCredsRegistryService { - public async getRegistryForIdentifier(agentContext: AgentContext, identifier: string): Promise { + public getRegistryForIdentifier(agentContext: AgentContext, identifier: string): AnonCredsRegistry { const registries = agentContext.dependencyManager.resolve(AnonCredsModuleConfig).registries // TODO: should we check if multiple are registered? diff --git a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts index 142e784405..1bf5614720 100644 --- a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts @@ -8,7 +8,7 @@ import type { import type { AnonCredsCredentialDefinition } from '../../models/registry' export interface GetCredentialDefinitionReturn { - credentialDefinition: AnonCredsCredentialDefinition | null + credentialDefinition?: AnonCredsCredentialDefinition credentialDefinitionId: string resolutionMetadata: AnonCredsResolutionMetadata credentialDefinitionMetadata: Extensible @@ -20,7 +20,7 @@ export interface RegisterCredentialDefinitionOptions { } export interface RegisterCredentialDefinitionReturnStateFailed extends AnonCredsOperationStateFailed { - credentialDefinition: AnonCredsCredentialDefinition + credentialDefinition?: AnonCredsCredentialDefinition credentialDefinitionId?: string } @@ -30,7 +30,7 @@ export interface RegisterCredentialDefinitionReturnStateFinished extends AnonCre } export interface RegisterCredentialDefinitionReturnState extends AnonCredsOperationState { - credentialDefinition: AnonCredsCredentialDefinition + credentialDefinition?: AnonCredsCredentialDefinition credentialDefinitionId?: string } diff --git a/packages/anoncreds/src/services/registry/RevocationListOptions.ts b/packages/anoncreds/src/services/registry/RevocationListOptions.ts index b6f0edea42..f3a07dc686 100644 --- a/packages/anoncreds/src/services/registry/RevocationListOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationListOptions.ts @@ -2,7 +2,7 @@ import type { AnonCredsResolutionMetadata, Extensible } from './base' import type { AnonCredsRevocationList } from '../../models/registry' export interface GetRevocationListReturn { - revocationList: AnonCredsRevocationList | null + revocationList?: AnonCredsRevocationList resolutionMetadata: AnonCredsResolutionMetadata revocationListMetadata: Extensible } diff --git a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts index 6d45377114..6e9d1349fe 100644 --- a/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationRegistryDefinitionOptions.ts @@ -2,7 +2,7 @@ import type { AnonCredsResolutionMetadata, Extensible } from './base' import type { AnonCredsRevocationRegistryDefinition } from '../../models/registry' export interface GetRevocationRegistryDefinitionReturn { - revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition | null + revocationRegistryDefinition?: AnonCredsRevocationRegistryDefinition revocationRegistryDefinitionId: string resolutionMetadata: AnonCredsResolutionMetadata revocationRegistryDefinitionMetadata: Extensible diff --git a/packages/anoncreds/src/services/registry/SchemaOptions.ts b/packages/anoncreds/src/services/registry/SchemaOptions.ts index c436859060..9ff42c9bc4 100644 --- a/packages/anoncreds/src/services/registry/SchemaOptions.ts +++ b/packages/anoncreds/src/services/registry/SchemaOptions.ts @@ -9,7 +9,7 @@ import type { AnonCredsSchema } from '../../models/registry' // Get Schema export interface GetSchemaReturn { - schema: AnonCredsSchema | null + schema?: AnonCredsSchema schemaId: string // Can contain e.g. the ledger transaction request/response resolutionMetadata: AnonCredsResolutionMetadata @@ -24,7 +24,7 @@ export interface RegisterSchemaOptions { } export interface RegisterSchemaReturnStateFailed extends AnonCredsOperationStateFailed { - schema: AnonCredsSchema + schema?: AnonCredsSchema schemaId?: string } @@ -34,7 +34,7 @@ export interface RegisterSchemaReturnStateFinished extends AnonCredsOperationSta } export interface RegisterSchemaReturnState extends AnonCredsOperationState { - schema: AnonCredsSchema + schema?: AnonCredsSchema schemaId?: string } diff --git a/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts index 096626f805..553b9e626c 100644 --- a/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts +++ b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts @@ -28,11 +28,11 @@ const anonCredsRegistryService = new AnonCredsRegistryService() describe('AnonCredsRegistryService', () => { test('returns the registry for an identifier based on the supportedMethods regex', async () => { - await expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'a')).resolves.toEqual(registryOne) - await expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'b')).resolves.toEqual(registryTwo) + expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'a')).toEqual(registryOne) + expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'b')).toEqual(registryTwo) }) - test('throws AnonCredsError if no registry is found for the given identifier', async () => { - await expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'c')).rejects.toThrow(AnonCredsError) + test('throws AnonCredsError if no registry is found for the given identifier', () => { + expect(() => anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'c')).toThrow(AnonCredsError) }) }) diff --git a/packages/anoncreds/src/utils/__tests__/credential.test.ts b/packages/anoncreds/src/utils/__tests__/credential.test.ts new file mode 100644 index 0000000000..0b81afe881 --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/credential.test.ts @@ -0,0 +1,225 @@ +import { CredentialPreviewAttribute } from '@aries-framework/core' + +import { assertCredentialValuesMatch, checkValidEncoding, convertAttributesToCredentialValues } from '../credential' + +/** + * Sample test cases for encoding/decoding of verifiable credential claims - Aries RFCs 0036 and 0037 + * @see https://gist.github.com/swcurran/78e5a9e8d11236f003f6a6263c6619a6 + */ +const testEncodings: { [key: string]: { raw: string | number | boolean | null; encoded: string } } = { + address2: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + zip: { + raw: '87121', + encoded: '87121', + }, + city: { + raw: 'SLC', + encoded: '101327353979588246869873249766058188995681113722618593621043638294296500696424', + }, + address1: { + raw: '101 Tela Lane', + encoded: '63690509275174663089934667471948380740244018358024875547775652380902762701972', + }, + state: { + raw: 'UT', + encoded: '93856629670657830351991220989031130499313559332549427637940645777813964461231', + }, + Empty: { + raw: '', + encoded: '102987336249554097029535212322581322789799900648198034993379397001115665086549', + }, + Null: { + raw: null, + encoded: '99769404535520360775991420569103450442789945655240760487761322098828903685777', + }, + 'bool True': { + raw: true, + encoded: '1', + }, + 'bool False': { + raw: false, + encoded: '0', + }, + 'str True': { + raw: 'True', + encoded: '27471875274925838976481193902417661171675582237244292940724984695988062543640', + }, + 'str False': { + raw: 'False', + encoded: '43710460381310391454089928988014746602980337898724813422905404670995938820350', + }, + 'max i32': { + raw: 2147483647, + encoded: '2147483647', + }, + 'max i32 + 1': { + raw: 2147483648, + encoded: '26221484005389514539852548961319751347124425277437769688639924217837557266135', + }, + 'min i32': { + raw: -2147483648, + encoded: '-2147483648', + }, + 'min i32 - 1': { + raw: -2147483649, + encoded: '68956915425095939579909400566452872085353864667122112803508671228696852865689', + }, + 'float 0.1': { + raw: 0.1, + encoded: '9382477430624249591204401974786823110077201914483282671737639310288175260432', + }, + 'str 0.1': { + raw: '0.1', + encoded: '9382477430624249591204401974786823110077201914483282671737639310288175260432', + }, + 'str 1.0': { + raw: '1.0', + encoded: '94532235908853478633102631881008651863941875830027892478278578250784387892726', + }, + 'str 1': { + raw: '1', + encoded: '1', + }, + 'leading zero number string': { + raw: '012345', + encoded: '12345', + }, + 'chr 0': { + raw: String.fromCharCode(0), + encoded: '49846369543417741186729467304575255505141344055555831574636310663216789168157', + }, + 'chr 1': { + raw: String.fromCharCode(1), + encoded: '34356466678672179216206944866734405838331831190171667647615530531663699592602', + }, + 'chr 2': { + raw: String.fromCharCode(2), + encoded: '99398763056634537812744552006896172984671876672520535998211840060697129507206', + }, +} + +describe('Utils | Credentials', () => { + describe('convertAttributesToCredentialValues', () => { + test('returns object with raw and encoded attributes', () => { + const attributes = [ + new CredentialPreviewAttribute({ + name: 'name', + mimeType: 'text/plain', + value: '101 Wilson Lane', + }), + new CredentialPreviewAttribute({ + name: 'age', + mimeType: 'text/plain', + value: '1234', + }), + ] + + expect(convertAttributesToCredentialValues(attributes)).toEqual({ + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + }) + }) + }) + + describe('assertCredentialValuesMatch', () => { + test('does not throw if attributes match', () => { + const firstValues = { + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + + expect(() => assertCredentialValuesMatch(firstValues, secondValues)).not.toThrow() + }) + + test('throws if number of values in the entries do not match', () => { + const firstValues = { + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + + expect(() => assertCredentialValuesMatch(firstValues, secondValues)).toThrow( + 'Number of values in first entry (1) does not match number of values in second entry (2)' + ) + }) + + test('throws if second value does not contain key from first value', () => { + const firstValues = { + name: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + anotherName: { + raw: '101 Wilson Lane', + encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', + }, + age: { raw: '1234', encoded: '1234' }, + } + + expect(() => assertCredentialValuesMatch(firstValues, secondValues)).toThrow( + "Second cred values object has no value for key 'name'" + ) + }) + + test('throws if encoded values do not match', () => { + const firstValues = { + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + age: { raw: '1234', encoded: '12345' }, + } + + expect(() => assertCredentialValuesMatch(firstValues, secondValues)).toThrow( + "Encoded credential values for key 'age' do not match" + ) + }) + + test('throws if raw values do not match', () => { + const firstValues = { + age: { raw: '1234', encoded: '1234' }, + } + const secondValues = { + age: { raw: '12345', encoded: '1234' }, + } + + expect(() => assertCredentialValuesMatch(firstValues, secondValues)).toThrow( + "Raw credential values for key 'age' do not match" + ) + }) + }) + + describe('checkValidEncoding', () => { + // Formatted for test.each + const testEntries = Object.entries(testEncodings).map( + ([name, { raw, encoded }]) => [name, raw, encoded] as [string, string | number | boolean | null, string] + ) + + test.each(testEntries)('returns true for valid encoding %s', (_, raw, encoded) => { + expect(checkValidEncoding(raw, encoded)).toEqual(true) + }) + }) +}) diff --git a/packages/anoncreds/src/utils/credential.ts b/packages/anoncreds/src/utils/credential.ts new file mode 100644 index 0000000000..6310270980 --- /dev/null +++ b/packages/anoncreds/src/utils/credential.ts @@ -0,0 +1,200 @@ +import type { AnonCredsSchema, AnonCredsCredentialValues } from '../models' +import type { CredentialPreviewAttributeOptions, LinkedAttachment } from '@aries-framework/core' + +import { CredentialPreviewAttribute, AriesFrameworkError, Hasher, encodeAttachment } from '@aries-framework/core' +import BigNumber from 'bn.js' + +const isString = (value: unknown): value is string => typeof value === 'string' +const isNumber = (value: unknown): value is number => typeof value === 'number' +const isBoolean = (value: unknown): value is boolean => typeof value === 'boolean' +const isNumeric = (value: string) => /^-?\d+$/.test(value) + +const isInt32 = (number: number) => { + const minI32 = -2147483648 + const maxI32 = 2147483647 + + // Check if number is integer and in range of int32 + return Number.isInteger(number) && number >= minI32 && number <= maxI32 +} + +/** + * Converts int value to string + * Converts string value: + * - hash with sha256, + * - convert to byte array and reverse it + * - convert it to BigInteger and return as a string + * @param attributes + * + * @returns CredValues + */ +export function convertAttributesToCredentialValues( + attributes: CredentialPreviewAttributeOptions[] +): AnonCredsCredentialValues { + return attributes.reduce((credentialValues, attribute) => { + return { + [attribute.name]: { + raw: attribute.value, + encoded: encode(attribute.value), + }, + ...credentialValues, + } + }, {}) +} + +/** + * Check whether the values of two credentials match (using {@link assertCredentialValuesMatch}) + * + * @returns a boolean whether the values are equal + * + */ +export function checkCredentialValuesMatch( + firstValues: AnonCredsCredentialValues, + secondValues: AnonCredsCredentialValues +): boolean { + try { + assertCredentialValuesMatch(firstValues, secondValues) + return true + } catch { + return false + } +} + +/** + * Assert two credential values objects match. + * + * @param firstValues The first values object + * @param secondValues The second values object + * + * @throws If not all values match + */ +export function assertCredentialValuesMatch( + firstValues: AnonCredsCredentialValues, + secondValues: AnonCredsCredentialValues +) { + const firstValuesKeys = Object.keys(firstValues) + const secondValuesKeys = Object.keys(secondValues) + + if (firstValuesKeys.length !== secondValuesKeys.length) { + throw new Error( + `Number of values in first entry (${firstValuesKeys.length}) does not match number of values in second entry (${secondValuesKeys.length})` + ) + } + + for (const key of firstValuesKeys) { + const firstValue = firstValues[key] + const secondValue = secondValues[key] + + if (!secondValue) { + throw new Error(`Second cred values object has no value for key '${key}'`) + } + + if (firstValue.encoded !== secondValue.encoded) { + throw new Error(`Encoded credential values for key '${key}' do not match`) + } + + if (firstValue.raw !== secondValue.raw) { + throw new Error(`Raw credential values for key '${key}' do not match`) + } + } +} + +/** + * Check whether the raw value matches the encoded version according to the encoding format described in Aries RFC 0037 + * Use this method to ensure the received proof (over the encoded) value is the same as the raw value of the data. + * + * @param raw + * @param encoded + * @returns Whether raw and encoded value match + * + * @see https://github.com/hyperledger/aries-framework-dotnet/blob/a18bef91e5b9e4a1892818df7408e2383c642dfa/src/Hyperledger.Aries/Utils/CredentialUtils.cs#L78-L89 + * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials + */ +export function checkValidEncoding(raw: unknown, encoded: string) { + return encoded === encode(raw) +} + +/** + * Encode value according to the encoding format described in Aries RFC 0036/0037 + * + * @param value + * @returns Encoded version of value + * + * @see https://github.com/hyperledger/aries-cloudagent-python/blob/0000f924a50b6ac5e6342bff90e64864672ee935/aries_cloudagent/messaging/util.py#L106-L136 + * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials + * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0036-issue-credential/README.md#encoding-claims-for-indy-based-verifiable-credentials + */ +export function encode(value: unknown) { + const isEmpty = (value: unknown) => isString(value) && value === '' + + // If bool return bool as number string + if (isBoolean(value)) { + return Number(value).toString() + } + + // If value is int32 return as number string + if (isNumber(value) && isInt32(value)) { + return value.toString() + } + + // If value is an int32 number string return as number string + if (isString(value) && !isEmpty(value) && !isNaN(Number(value)) && isNumeric(value) && isInt32(Number(value))) { + return Number(value).toString() + } + + if (isNumber(value)) { + value = value.toString() + } + + // If value is null we must use the string value 'None' + if (value === null || value === undefined) { + value = 'None' + } + + return new BigNumber(Hasher.hash(Buffer.from(value as string), 'sha2-256')).toString() +} + +export function assertAttributesMatch(schema: AnonCredsSchema, attributes: CredentialPreviewAttribute[]) { + const schemaAttributes = schema.attrNames + const credAttributes = attributes.map((a) => a.name) + + const difference = credAttributes + .filter((x) => !schemaAttributes.includes(x)) + .concat(schemaAttributes.filter((x) => !credAttributes.includes(x))) + + if (difference.length > 0) { + throw new AriesFrameworkError( + `The credential preview attributes do not match the schema attributes (difference is: ${difference}, needs: ${schemaAttributes})` + ) + } +} + +/** + * Adds attribute(s) to the credential preview that is linked to the given attachment(s) + * + * @param attachments a list of the attachments that need to be linked to a credential + * @param preview the credential previews where the new linked credential has to be appended to + * + * @returns a modified version of the credential preview with the linked credentials + * */ +export function createAndLinkAttachmentsToPreview( + attachments: LinkedAttachment[], + previewAttributes: CredentialPreviewAttribute[] +) { + const credentialPreviewAttributeNames = previewAttributes.map((attribute) => attribute.name) + const newPreviewAttributes = [...previewAttributes] + + attachments.forEach((linkedAttachment) => { + if (credentialPreviewAttributeNames.includes(linkedAttachment.attributeName)) { + throw new AriesFrameworkError(`linkedAttachment ${linkedAttachment.attributeName} already exists in the preview`) + } else { + const credentialPreviewAttribute = new CredentialPreviewAttribute({ + name: linkedAttachment.attributeName, + mimeType: linkedAttachment.attachment.mimeType, + value: encodeAttachment(linkedAttachment.attachment), + }) + newPreviewAttributes.push(credentialPreviewAttribute) + } + }) + + return newPreviewAttributes +} diff --git a/packages/anoncreds/src/utils/metadata.ts b/packages/anoncreds/src/utils/metadata.ts new file mode 100644 index 0000000000..1d8448ebfa --- /dev/null +++ b/packages/anoncreds/src/utils/metadata.ts @@ -0,0 +1,29 @@ +// TODO: we may want to already support multiple credentials in the metadata of a credential +// record, as that's what the RFCs support. We already need to write a migration script for modules + +/** + * Metadata key for strong metadata on an AnonCreds credential. + * + * MUST be used with {@link AnonCredsCredentialMetadata} + */ +export const AnonCredsCredentialMetadataKey = '_anonCreds/anonCredsCredential' + +/** + * Metadata key for strong metadata on an AnonCreds credential request. + * + * MUST be used with {@link AnonCredsCredentialRequestMetadata} + */ +export const AnonCredsCredentialRequestMetadataKey = '_anonCreds/anonCredsCredentialRequest' + +/** + * Metadata for an AnonCreds credential that will be stored + * in the credential record. + * + * MUST be used with {@link AnonCredsCredentialMetadataKey} + */ +export interface AnonCredsCredentialMetadata { + schemaId?: string + credentialDefinitionId?: string + revocationRegistryId?: string + credentialRevocationId?: string +} diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts new file mode 100644 index 0000000000..a1426fad46 --- /dev/null +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -0,0 +1,155 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import type { + AnonCredsRegistry, + GetSchemaReturn, + RegisterSchemaOptions, + RegisterSchemaReturn, + GetCredentialDefinitionReturn, + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, + GetRevocationRegistryDefinitionReturn, + GetRevocationListReturn, + AnonCredsSchema, + AnonCredsCredentialDefinition, +} from '../src' +import type { AgentContext } from '@aries-framework/core' + +import { Hasher, TypedArrayEncoder } from '@aries-framework/core' +import BigNumber from 'bn.js' + +/** + * In memory implementation of the {@link AnonCredsRegistry} interface. Useful for testing. + */ +export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { + // Roughly match that the identifier starts with an unqualified indy did. Once the + // anoncreds tests are not based on the indy-sdk anymore, we can use any identifier + // we want, but the indy-sdk is picky about the identifier format. + public readonly supportedIdentifier = /^[a-zA-Z0-9]{21,22}/ + + private schemas: Record = {} + private credentialDefinitions: Record = {} + + public async getSchema(agentContext: AgentContext, schemaId: string): Promise { + const schema = this.schemas[schemaId] + const indyLedgerSeqNo = getSeqNoFromSchemaId(schemaId) + + if (!schema) { + return { + resolutionMetadata: { + error: 'notFound', + message: `Schema with id ${schemaId} not found in memory registry`, + }, + schemaId, + schemaMetadata: { + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo, + }, + } + } + + return { + resolutionMetadata: {}, + schema, + schemaId, + schemaMetadata: {}, + } + } + + public async registerSchema( + agentContext: AgentContext, + options: RegisterSchemaOptions + ): Promise { + const schemaId = `${options.schema.issuerId}:2:${options.schema.name}:${options.schema.version}` + const indyLedgerSeqNo = getSeqNoFromSchemaId(schemaId) + + this.schemas[schemaId] = options.schema + + return { + registrationMetadata: {}, + schemaMetadata: { + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo, + }, + schemaState: { + state: 'finished', + schema: options.schema, + schemaId, + }, + } + } + + public async getCredentialDefinition( + agentContext: AgentContext, + credentialDefinitionId: string + ): Promise { + const credentialDefinition = this.credentialDefinitions[credentialDefinitionId] + + if (!credentialDefinition) { + return { + resolutionMetadata: { + error: 'notFound', + message: `Credential definition with id ${credentialDefinitionId} not found in memory registry`, + }, + credentialDefinitionId, + credentialDefinitionMetadata: {}, + } + } + + return { + resolutionMetadata: {}, + credentialDefinition, + credentialDefinitionId, + credentialDefinitionMetadata: {}, + } + } + + public async registerCredentialDefinition( + agentContext: AgentContext, + options: RegisterCredentialDefinitionOptions + ): Promise { + const indyLedgerSeqNo = getSeqNoFromSchemaId(options.credentialDefinition.schemaId) + const credentialDefinitionId = `${options.credentialDefinition.issuerId}:3:CL:${indyLedgerSeqNo}:${options.credentialDefinition.tag}` + + this.credentialDefinitions[credentialDefinitionId] = options.credentialDefinition + + return { + registrationMetadata: {}, + credentialDefinitionMetadata: {}, + credentialDefinitionState: { + state: 'finished', + credentialDefinition: options.credentialDefinition, + credentialDefinitionId, + }, + } + } + + public getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise { + throw new Error('Method not implemented.') + } + + public getRevocationList( + agentContext: AgentContext, + revocationRegistryId: string, + timestamp: number + ): Promise { + throw new Error('Method not implemented.') + } +} + +/** + * Calculates a consistent sequence number for a given schema id. + * + * Does this by hashing the schema id, transforming the hash to a number and taking the first 6 digits. + */ +function getSeqNoFromSchemaId(schemaId: string) { + const seqNo = Number( + new BigNumber(Hasher.hash(TypedArrayEncoder.fromString(schemaId), 'sha2-256')).toString().slice(0, 5) + ) + + return seqNo +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e2b1665a2a..ee9c82dfa0 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -67,6 +67,10 @@ export type { Constructor } from './utils/mixins' export * from './agent/Events' export * from './crypto/' +export { encodeAttachment } from './utils/attachment' +export { Hasher } from './utils/Hasher' +export { MessageValidator } from './utils/MessageValidator' +export { LinkedAttachment, LinkedAttachmentOptions } from './utils/LinkedAttachment' import { parseInvitationUrl } from './utils/parseInvitation' import { uuid } from './utils/uuid' diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts index eeee56e5d9..73c8082372 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts @@ -36,11 +36,6 @@ export interface IndyOfferCredentialFormat { linkedAttachments?: LinkedAttachment[] } -export interface IndyIssueCredentialFormat { - credentialDefinitionId?: string - attributes?: CredentialPreviewAttributeOptions[] -} - export interface IndyCredentialFormat extends CredentialFormat { formatKey: 'indy' credentialRecordType: 'indy' diff --git a/packages/core/src/modules/credentials/index.ts b/packages/core/src/modules/credentials/index.ts index d34680afe1..286f34276d 100644 --- a/packages/core/src/modules/credentials/index.ts +++ b/packages/core/src/modules/credentials/index.ts @@ -7,3 +7,4 @@ export * from './formats' export * from './protocol' export * from './CredentialsModule' export * from './CredentialsModuleConfig' +export { CredentialProblemReportError, CredentialProblemReportReason } from './errors' diff --git a/packages/core/src/storage/Metadata.ts b/packages/core/src/storage/Metadata.ts index 87c3e0d298..c635c1c2c5 100644 --- a/packages/core/src/storage/Metadata.ts +++ b/packages/core/src/storage/Metadata.ts @@ -1,5 +1,9 @@ +// Any is used to prevent frustrating TS errors if we just want to store arbitrary json data +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type MetadataValue = Record + export type MetadataBase = { - [key: string]: Record + [key: string]: MetadataValue } /** @@ -31,7 +35,7 @@ export class Metadata { * @returns the value saved in the key value pair * @returns null when the key could not be found */ - public get, Key extends string = string>( + public get( key: Key ): (Key extends keyof MetadataTypes ? MetadataTypes[Key] : Value) | null { return (this.data[key] as Key extends keyof MetadataTypes ? MetadataTypes[Key] : Value) ?? null @@ -43,11 +47,11 @@ export class Metadata { * @param key the key to set the metadata by * @param value the value to set in the metadata */ - public set, Key extends string = string>( + public set( key: Key, value: Key extends keyof MetadataTypes ? MetadataTypes[Key] : Value ): void { - this.data[key] = value as Record + this.data[key] = value as MetadataValue } /** @@ -56,7 +60,7 @@ export class Metadata { * @param key the key to add the metadata at * @param value the value to add in the metadata */ - public add, Key extends string = string>( + public add( key: Key, value: Partial ): void { diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 9718a60975..597cc6bda7 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -19,7 +19,7 @@ import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../ import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' import type { ProofAttributeInfo, ProofPredicateInfo } from '../src/modules/proofs/formats/indy/models' import type { AutoAcceptProof } from '../src/modules/proofs/models/ProofAutoAcceptType' -import type { Awaited } from '../src/types' +import type { Awaited, WalletConfig } from '../src/types' import type { CredDef, Schema } from 'indy-sdk' import type { Observable } from 'rxjs' @@ -162,9 +162,12 @@ export function getPostgresAgentOptions(name: string, extraConfig: Partial = {}) { +export function getAgentConfig( + name: string, + extraConfig: Partial = {} +): AgentConfig & { walletConfig: WalletConfig } { const { config, dependencies } = getAgentOptions(name, extraConfig) - return new AgentConfig(config, dependencies) + return new AgentConfig(config, dependencies) as AgentConfig & { walletConfig: WalletConfig } } export function getAgentContext({ diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 95b08fa88b..ffe975b7e1 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -75,13 +75,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { issuerId: issuerId, }, schemaId: schema.id, - resolutionMetadata: { + resolutionMetadata: {}, + schemaMetadata: { didIndyNamespace: pool.didIndyNamespace, // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. // For this reason we return it in the metadata. indyLedgerSeqNo: schema.seqNo, }, - schemaMetadata: {}, } } catch (error) { agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`, { @@ -90,7 +90,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { }) return { - schema: null, schemaId, resolutionMetadata: { error: 'notFound', @@ -157,13 +156,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { }, schemaId: schema.id, }, - registrationMetadata: { + registrationMetadata: {}, + schemaMetadata: { // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. // For this reason we return it in the metadata. indyLedgerSeqNo: schema.seqNo, didIndyNamespace: pool.didIndyNamespace, }, - schemaMetadata: {}, } } catch (error) { agentContext.config.logger.error(`Error registering schema for did '${options.schema.issuerId}'`, { @@ -229,10 +228,10 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { type: 'CL', value: credentialDefinition.value, }, - credentialDefinitionMetadata: {}, - resolutionMetadata: { + credentialDefinitionMetadata: { didIndyNamespace: pool.didIndyNamespace, }, + resolutionMetadata: {}, } } catch (error) { agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { @@ -242,7 +241,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { return { credentialDefinitionId, - credentialDefinition: null, credentialDefinitionMetadata: {}, resolutionMetadata: { error: 'notFound', @@ -280,14 +278,17 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) // TODO: this will bypass caching if done on a higher level. - const { schema, resolutionMetadata } = await this.getSchema(agentContext, options.credentialDefinition.schemaId) + const { schema, schemaMetadata, resolutionMetadata } = await this.getSchema( + agentContext, + options.credentialDefinition.schemaId + ) - if (!schema || !resolutionMetadata.indyLedgerSeqNo || typeof resolutionMetadata.indyLedgerSeqNo !== 'number') { + if (!schema || !schemaMetadata.indyLedgerSeqNo || typeof schemaMetadata.indyLedgerSeqNo !== 'number') { return { - registrationMetadata: { + registrationMetadata: {}, + credentialDefinitionMetadata: { didIndyNamespace: pool.didIndyNamespace, }, - credentialDefinitionMetadata: {}, credentialDefinitionState: { credentialDefinition: options.credentialDefinition, state: 'failed', @@ -298,7 +299,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const credentialDefinitionId = getLegacyCredentialDefinitionId( options.credentialDefinition.issuerId, - resolutionMetadata.indyLedgerSeqNo, + schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag ) @@ -327,15 +328,15 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) return { - credentialDefinitionMetadata: {}, + credentialDefinitionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, credentialDefinitionState: { credentialDefinition: options.credentialDefinition, credentialDefinitionId, state: 'finished', }, - registrationMetadata: { - didIndyNamespace: pool.didIndyNamespace, - }, + registrationMetadata: {}, } } catch (error) { agentContext.config.logger.error( @@ -388,13 +389,12 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) return { - resolutionMetadata: { - didIndyNamespace: pool.didIndyNamespace, - }, + resolutionMetadata: {}, revocationRegistryDefinition: anonCredsRevocationRegistryDefinitionFromIndySdk(revocationRegistryDefinition), revocationRegistryDefinitionId, revocationRegistryDefinitionMetadata: { issuanceType: revocationRegistryDefinition.value.issuanceType, + didIndyNamespace: pool.didIndyNamespace, }, } } catch (error) { @@ -411,7 +411,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { error: 'notFound', message: `unable to resolve revocation registry definition: ${error.message}`, }, - revocationRegistryDefinition: null, revocationRegistryDefinitionId, revocationRegistryDefinitionMetadata: {}, } @@ -469,20 +468,18 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) { return { resolutionMetadata: { - didIndyNamespace: pool.didIndyNamespace, error: `error resolving revocation registry definition with id ${revocationRegistryId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, }, - revocationListMetadata: {}, - revocationList: null, + revocationListMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, } } const isIssuanceByDefault = revocationRegistryDefinitionMetadata.issuanceType === 'ISSUANCE_BY_DEFAULT' return { - resolutionMetadata: { - didIndyNamespace: pool.didIndyNamespace, - }, + resolutionMetadata: {}, revocationList: anonCredsRevocationListFromIndySdk( revocationRegistryId, revocationRegistryDefinition, @@ -490,7 +487,9 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { deltaTimestamp, isIssuanceByDefault ), - revocationListMetadata: {}, + revocationListMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, } } catch (error) { agentContext.config.logger.error( @@ -506,7 +505,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { error: 'notFound', message: `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation: ${error.message}`, }, - revocationList: null, revocationListMetadata: {}, } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 49b619332d..e472d1c1c4 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -4,12 +4,13 @@ import type { CreateCredentialRequestOptions, CreateCredentialRequestReturn, CreateProofOptions, - CredentialInfo, + AnonCredsCredentialInfo, GetCredentialOptions, StoreCredentialOptions, GetCredentialsForProofRequestOptions, GetCredentialsForProofRequestReturn, - RequestedCredentials, + AnonCredsRequestedCredentials, + AnonCredsCredentialRequestMetadata, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { @@ -18,6 +19,8 @@ import type { RevStates, Schemas, IndyCredential as IndySdkCredential, + CredReqMetadata, + IndyProofRequest, } from 'indy-sdk' import { inject } from '@aries-framework/core' @@ -26,6 +29,7 @@ import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' import { getIndySeqNoFromUnqualifiedCredentialDefinitionId } from '../utils/identifiers' +import { generateLegacyProverDidLikeString } from '../utils/proverDid' import { indySdkCredentialDefinitionFromAnonCreds, indySdkRevocationRegistryDefinitionFromAnonCreds, @@ -84,7 +88,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { const indyProof = await this.indySdk.proverCreateProof( agentContext.wallet.handle, - proofRequest, + proofRequest as IndyProofRequest, this.parseRequestedCredentials(requestedCredentials), agentContext.wallet.masterSecretId, indySchemas, @@ -122,7 +126,8 @@ export class IndySdkHolderService implements AnonCredsHolderService { return await this.indySdk.proverStoreCredential( agentContext.wallet.handle, options.credentialId ?? null, - options.credentialRequestMetadata, + // The type is typed as a Record in the indy-sdk, but the anoncreds package contains the correct type + options.credentialRequestMetadata as unknown as CredReqMetadata, options.credential, indySdkCredentialDefinitionFromAnonCreds(options.credentialDefinitionId, options.credentialDefinition), indyRevocationRegistryDefinition @@ -136,7 +141,10 @@ export class IndySdkHolderService implements AnonCredsHolderService { } } - public async getCredential(agentContext: AgentContext, options: GetCredentialOptions): Promise { + public async getCredential( + agentContext: AgentContext, + options: GetCredentialOptions + ): Promise { assertIndySdkWallet(agentContext.wallet) try { @@ -145,7 +153,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { return { credentialDefinitionId: result.cred_def_id, attributes: result.attrs, - referent: result.referent, + credentialId: result.referent, schemaId: result.schema_id, credentialRevocationId: result.cred_rev_id, revocationRegistryId: result.rev_reg_id, @@ -165,10 +173,14 @@ export class IndySdkHolderService implements AnonCredsHolderService { ): Promise { assertIndySdkWallet(agentContext.wallet) + // We just generate a prover did like string, as it's not used for anything and we don't need + // to prove ownership of the did. It's deprecated in AnonCreds v1, but kept for backwards compatibility + const proverDid = generateLegacyProverDidLikeString() + try { const result = await this.indySdk.proverCreateCredentialReq( agentContext.wallet.handle, - options.holderDid, + proverDid, options.credentialOffer, // NOTE: Is it safe to use the cred_def_id from the offer? I think so. You can't create a request // for a cred def that is not in the offer @@ -180,7 +192,8 @@ export class IndySdkHolderService implements AnonCredsHolderService { return { credentialRequest: result[0], - credentialRequestMetadata: result[1], + // The type is typed as a Record in the indy-sdk, but the anoncreds package contains the correct type + credentialRequestMetadata: result[1] as unknown as AnonCredsCredentialRequestMetadata, } } catch (error) { agentContext.config.logger.error(`Error creating Indy Credential Request`, { @@ -216,7 +229,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { // Open indy credential search const searchHandle = await this.indySdk.proverSearchCredentialsForProofReq( agentContext.wallet.handle, - options.proofRequest, + options.proofRequest as IndyProofRequest, options.extraQuery ?? null ) @@ -240,7 +253,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { return credentials.map((credential) => ({ credentialInfo: { credentialDefinitionId: credential.cred_info.cred_def_id, - referent: credential.cred_info.referent, + credentialId: credential.cred_info.referent, attributes: credential.cred_info.attrs, schemaId: credential.cred_info.schema_id, revocationRegistryId: credential.cred_info.rev_reg_id, @@ -299,7 +312,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { /** * Converts a public api form of {@link RequestedCredentials} interface into a format {@link Indy.IndyRequestedCredentials} that Indy SDK expects. **/ - private parseRequestedCredentials(requestedCredentials: RequestedCredentials): IndyRequestedCredentials { + private parseRequestedCredentials(requestedCredentials: AnonCredsRequestedCredentials): IndyRequestedCredentials { const indyRequestedCredentials: IndyRequestedCredentials = { requested_attributes: {}, requested_predicates: {}, diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index f877be4f75..96e9ef266a 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -1,5 +1,4 @@ import type { CreateCredentialDefinitionMetadata } from './IndySdkIssuerServiceMetadata' -import type { IndySdkUtilitiesService } from './IndySdkUtilitiesService' import type { AnonCredsIssuerService, CreateCredentialDefinitionOptions, @@ -18,15 +17,15 @@ import { AriesFrameworkError, inject } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' +import { generateLegacyProverDidLikeString } from '../utils/proverDid' +import { createTailsReader } from '../utils/tails' import { indySdkSchemaFromAnonCreds } from '../utils/transform' export class IndySdkIssuerService implements AnonCredsIssuerService { private indySdk: IndySdk - private IndySdkUtilitiesService: IndySdkUtilitiesService - public constructor(IndySdkUtilitiesService: IndySdkUtilitiesService, @inject(IndySdkSymbol) indySdk: IndySdk) { + public constructor(@inject(IndySdkSymbol) indySdk: IndySdk) { this.indySdk = indySdk - this.IndySdkUtilitiesService = IndySdkUtilitiesService } public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { @@ -73,7 +72,7 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { return { issuerId, tag: credentialDefinition.tag, - schemaId: credentialDefinition.schemaId, + schemaId, type: 'CL', value: credentialDefinition.value, } @@ -103,16 +102,19 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { assertIndySdkWallet(agentContext.wallet) try { // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present - const tailsReaderHandle = tailsFilePath ? await this.IndySdkUtilitiesService.createTailsReader(tailsFilePath) : 0 + const tailsReaderHandle = tailsFilePath ? await createTailsReader(agentContext, tailsFilePath) : 0 if (revocationRegistryId || tailsFilePath) { throw new AriesFrameworkError('Revocation not supported yet') } + // prover_did is deprecated and thus if not provided we generate something on our side, as it's still required by the indy sdk + const proverDid = credentialRequest.prover_did ?? generateLegacyProverDidLikeString() + const [credential, credentialRevocationId] = await this.indySdk.issuerCreateCredential( agentContext.wallet.handle, credentialOffer, - credentialRequest, + { ...credentialRequest, prover_did: proverDid }, credentialValues, revocationRegistryId ?? null, tailsReaderHandle diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts index 0ed637a6ee..4f7eb6ef42 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts @@ -2,9 +2,9 @@ import type { AnonCredsRevocationRegistryDefinition, AnonCredsRevocationList, AnonCredsProofRequest, - RequestedCredentials, - CredentialInfo, - NonRevokedInterval, + AnonCredsRequestedCredentials, + AnonCredsCredentialInfo, + AnonCredsNonRevokedInterval, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { RevStates } from 'indy-sdk' @@ -13,13 +13,12 @@ import { AriesFrameworkError, inject, injectable } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' +import { createTailsReader } from '../utils/tails' import { indySdkRevocationDeltaFromAnonCreds, indySdkRevocationRegistryDefinitionFromAnonCreds, } from '../utils/transform' -import { IndySdkUtilitiesService } from './IndySdkUtilitiesService' - enum RequestReferentType { Attribute = 'attribute', Predicate = 'predicate', @@ -34,11 +33,9 @@ enum RequestReferentType { @injectable() export class IndySdkRevocationService { private indySdk: IndySdk - private indySdkUtilitiesService: IndySdkUtilitiesService - public constructor(indyUtilitiesService: IndySdkUtilitiesService, @inject(IndySdkSymbol) indySdk: IndySdk) { + public constructor(@inject(IndySdkSymbol) indySdk: IndySdk) { this.indySdk = indySdk - this.indySdkUtilitiesService = indyUtilitiesService } /** @@ -47,7 +44,7 @@ export class IndySdkRevocationService { public async createRevocationState( agentContext: AgentContext, proofRequest: AnonCredsProofRequest, - requestedCredentials: RequestedCredentials, + requestedCredentials: AnonCredsRequestedCredentials, revocationRegistries: { [revocationRegistryDefinitionId: string]: { // Tails is already downloaded @@ -68,8 +65,8 @@ export class IndySdkRevocationService { const referentCredentials: Array<{ type: RequestReferentType referent: string - credentialInfo: CredentialInfo - referentRevocationInterval: NonRevokedInterval | undefined + credentialInfo: AnonCredsCredentialInfo + referentRevocationInterval: AnonCredsNonRevokedInterval | undefined }> = [] //Retrieve information for referents and push to single array @@ -114,7 +111,7 @@ export class IndySdkRevocationService { // most accurate revocation list for a given timestamp. It doesn't have to be that the revocationList is from the `to` timestamp however. const revocationList = revocationLists[requestRevocationInterval.to] - const tails = await this.indySdkUtilitiesService.createTailsReader(tailsFilePath) + const tails = await createTailsReader(agentContext, tailsFilePath) const revocationState = await this.indySdk.createRevocationState( tails, @@ -152,7 +149,7 @@ export class IndySdkRevocationService { // TODO: we should do this verification on a higher level I think? // Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints private assertRevocationInterval( - revocationInterval: NonRevokedInterval + revocationInterval: AnonCredsNonRevokedInterval ): asserts revocationInterval is BestPracticeNonRevokedInterval { if (!revocationInterval.to) { throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts deleted file mode 100644 index 1ac0dec33e..0000000000 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkUtilitiesService.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { BlobReaderHandle } from 'indy-sdk' - -import { - AriesFrameworkError, - FileSystem, - getDirFromFilePath, - IndySdkError, - InjectionSymbols, - Logger, -} from '@aries-framework/core' -import { inject, injectable } from 'tsyringe' - -import { isIndyError } from '../../error' -import { IndySdk, IndySdkSymbol } from '../../types' - -@injectable() -export class IndySdkUtilitiesService { - private indySdk: IndySdk - private logger: Logger - private fileSystem: FileSystem - - public constructor( - @inject(InjectionSymbols.Logger) logger: Logger, - @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, - @inject(IndySdkSymbol) indySdk: IndySdk - ) { - this.indySdk = indySdk - this.logger = logger - this.fileSystem = fileSystem - } - - /** - * Get a handler for the blob storage tails file reader. - * - * @param tailsFilePath The path of the tails file - * @returns The blob storage reader handle - */ - public async createTailsReader(tailsFilePath: string): Promise { - try { - this.logger.debug(`Opening tails reader at path ${tailsFilePath}`) - const tailsFileExists = await this.fileSystem.exists(tailsFilePath) - - // Extract directory from path (should also work with windows paths) - const dirname = getDirFromFilePath(tailsFilePath) - - if (!tailsFileExists) { - throw new AriesFrameworkError(`Tails file does not exist at path ${tailsFilePath}`) - } - - const tailsReaderConfig = { - base_dir: dirname, - } - - const tailsReader = await this.indySdk.openBlobStorageReader('default', tailsReaderConfig) - this.logger.debug(`Opened tails reader at path ${tailsFilePath}`) - return tailsReader - } catch (error) { - if (isIndyError(error)) { - throw new IndySdkError(error) - } - - throw error - } - } -} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts index d302e66c97..d07a4ef1ef 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -1,5 +1,5 @@ import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' -import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs } from 'indy-sdk' +import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs, IndyProofRequest } from 'indy-sdk' import { inject } from '@aries-framework/core' @@ -72,7 +72,7 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { } return await this.indySdk.verifierVerifyProof( - options.proofRequest, + options.proofRequest as IndyProofRequest, options.proof, indySchemas, indyCredentialDefinitions, diff --git a/packages/indy-sdk/src/anoncreds/utils/proverDid.ts b/packages/indy-sdk/src/anoncreds/utils/proverDid.ts new file mode 100644 index 0000000000..2d12648c70 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/proverDid.ts @@ -0,0 +1,12 @@ +import { TypedArrayEncoder, utils } from '@aries-framework/core' + +/** + * generates a string that adheres to the format of a legacy indy did. + * + * This can be used for the `prover_did` property that is required in the legacy anoncreds credential + * request. This doesn't actually have to be a did, but some frameworks (like ACA-Py) require it to be + * an unqualified indy did. + */ +export function generateLegacyProverDidLikeString() { + return TypedArrayEncoder.toBase58(TypedArrayEncoder.fromString(utils.uuid()).slice(0, 16)) +} diff --git a/packages/indy-sdk/src/anoncreds/utils/tails.ts b/packages/indy-sdk/src/anoncreds/utils/tails.ts new file mode 100644 index 0000000000..f803ea5d78 --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/tails.ts @@ -0,0 +1,45 @@ +import type { IndySdk } from '../../types' +import type { AgentContext, FileSystem } from '@aries-framework/core' + +import { AriesFrameworkError, getDirFromFilePath, IndySdkError, InjectionSymbols } from '@aries-framework/core' + +import { isIndyError } from '../../error' +import { IndySdkSymbol } from '../../types' + +/** + * Get a handler for the blob storage tails file reader. + * + * @param agentContext The agent context + * @param tailsFilePath The path of the tails file + * @returns The blob storage reader handle + */ +export async function createTailsReader(agentContext: AgentContext, tailsFilePath: string) { + const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + + try { + agentContext.config.logger.debug(`Opening tails reader at path ${tailsFilePath}`) + const tailsFileExists = await fileSystem.exists(tailsFilePath) + + // Extract directory from path (should also work with windows paths) + const dirname = getDirFromFilePath(tailsFilePath) + + if (!tailsFileExists) { + throw new AriesFrameworkError(`Tails file does not exist at path ${tailsFilePath}`) + } + + const tailsReaderConfig = { + base_dir: dirname, + } + + const tailsReader = await indySdk.openBlobStorageReader('default', tailsReaderConfig) + agentContext.config.logger.debug(`Opened tails reader at path ${tailsFilePath}`) + return tailsReader + } catch (error) { + if (isIndyError(error)) { + throw new IndySdkError(error) + } + + throw error + } +} diff --git a/yarn.lock b/yarn.lock index 30954778a4..0f10727077 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3479,7 +3479,7 @@ bindings@^1.3.1: dependencies: file-uri-to-path "1.0.0" -bn.js@^5.2.0: +bn.js@^5.2.0, bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== From b5eb08e99d7ea61adefb8c6c0c5c99c6c1ba1597 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 30 Jan 2023 10:44:17 -0300 Subject: [PATCH 020/139] feat(indy-vdr): did:sov resolver (#1247) Signed-off-by: Ariel Gentile --- .../src/dids/IndyVdrSovDidResolver.ts | 95 +++++++++ .../__tests__/IndyVdrSovDidResolver.test.ts | 127 ++++++++++++ .../didSovR1xKJw17sUoXhejEpugMYJ.json | 51 +++++ .../didSovWJz9mHyW9BZksioQnRsrAo.json | 49 +++++ packages/indy-vdr/src/dids/didSovUtil.ts | 166 ++++++++++++++++ packages/indy-vdr/src/dids/index.ts | 1 + packages/indy-vdr/src/index.ts | 2 + .../indy-vdr/src/pool/IndyVdrPoolService.ts | 2 +- packages/indy-vdr/src/pool/index.ts | 1 + packages/indy-vdr/tests/helpers.ts | 43 ++++ .../tests/indy-vdr-did-resolver.e2e.test.ts | 188 ++++++++++++++++++ packages/indy-vdr/tests/setup.ts | 2 +- 12 files changed, 725 insertions(+), 2 deletions(-) create mode 100644 packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts create mode 100644 packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json create mode 100644 packages/indy-vdr/src/dids/didSovUtil.ts create mode 100644 packages/indy-vdr/src/dids/index.ts create mode 100644 packages/indy-vdr/tests/helpers.ts create mode 100644 packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts new file mode 100644 index 0000000000..bb51d4aeaa --- /dev/null +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -0,0 +1,95 @@ +import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' +import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' + +import { GetAttribRequest, GetNymRequest } from 'indy-vdr-test-shared' + +import { IndyVdrError, IndyVdrNotFoundError } from '../error' +import { IndyVdrPoolService } from '../pool' + +import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' + +export class IndyVdrSovDidResolver implements DidResolver { + public readonly supportedMethods = ['sov'] + + public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { + const didDocumentMetadata = {} + + try { + const nym = await this.getPublicDid(agentContext, parsed.id) + const endpoints = await this.getEndpointsForDid(agentContext, parsed.id) + + const keyAgreementId = `${parsed.did}#key-agreement-1` + const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) + addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) + + return { + didDocument: builder.build(), + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } + + private async getPublicDid(agentContext: AgentContext, did: string) { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + + const request = new GetNymRequest({ dest: did }) + + const didResponse = await pool.submitReadRequest(request) + + if (!didResponse.result.data) { + throw new IndyVdrNotFoundError(`DID ${did} not found`) + } + return JSON.parse(didResponse.result.data) as GetNymResponseData + } + + private async getEndpointsForDid(agentContext: AgentContext, did: string) { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + + try { + agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.indyNamespace}'`) + + const request = new GetAttribRequest({ targetDid: did, raw: 'endpoint' }) + + agentContext.config.logger.debug( + `Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.indyNamespace}'` + ) + const response = await pool.submitReadRequest(request) + + if (!response.result.data) return {} + + const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib + agentContext.config.logger.debug( + `Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.indyNamespace}'`, + { + response, + endpoints, + } + ) + + return endpoints ?? {} + } catch (error) { + agentContext.config.logger.error( + `Error retrieving endpoints for did '${did}' from ledger '${pool.indyNamespace}'`, + { + error, + } + ) + + throw new IndyVdrError(error) + } + } +} diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts new file mode 100644 index 0000000000..269aaa1a46 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts @@ -0,0 +1,127 @@ +import { JsonTransformer } from '@aries-framework/core' + +import { parseDid } from '../../../../core/src/modules/dids/domain/parse' +import { getAgentConfig, getAgentContext, mockProperty } from '../../../../core/tests/helpers' +import { IndyVdrPool, IndyVdrPoolService } from '../../pool' +import { IndyVdrSovDidResolver } from '../IndyVdrSovDidResolver' + +import didSovR1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' +import didSovWJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' + +jest.mock('../../pool/IndyVdrPoolService') +const IndyVdrPoolServiceMock = IndyVdrPoolService as jest.Mock +const poolServiceMock = new IndyVdrPoolServiceMock() + +jest.mock('../../pool/IndyVdrPool') +const IndyVdrPoolMock = IndyVdrPool as jest.Mock +const poolMock = new IndyVdrPoolMock() +mockProperty(poolMock, 'indyNamespace', 'local') +jest.spyOn(poolServiceMock, 'getPoolForDid').mockResolvedValue(poolMock) + +const agentConfig = getAgentConfig('IndyVdrSovDidResolver') + +const agentContext = getAgentContext({ + agentConfig, + registerInstances: [[IndyVdrPoolService, poolServiceMock]], +}) + +const resolver = new IndyVdrSovDidResolver() + +describe('DidResolver', () => { + describe('IndyVdrSovDidResolver', () => { + it('should correctly resolve a did:sov document', async () => { + const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' + + const nymResponse = { + result: { + data: JSON.stringify({ + did: 'R1xKJw17sUoXhejEpugMYJ', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + role: 'ENDORSER', + }), + }, + } + + const attribResponse = { + result: { + data: JSON.stringify({ + endpoint: { + endpoint: 'https://ssi.com', + profile: 'https://profile.com', + hub: 'https://hub.com', + }, + }), + }, + } + + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + + const result = await resolver.resolve(agentContext, did, parseDid(did)) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didSovR1xKJw17sUoXhejEpugMYJFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should resolve a did:sov document with routingKeys and types entries in the attrib', async () => { + const did = 'did:sov:WJz9mHyW9BZksioQnRsrAo' + + const nymResponse = { + result: { + data: JSON.stringify({ + did: 'WJz9mHyW9BZksioQnRsrAo', + verkey: 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8', + role: 'ENDORSER', + }), + }, + } + + const attribResponse = { + result: { + data: JSON.stringify({ + endpoint: { + endpoint: 'https://agent.com', + types: ['endpoint', 'did-communication', 'DIDComm'], + routingKeys: ['routingKey1', 'routingKey2'], + }, + }), + }, + } + + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + + const result = await resolver.resolve(agentContext, did, parseDid(did)) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didSovWJz9mHyW9BZksioQnRsrAoFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { + const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' + + jest.spyOn(poolMock, 'submitReadRequest').mockRejectedValue(new Error('Error submitting read request')) + + const result = await resolver.resolve(agentContext, did, parseDid(did)) + + expect(result).toMatchObject({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did 'did:sov:R1xKJw17sUoXhejEpugMYJ': Error: Error submitting read request`, + }, + }) + }) + }) +}) diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json new file mode 100644 index 0000000000..6a6e4ed706 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json @@ -0,0 +1,51 @@ +{ + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1" + ], + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ", + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:R1xKJw17sUoXhejEpugMYJ", + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#key-1", + "publicKeyBase58": "E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu" + }, + { + "type": "X25519KeyAgreementKey2019", + "controller": "did:sov:R1xKJw17sUoXhejEpugMYJ", + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1", + "publicKeyBase58": "Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt" + } + ], + "authentication": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-1"], + "assertionMethod": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-1"], + "keyAgreement": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], + "service": [ + { + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#endpoint", + "type": "endpoint", + "serviceEndpoint": "https://ssi.com" + }, + { + "accept": ["didcomm/aip2;env=rfc19"], + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#did-communication", + "priority": 0, + "recipientKeys": ["did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], + "routingKeys": [], + "serviceEndpoint": "https://ssi.com", + "type": "did-communication" + }, + { + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#profile", + "serviceEndpoint": "https://profile.com", + "type": "profile" + }, + { + "id": "did:sov:R1xKJw17sUoXhejEpugMYJ#hub", + "serviceEndpoint": "https://hub.com", + "type": "hub" + } + ] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json new file mode 100644 index 0000000000..7b74e0587f --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json @@ -0,0 +1,49 @@ +{ + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1", + "https://didcomm.org/messaging/contexts/v2" + ], + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo", + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:WJz9mHyW9BZksioQnRsrAo", + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#key-1", + "publicKeyBase58": "GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8" + }, + { + "type": "X25519KeyAgreementKey2019", + "controller": "did:sov:WJz9mHyW9BZksioQnRsrAo", + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1", + "publicKeyBase58": "S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud" + } + ], + "authentication": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-1"], + "assertionMethod": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-1"], + "keyAgreement": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "service": [ + { + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#endpoint", + "type": "endpoint", + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#did-communication", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "routingKeys": ["routingKey1", "routingKey2"], + "accept": ["didcomm/aip2;env=rfc19"], + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#didcomm-1", + "type": "DIDComm", + "serviceEndpoint": "https://agent.com", + "accept": ["didcomm/v2"], + "routingKeys": ["routingKey1", "routingKey2"] + } + ] +} diff --git a/packages/indy-vdr/src/dids/didSovUtil.ts b/packages/indy-vdr/src/dids/didSovUtil.ts new file mode 100644 index 0000000000..9fbe414b78 --- /dev/null +++ b/packages/indy-vdr/src/dids/didSovUtil.ts @@ -0,0 +1,166 @@ +import { + TypedArrayEncoder, + DidDocumentService, + DidDocumentBuilder, + DidCommV1Service, + DidCommV2Service, + convertPublicKeyToX25519, +} from '@aries-framework/core' + +export interface IndyEndpointAttrib { + endpoint?: string + types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> + routingKeys?: string[] + [key: string]: unknown +} + +export interface GetNymResponseData { + did: string + verkey: string + role: string +} + +export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ + +/** + * Check a base58 encoded string against a regex expression to determine if it is a full valid verkey + * @param verkey Base58 encoded string representation of a verkey + * @return Boolean indicating if the string is a valid verkey + */ +export function isFullVerkey(verkey: string): boolean { + return FULL_VERKEY_REGEX.test(verkey) +} + +export function getFullVerkey(did: string, verkey: string) { + if (isFullVerkey(verkey)) return verkey + + // Did could have did:xxx prefix, only take the last item after : + const id = did.split(':').pop() ?? did + // Verkey is prefixed with ~ if abbreviated + const verkeyWithoutTilde = verkey.slice(1) + + // Create base58 encoded public key (32 bytes) + return TypedArrayEncoder.toBase58( + Buffer.concat([ + // Take did identifier (16 bytes) + TypedArrayEncoder.fromBase58(id), + // Concat the abbreviated verkey (16 bytes) + TypedArrayEncoder.fromBase58(verkeyWithoutTilde), + ]) + ) +} + +export function sovDidDocumentFromDid(fullDid: string, verkey: string) { + const verificationMethodId = `${fullDid}#key-1` + const keyAgreementId = `${fullDid}#key-agreement-1` + + const publicKeyBase58 = getFullVerkey(fullDid, verkey) + const publicKeyX25519 = TypedArrayEncoder.toBase58( + convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(publicKeyBase58)) + ) + + const builder = new DidDocumentBuilder(fullDid) + .addContext('https://w3id.org/security/suites/ed25519-2018/v1') + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: fullDid, + id: verificationMethodId, + publicKeyBase58: publicKeyBase58, + type: 'Ed25519VerificationKey2018', + }) + .addVerificationMethod({ + controller: fullDid, + id: keyAgreementId, + publicKeyBase58: publicKeyX25519, + type: 'X25519KeyAgreementKey2019', + }) + .addAuthentication(verificationMethodId) + .addAssertionMethod(verificationMethodId) + .addKeyAgreement(keyAgreementId) + + return builder +} + +// Process Indy Attrib Endpoint Types according to: https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html > Read (Resolve) > DID Service Endpoint +function processEndpointTypes(types?: string[]) { + const expectedTypes = ['endpoint', 'did-communication', 'DIDComm'] + const defaultTypes = ['endpoint', 'did-communication'] + + // Return default types if types "is NOT present [or] empty" + if (!types || types.length <= 0) { + return defaultTypes + } + + // Return default types if types "contain any other values" + for (const type of types) { + if (!expectedTypes.includes(type)) { + return defaultTypes + } + } + + // Return provided types + return types +} + +export function addServicesFromEndpointsAttrib( + builder: DidDocumentBuilder, + did: string, + endpoints: IndyEndpointAttrib, + keyAgreementId: string +) { + const { endpoint, routingKeys, types, ...otherEndpoints } = endpoints + + if (endpoint) { + const processedTypes = processEndpointTypes(types) + + // If 'endpoint' included in types, add id to the services array + if (processedTypes.includes('endpoint')) { + builder.addService( + new DidDocumentService({ + id: `${did}#endpoint`, + serviceEndpoint: endpoint, + type: 'endpoint', + }) + ) + } + + // If 'did-communication' included in types, add DIDComm v1 entry + if (processedTypes.includes('did-communication')) { + builder.addService( + new DidCommV1Service({ + id: `${did}#did-communication`, + serviceEndpoint: endpoint, + priority: 0, + routingKeys: routingKeys ?? [], + recipientKeys: [keyAgreementId], + accept: ['didcomm/aip2;env=rfc19'], + }) + ) + + // If 'DIDComm' included in types, add DIDComm v2 entry + if (processedTypes.includes('DIDComm')) { + builder + .addService( + new DidCommV2Service({ + id: `${did}#didcomm-1`, + serviceEndpoint: endpoint, + routingKeys: routingKeys ?? [], + accept: ['didcomm/v2'], + }) + ) + .addContext('https://didcomm.org/messaging/contexts/v2') + } + } + } + + // Add other endpoint types + for (const [type, endpoint] of Object.entries(otherEndpoints)) { + builder.addService( + new DidDocumentService({ + id: `${did}#${type}`, + serviceEndpoint: endpoint as string, + type, + }) + ) + } +} diff --git a/packages/indy-vdr/src/dids/index.ts b/packages/indy-vdr/src/dids/index.ts new file mode 100644 index 0000000000..7f9973684d --- /dev/null +++ b/packages/indy-vdr/src/dids/index.ts @@ -0,0 +1 @@ +export { IndyVdrSovDidResolver } from './IndyVdrSovDidResolver' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index 8a5ca6c21a..ca0fe42285 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -1,3 +1,5 @@ +export { IndyVdrSovDidResolver } from './dids' + try { // eslint-disable-next-line import/no-extraneous-dependencies require('indy-vdr-test-nodejs') diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index 2cc1b5a206..0ac5d9a1aa 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -188,7 +188,7 @@ export class IndyVdrPoolService { this.logger.trace(`Retrieved did '${did}' from ledger '${pool.indyNamespace}'`, result) return { - did: result, + did: { nymResponse: { did: result.dest, verkey: result.verkey }, indyNamespace: pool.indyNamespace }, pool, response, } diff --git a/packages/indy-vdr/src/pool/index.ts b/packages/indy-vdr/src/pool/index.ts index 1e1f1b52f8..ec4bc06677 100644 --- a/packages/indy-vdr/src/pool/index.ts +++ b/packages/indy-vdr/src/pool/index.ts @@ -1 +1,2 @@ export * from './IndyVdrPool' +export * from './IndyVdrPoolService' diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts new file mode 100644 index 0000000000..7ea99d8263 --- /dev/null +++ b/packages/indy-vdr/tests/helpers.ts @@ -0,0 +1,43 @@ +import type { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' +import type { AgentContext, Key } from '@aries-framework/core' + +import { KeyType } from '@aries-framework/core' +import { AttribRequest, NymRequest } from 'indy-vdr-test-shared' + +import { indyDidFromPublicKeyBase58 } from '../src/utils/did' + +export async function createDidOnLedger( + indyVdrPoolService: IndyVdrPoolService, + agentContext: AgentContext, + submitterDid: string, + signerKey: Key +) { + const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') + + const key = await agentContext.wallet.createKey({ keyType: KeyType.Ed25519 }) + const did = indyDidFromPublicKeyBase58(key.publicKeyBase58) + + const nymRequest = new NymRequest({ + dest: did, + submitterDid, + verkey: key.publicKeyBase58, + }) + + await pool.submitWriteRequest(agentContext, nymRequest, signerKey) + + const attribRequest = new AttribRequest({ + submitterDid: did, + targetDid: did, + raw: JSON.stringify({ + endpoint: { + endpoint: 'https://agent.com', + types: ['endpoint', 'did-communication', 'DIDComm'], + routingKeys: ['routingKey1', 'routingKey2'], + }, + }), + }) + + await pool.submitWriteRequest(agentContext, attribRequest, key) + + return { did, key } +} diff --git a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts new file mode 100644 index 0000000000..72a09afc83 --- /dev/null +++ b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts @@ -0,0 +1,188 @@ +import type { Key } from '@aries-framework/core' + +import { + CacheModuleConfig, + InMemoryLruCache, + JsonTransformer, + IndyWallet, + KeyType, + SigningProviderRegistry, +} from '@aries-framework/core' + +import { parseDid } from '../../core/src/modules/dids/domain/parse' +import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import testLogger from '../../core/tests/logger' +import { IndyVdrSovDidResolver } from '../src/dids' +import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' +import { indyDidFromPublicKeyBase58 } from '../src/utils/did' + +import { createDidOnLedger } from './helpers' + +const logger = testLogger +const wallet = new IndyWallet(agentDependencies, logger, new SigningProviderRegistry([])) +const agentConfig = getAgentConfig('IndyVdrResolver E2E', { logger }) + +const cache = new InMemoryLruCache({ limit: 200 }) +const indyVdrSovDidResolver = new IndyVdrSovDidResolver() + +const config = { + isProduction: false, + genesisTransactions, + indyNamespace: `pool:localtest`, + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, +} as const + +let signerKey: Key + +const agentContext = getAgentContext({ + wallet, + agentConfig, + registerInstances: [ + [IndyVdrPoolService, new IndyVdrPoolService(logger)], + [CacheModuleConfig, new CacheModuleConfig({ cache })], + ], +}) + +const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) +indyVdrPoolService.setPools([config]) + +describe('IndyVdrSov', () => { + beforeAll(async () => { + await indyVdrPoolService.connectToPools() + + if (agentConfig.walletConfig) { + await wallet.createAndOpen(agentConfig.walletConfig) + } + + signerKey = await wallet.createKey({ seed: '000000000000000000000000Trustee9', keyType: KeyType.Ed25519 }) + }) + + afterAll(async () => { + for (const pool of indyVdrPoolService.pools) { + pool.close() + } + + await wallet.delete() + }) + + describe('did:sov resolver', () => { + test('can resolve a did sov using the pool', async () => { + const did = 'did:sov:TL1EaPFCZ8Si5aUrqScBDt' + const didResult = await indyVdrSovDidResolver.resolve(agentContext, did, parseDid(did)) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#key-1`, + publicKeyBase58: expect.any(String), + }, + { + controller: did, + type: 'X25519KeyAgreementKey2019', + id: `${did}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#key-1`], + assertionMethod: [`${did}#key-1`], + keyAgreement: [`${did}#key-agreement-1`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('resolve a did with endpoints', async () => { + // First we need to create a new DID and add ATTRIB endpoint to it + const { did } = await createDidOnLedger( + indyVdrPoolService, + agentContext, + indyDidFromPublicKeyBase58(signerKey.publicKeyBase58), + signerKey + ) + + // DID created. Now resolve it + + const fullyQualifiedDid = `did:sov:${did}` + const didResult = await indyVdrSovDidResolver.resolve( + agentContext, + fullyQualifiedDid, + parseDid(fullyQualifiedDid) + ) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: fullyQualifiedDid, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: fullyQualifiedDid, + id: `${fullyQualifiedDid}#key-1`, + publicKeyBase58: expect.any(String), + }, + { + controller: fullyQualifiedDid, + type: 'X25519KeyAgreementKey2019', + id: `${fullyQualifiedDid}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${fullyQualifiedDid}#key-1`], + assertionMethod: [`${fullyQualifiedDid}#key-1`], + keyAgreement: [`${fullyQualifiedDid}#key-agreement-1`], + service: [ + { + id: `${fullyQualifiedDid}#endpoint`, + type: 'endpoint', + serviceEndpoint: 'https://agent.com', + }, + { + id: `${fullyQualifiedDid}#did-communication`, + type: 'did-communication', + priority: 0, + recipientKeys: [`${fullyQualifiedDid}#key-agreement-1`], + routingKeys: ['routingKey1', 'routingKey2'], + accept: ['didcomm/aip2;env=rfc19'], + serviceEndpoint: 'https://agent.com', + }, + { + id: `${fullyQualifiedDid}#didcomm-1`, + type: 'DIDComm', + serviceEndpoint: 'https://agent.com', + accept: ['didcomm/v2'], + routingKeys: ['routingKey1', 'routingKey2'], + }, + ], + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + }) +}) diff --git a/packages/indy-vdr/tests/setup.ts b/packages/indy-vdr/tests/setup.ts index ce7749d25e..d69181fd10 100644 --- a/packages/indy-vdr/tests/setup.ts +++ b/packages/indy-vdr/tests/setup.ts @@ -1,4 +1,4 @@ // Needed to register indy-vdr node bindings import '../src/index' -jest.setTimeout(20000) +jest.setTimeout(60000) From acdb20a79d038fb4163d281ee8de0ccb649fdc32 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 2 Feb 2023 12:23:53 -0300 Subject: [PATCH 021/139] feat(indy-vdr): use @hyperledger packages (#1252) Signed-off-by: Ariel Gentile --- packages/indy-vdr/package.json | 6 +-- .../src/dids/IndyVdrSovDidResolver.ts | 2 +- packages/indy-vdr/src/index.ts | 2 +- packages/indy-vdr/src/pool/IndyVdrPool.ts | 11 ++--- .../indy-vdr/src/pool/IndyVdrPoolService.ts | 4 +- packages/indy-vdr/tests/helpers.ts | 2 +- .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 2 +- yarn.lock | 44 ++++++++++++------- 8 files changed, 42 insertions(+), 31 deletions(-) diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 32c8689d5d..e12d0116de 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -26,11 +26,11 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "indy-vdr-test-shared": "^0.1.3" + "@hyperledger/indy-vdr-shared": "^0.1.0-dev.4" }, "devDependencies": { - "indy-vdr-test-nodejs": "^0.1.3", - "rimraf": "~4.0.7", + "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.4", + "rimraf": "^4.0.7", "typescript": "~4.9.4" } } diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts index bb51d4aeaa..92fbefa20f 100644 --- a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -1,7 +1,7 @@ import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' -import { GetAttribRequest, GetNymRequest } from 'indy-vdr-test-shared' +import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrError, IndyVdrNotFoundError } from '../error' import { IndyVdrPoolService } from '../pool' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index ca0fe42285..8278d55827 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -2,7 +2,7 @@ export { IndyVdrSovDidResolver } from './dids' try { // eslint-disable-next-line import/no-extraneous-dependencies - require('indy-vdr-test-nodejs') + require('@hyperledger/indy-vdr-nodejs') } catch (error) { throw new Error('Error registering nodejs bindings for Indy VDR') } diff --git a/packages/indy-vdr/src/pool/IndyVdrPool.ts b/packages/indy-vdr/src/pool/IndyVdrPool.ts index 6958d882ab..99ba0d1b06 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPool.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPool.ts @@ -1,5 +1,5 @@ import type { Logger, AgentContext, Key } from '@aries-framework/core' -import type { IndyVdrRequest, IndyVdrPool as indyVdrPool } from 'indy-vdr-test-shared' +import type { IndyVdrRequest, IndyVdrPool as indyVdrPool } from '@hyperledger/indy-vdr-shared' import { TypedArrayEncoder } from '@aries-framework/core' import { @@ -7,7 +7,7 @@ import { GetAcceptanceMechanismsRequest, PoolCreate, indyVdr, -} from 'indy-vdr-test-shared' +} from '@hyperledger/indy-vdr-shared' import { IndyVdrError } from '../error' @@ -146,7 +146,9 @@ export class IndyVdrPool { acceptanceMechanismType: poolTaa.acceptanceMechanism, }) - request.setTransactionAuthorAgreementAcceptance({ acceptance }) + request.setTransactionAuthorAgreementAcceptance({ + acceptance: JSON.parse(acceptance), + }) } private async getTransactionAuthorAgreement(): Promise { @@ -172,8 +174,7 @@ export class IndyVdrPool { // If TAA is not null, we can be sure AcceptanceMechanisms is also not null const authorAgreement = taaData as Omit - // FIME: remove cast when https://github.com/hyperledger/indy-vdr/pull/142 is released - const acceptanceMechanisms = acceptanceMechanismResponse.result.data as unknown as AcceptanceMechanisms + const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms this.authorAgreement = { ...authorAgreement, acceptanceMechanisms, diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index 0ac5d9a1aa..b6a1a0f989 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -1,9 +1,9 @@ import type { IndyVdrPoolConfig } from './IndyVdrPool' import type { AgentContext } from '@aries-framework/core' -import type { GetNymResponse } from 'indy-vdr-test-shared' +import type { GetNymResponse } from '@hyperledger/indy-vdr-shared' import { Logger, InjectionSymbols, injectable, inject, CacheModuleConfig } from '@aries-framework/core' -import { GetNymRequest } from 'indy-vdr-test-shared' +import { GetNymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrError, IndyVdrNotFoundError, IndyVdrNotConfiguredError } from '../error' import { isSelfCertifiedDid, DID_INDY_REGEX } from '../utils/did' diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index 7ea99d8263..2ac21a7711 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -2,7 +2,7 @@ import type { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' import type { AgentContext, Key } from '@aries-framework/core' import { KeyType } from '@aries-framework/core' -import { AttribRequest, NymRequest } from 'indy-vdr-test-shared' +import { AttribRequest, NymRequest } from '@hyperledger/indy-vdr-shared' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index 5920344527..52bd467cd5 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -1,7 +1,7 @@ import type { Key } from '@aries-framework/core' import { IndyWallet, KeyType, SigningProviderRegistry, TypedArrayEncoder } from '@aries-framework/core' -import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from 'indy-vdr-test-shared' +import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from '@hyperledger/indy-vdr-shared' import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' diff --git a/yarn.lock b/yarn.lock index 0f10727077..945e21eff3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -858,6 +858,23 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== +"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.4": + version "0.1.0-dev.4" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.4.tgz#b5d2090b30c4a51e4e4f15a024054aada0d3550e" + integrity sha512-SwvcoOONhxD9LaX7vunNi1KFKmDb8wmutkBI+Hl6JMX3R+0QgpyQx5M3cfp+V34fBS8pqzKbq9lQmo+pDu3IWg== + dependencies: + "@hyperledger/indy-vdr-shared" "0.1.0-dev.4" + "@mapbox/node-pre-gyp" "^1.0.10" + ffi-napi "^4.0.3" + ref-array-di "^1.2.2" + ref-napi "^3.0.3" + ref-struct-di "^1.1.1" + +"@hyperledger/indy-vdr-shared@0.1.0-dev.4", "@hyperledger/indy-vdr-shared@^0.1.0-dev.4": + version "0.1.0-dev.4" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.4.tgz#ad9ff18ea285cf3c8ba0b4a5bff03c02f57898e4" + integrity sha512-M6AnLQNryEqcWiH8oNNI/ovkFOykFg7zlO4oM+1xMbHbNzAe6ShBYQDB189zTQAG4RUkuA8yiLHt90g/q6N8dg== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -6139,23 +6156,6 @@ indy-sdk@^1.16.0-dev-1636: nan "^2.11.1" node-gyp "^8.0.0" -indy-vdr-test-nodejs@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/indy-vdr-test-nodejs/-/indy-vdr-test-nodejs-0.1.3.tgz#97eaf38b1035bfabcd772a8399f23d766dfd493e" - integrity sha512-E6r86QGbswa+hBgMJKVWJycqvvmOgepFMDaAvuZQtxQK1Z2gghco6m/9EOAPYaJRs0MMEEhzUGhvtSpCzeZ6sg== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.10" - ffi-napi "^4.0.3" - indy-vdr-test-shared "0.1.3" - ref-array-di "^1.2.2" - ref-napi "^3.0.3" - ref-struct-di "^1.1.1" - -indy-vdr-test-shared@0.1.3, indy-vdr-test-shared@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/indy-vdr-test-shared/-/indy-vdr-test-shared-0.1.3.tgz#3b5ee9492ebc3367a027670aa9686c493de5929c" - integrity sha512-fdgV388zi3dglu49kqrV+i40w+18uJkv96Tk4nziLdP280SLnZKKnIRAiq11Hj8aHpnZmwMloyQCsIyQZDZk2g== - infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -11035,6 +11035,16 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" + integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" From f1e493799f3b71942b2263010f2661f7839d8324 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 5 Feb 2023 17:00:27 +0100 Subject: [PATCH 022/139] ci: forceExit and bail tests (#1266) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 2 +- packages/anoncreds/jest.config.ts | 2 +- packages/anoncreds/tests/setup.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 packages/anoncreds/tests/setup.ts diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 4134d8c5f1..6890536c12 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -108,7 +108,7 @@ jobs: run: yarn install - name: Run tests - run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage + run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage --forceExit --bail - uses: codecov/codecov-action@v1 if: always() diff --git a/packages/anoncreds/jest.config.ts b/packages/anoncreds/jest.config.ts index c7c5196637..55c67d70a6 100644 --- a/packages/anoncreds/jest.config.ts +++ b/packages/anoncreds/jest.config.ts @@ -8,7 +8,7 @@ const config: Config.InitialOptions = { ...base, name: packageJson.name, displayName: packageJson.name, - // setupFilesAfterEnv: ['./tests/setup.ts'], + setupFilesAfterEnv: ['./tests/setup.ts'], } export default config diff --git a/packages/anoncreds/tests/setup.ts b/packages/anoncreds/tests/setup.ts new file mode 100644 index 0000000000..719a473b6e --- /dev/null +++ b/packages/anoncreds/tests/setup.ts @@ -0,0 +1 @@ +jest.setTimeout(10000) From 3a4c5ecd940e49d4d192eef1d41f2aaedb34d85a Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 6 Feb 2023 21:49:12 +0100 Subject: [PATCH 023/139] feat(anoncreds): add anoncreds API (#1232) Signed-off-by: Timo Glastra --- packages/anoncreds/src/AnonCredsApi.ts | 428 ++++++++++++++++++ packages/anoncreds/src/AnonCredsApiOptions.ts | 4 + packages/anoncreds/src/AnonCredsModule.ts | 16 + .../src/__tests__/AnonCredsModule.test.ts | 14 +- .../src/error/AnonCredsStoreRecordError.ts | 7 + packages/anoncreds/src/error/index.ts | 1 + .../LegacyIndyCredentialFormatService.test.ts | 2 +- packages/anoncreds/src/index.ts | 4 + packages/anoncreds/src/models/exchange.ts | 2 +- packages/anoncreds/src/models/registry.ts | 2 +- ...nCredsCredentialDefinitionPrivateRecord.ts | 41 ++ ...dsCredentialDefinitionPrivateRepository.ts | 23 + .../AnonCredsCredentialDefinitionRecord.ts | 50 ++ ...AnonCredsCredentialDefinitionRepository.ts | 23 + .../AnonCredsKeyCorrectnessProofRecord.ts | 41 ++ .../AnonCredsKeyCorrectnessProofRepository.ts | 23 + .../repository/AnonCredsLinkSecretRecord.ts | 42 ++ .../AnonCredsLinkSecretRepository.ts | 31 ++ .../src/repository/AnonCredsSchemaRecord.ts | 50 ++ .../repository/AnonCredsSchemaRepository.ts | 23 + ...CredentialDefinitionRecordMetadataTypes.ts | 11 + .../anonCredsSchemaRecordMetadataTypes.ts | 11 + packages/anoncreds/src/repository/index.ts | 10 + .../src/services/AnonCredsHolderService.ts | 4 + .../services/AnonCredsHolderServiceOptions.ts | 16 +- .../src/services/AnonCredsIssuerService.ts | 5 +- .../services/AnonCredsIssuerServiceOptions.ts | 8 +- .../AnonCredsVerifierServiceOptions.ts | 6 +- .../services/registry/AnonCredsRegistry.ts | 7 +- .../registry/AnonCredsRegistryService.ts | 2 +- ...ions.ts => RevocationStatusListOptions.ts} | 8 +- .../AnonCredsRegistryService.test.ts | 2 +- .../anoncreds/src/services/registry/index.ts | 2 +- .../tests/InMemoryAnonCredsRegistry.ts | 83 +++- packages/anoncreds/tests/anoncreds.test.ts | 312 +++++++++++++ packages/anoncreds/tests/setup.ts | 2 +- packages/indy-sdk/src/IndySdkModule.ts | 18 + .../services/IndySdkAnonCredsRegistry.ts | 16 +- .../services/IndySdkHolderService.ts | 30 +- .../services/IndySdkIssuerService.ts | 19 +- .../services/IndySdkRevocationService.ts | 18 +- .../services/IndySdkVerifierService.ts | 11 +- .../utils/__tests__/transform.test.ts | 2 +- .../indy-sdk/src/anoncreds/utils/transform.ts | 20 +- 44 files changed, 1370 insertions(+), 80 deletions(-) create mode 100644 packages/anoncreds/src/AnonCredsApi.ts create mode 100644 packages/anoncreds/src/AnonCredsApiOptions.ts create mode 100644 packages/anoncreds/src/error/AnonCredsStoreRecordError.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRepository.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRepository.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsLinkSecretRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsLinkSecretRepository.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsSchemaRepository.ts create mode 100644 packages/anoncreds/src/repository/anonCredsCredentialDefinitionRecordMetadataTypes.ts create mode 100644 packages/anoncreds/src/repository/anonCredsSchemaRecordMetadataTypes.ts create mode 100644 packages/anoncreds/src/repository/index.ts rename packages/anoncreds/src/services/registry/{RevocationListOptions.ts => RevocationStatusListOptions.ts} (70%) create mode 100644 packages/anoncreds/tests/anoncreds.test.ts diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts new file mode 100644 index 0000000000..b52f4dbc0f --- /dev/null +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -0,0 +1,428 @@ +import type { AnonCredsCreateLinkSecretOptions } from './AnonCredsApiOptions' +import type { AnonCredsCredentialDefinition } from './models' +import type { + GetCredentialDefinitionReturn, + GetRevocationStatusListReturn, + GetRevocationRegistryDefinitionReturn, + GetSchemaReturn, + RegisterCredentialDefinitionReturn, + RegisterSchemaOptions, + RegisterSchemaReturn, +} from './services' +import type { Extensible } from './services/registry/base' + +import { AgentContext, inject, injectable } from '@aries-framework/core' + +import { AnonCredsModuleConfig } from './AnonCredsModuleConfig' +import { AnonCredsStoreRecordError } from './error' +import { + AnonCredsCredentialDefinitionPrivateRecord, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsKeyCorrectnessProofRecord, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsLinkSecretRecord, + AnonCredsLinkSecretRepository, +} from './repository' +import { AnonCredsCredentialDefinitionRecord } from './repository/AnonCredsCredentialDefinitionRecord' +import { AnonCredsCredentialDefinitionRepository } from './repository/AnonCredsCredentialDefinitionRepository' +import { AnonCredsSchemaRecord } from './repository/AnonCredsSchemaRecord' +import { AnonCredsSchemaRepository } from './repository/AnonCredsSchemaRepository' +import { AnonCredsCredentialDefinitionRecordMetadataKeys } from './repository/anonCredsCredentialDefinitionRecordMetadataTypes' +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsIssuerService, + AnonCredsHolderService, +} from './services' +import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' + +@injectable() +export class AnonCredsApi { + public config: AnonCredsModuleConfig + + private agentContext: AgentContext + private anonCredsRegistryService: AnonCredsRegistryService + private anonCredsSchemaRepository: AnonCredsSchemaRepository + private anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository + private anonCredsCredentialDefinitionPrivateRepository: AnonCredsCredentialDefinitionPrivateRepository + private anonCredsKeyCorrectnessProofRepository: AnonCredsKeyCorrectnessProofRepository + private anonCredsLinkSecretRepository: AnonCredsLinkSecretRepository + private anonCredsIssuerService: AnonCredsIssuerService + private anonCredsHolderService: AnonCredsHolderService + + public constructor( + agentContext: AgentContext, + anonCredsRegistryService: AnonCredsRegistryService, + config: AnonCredsModuleConfig, + @inject(AnonCredsIssuerServiceSymbol) anonCredsIssuerService: AnonCredsIssuerService, + @inject(AnonCredsHolderServiceSymbol) anonCredsHolderService: AnonCredsHolderService, + anonCredsSchemaRepository: AnonCredsSchemaRepository, + anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository, + anonCredsCredentialDefinitionPrivateRepository: AnonCredsCredentialDefinitionPrivateRepository, + anonCredsKeyCorrectnessProofRepository: AnonCredsKeyCorrectnessProofRepository, + anonCredsLinkSecretRepository: AnonCredsLinkSecretRepository + ) { + this.agentContext = agentContext + this.anonCredsRegistryService = anonCredsRegistryService + this.config = config + this.anonCredsIssuerService = anonCredsIssuerService + this.anonCredsHolderService = anonCredsHolderService + this.anonCredsSchemaRepository = anonCredsSchemaRepository + this.anonCredsCredentialDefinitionRepository = anonCredsCredentialDefinitionRepository + this.anonCredsCredentialDefinitionPrivateRepository = anonCredsCredentialDefinitionPrivateRepository + this.anonCredsKeyCorrectnessProofRepository = anonCredsKeyCorrectnessProofRepository + this.anonCredsLinkSecretRepository = anonCredsLinkSecretRepository + } + + /** + * Create a Link Secret, optionally indicating its ID and if it will be the default one + * If there is no default Link Secret, this will be set as default (even if setAsDefault is true). + * + */ + public async createLinkSecret(options?: AnonCredsCreateLinkSecretOptions) { + const { linkSecretId, linkSecretValue } = await this.anonCredsHolderService.createLinkSecret(this.agentContext, { + linkSecretId: options?.linkSecretId, + }) + + // In some cases we don't have the linkSecretValue. However we still want a record so we know which link secret ids are valid + const linkSecretRecord = new AnonCredsLinkSecretRecord({ linkSecretId, value: linkSecretValue }) + + // If it is the first link secret registered, set as default + const defaultLinkSecretRecord = await this.anonCredsLinkSecretRepository.findDefault(this.agentContext) + if (!defaultLinkSecretRecord || options?.setAsDefault) { + linkSecretRecord.setTag('isDefault', true) + } + + // Set the current default link secret as not default + if (defaultLinkSecretRecord && options?.setAsDefault) { + defaultLinkSecretRecord.setTag('isDefault', false) + await this.anonCredsLinkSecretRepository.update(this.agentContext, defaultLinkSecretRecord) + } + + await this.anonCredsLinkSecretRepository.save(this.agentContext, linkSecretRecord) + } + + /** + * Get a list of ids for the created link secrets + */ + public async getLinkSecretIds(): Promise { + const linkSecrets = await this.anonCredsLinkSecretRepository.getAll(this.agentContext) + + return linkSecrets.map((linkSecret) => linkSecret.linkSecretId) + } + + /** + * Retrieve a {@link AnonCredsSchema} from the registry associated + * with the {@link schemaId} + */ + public async getSchema(schemaId: string): Promise { + const failedReturnBase = { + resolutionMetadata: { + error: 'error', + message: `Unable to resolve schema ${schemaId}`, + }, + schemaId, + schemaMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(schemaId) + if (!registry) { + failedReturnBase.resolutionMetadata.error = 'unsupportedAnonCredsMethod' + failedReturnBase.resolutionMetadata.message = `Unable to resolve schema ${schemaId}: No registry found for identifier ${schemaId}` + return failedReturnBase + } + + try { + const result = await registry.getSchema(this.agentContext, schemaId) + return result + } catch (error) { + failedReturnBase.resolutionMetadata.message = `Unable to resolve schema ${schemaId}: ${error.message}` + return failedReturnBase + } + } + + public async registerSchema(options: RegisterSchemaOptions): Promise { + const failedReturnBase = { + schemaState: { + state: 'failed' as const, + schema: options.schema, + reason: `Error registering schema for issuerId ${options.schema.issuerId}`, + }, + registrationMetadata: {}, + schemaMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(options.schema.issuerId) + if (!registry) { + failedReturnBase.schemaState.reason = `Unable to register schema. No registry found for issuerId ${options.schema.issuerId}` + return failedReturnBase + } + + try { + const result = await registry.registerSchema(this.agentContext, options) + await this.storeSchemaRecord(result) + + return result + } catch (error) { + // Storage failed + if (error instanceof AnonCredsStoreRecordError) { + failedReturnBase.schemaState.reason = `Error storing schema record: ${error.message}` + return failedReturnBase + } + + // In theory registerSchema SHOULD NOT throw, but we can't know for sure + failedReturnBase.schemaState.reason = `Error registering schema: ${error.message}` + return failedReturnBase + } + } + + /** + * Retrieve a {@link AnonCredsCredentialDefinition} from the registry associated + * with the {@link credentialDefinitionId} + */ + public async getCredentialDefinition(credentialDefinitionId: string): Promise { + const failedReturnBase = { + resolutionMetadata: { + error: 'error', + message: `Unable to resolve credential definition ${credentialDefinitionId}`, + }, + credentialDefinitionId, + credentialDefinitionMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(credentialDefinitionId) + if (!registry) { + failedReturnBase.resolutionMetadata.error = 'unsupportedAnonCredsMethod' + failedReturnBase.resolutionMetadata.message = `Unable to resolve credential definition ${credentialDefinitionId}: No registry found for identifier ${credentialDefinitionId}` + return failedReturnBase + } + + try { + const result = await registry.getCredentialDefinition(this.agentContext, credentialDefinitionId) + return result + } catch (error) { + failedReturnBase.resolutionMetadata.message = `Unable to resolve credential definition ${credentialDefinitionId}: ${error.message}` + return failedReturnBase + } + } + + public async registerCredentialDefinition(options: { + credentialDefinition: Omit + // TODO: options should support supportsRevocation at some points + options: Extensible + }): Promise { + const failedReturnBase = { + credentialDefinitionState: { + state: 'failed' as const, + reason: `Error registering credential definition for issuerId ${options.credentialDefinition.issuerId}`, + }, + registrationMetadata: {}, + credentialDefinitionMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(options.credentialDefinition.issuerId) + if (!registry) { + failedReturnBase.credentialDefinitionState.reason = `Unable to register credential definition. No registry found for issuerId ${options.credentialDefinition.issuerId}` + return failedReturnBase + } + + const schemaRegistry = this.findRegistryForIdentifier(options.credentialDefinition.schemaId) + if (!schemaRegistry) { + failedReturnBase.credentialDefinitionState.reason = `Unable to register credential definition. No registry found for schemaId ${options.credentialDefinition.schemaId}` + return failedReturnBase + } + + try { + const schemaResult = await schemaRegistry.getSchema(this.agentContext, options.credentialDefinition.schemaId) + + if (!schemaResult.schema) { + failedReturnBase.credentialDefinitionState.reason = `error resolving schema with id ${options.credentialDefinition.schemaId}: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` + return failedReturnBase + } + + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = + await this.anonCredsIssuerService.createCredentialDefinition( + this.agentContext, + { + issuerId: options.credentialDefinition.issuerId, + schemaId: options.credentialDefinition.schemaId, + tag: options.credentialDefinition.tag, + supportRevocation: false, + schema: schemaResult.schema, + }, + // FIXME: Indy SDK requires the schema seq no to be passed in here. This is not ideal. + { + indyLedgerSchemaSeqNo: schemaResult.schemaMetadata.indyLedgerSeqNo, + } + ) + + const result = await registry.registerCredentialDefinition(this.agentContext, { + credentialDefinition, + options: options.options, + }) + + await this.storeCredentialDefinitionRecord(result, credentialDefinitionPrivate, keyCorrectnessProof) + + return result + } catch (error) { + // Storage failed + if (error instanceof AnonCredsStoreRecordError) { + failedReturnBase.credentialDefinitionState.reason = `Error storing credential definition records: ${error.message}` + return failedReturnBase + } + + // In theory registerCredentialDefinition SHOULD NOT throw, but we can't know for sure + failedReturnBase.credentialDefinitionState.reason = `Error registering credential definition: ${error.message}` + return failedReturnBase + } + } + + /** + * Retrieve a {@link AnonCredsRevocationRegistryDefinition} from the registry associated + * with the {@link revocationRegistryDefinitionId} + */ + public async getRevocationRegistryDefinition( + revocationRegistryDefinitionId: string + ): Promise { + const failedReturnBase = { + resolutionMetadata: { + error: 'error', + message: `Unable to resolve revocation registry ${revocationRegistryDefinitionId}`, + }, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(revocationRegistryDefinitionId) + if (!registry) { + failedReturnBase.resolutionMetadata.error = 'unsupportedAnonCredsMethod' + failedReturnBase.resolutionMetadata.message = `Unable to resolve revocation registry ${revocationRegistryDefinitionId}: No registry found for identifier ${revocationRegistryDefinitionId}` + return failedReturnBase + } + + try { + const result = await registry.getRevocationRegistryDefinition(this.agentContext, revocationRegistryDefinitionId) + return result + } catch (error) { + failedReturnBase.resolutionMetadata.message = `Unable to resolve revocation registry ${revocationRegistryDefinitionId}: ${error.message}` + return failedReturnBase + } + } + + /** + * Retrieve the {@link AnonCredsRevocationStatusList} for the given {@link timestamp} from the registry associated + * with the {@link revocationRegistryDefinitionId} + */ + public async getRevocationStatusList( + revocationRegistryDefinitionId: string, + timestamp: number + ): Promise { + const failedReturnBase = { + resolutionMetadata: { + error: 'error', + message: `Unable to resolve revocation status list for revocation registry ${revocationRegistryDefinitionId}`, + }, + revocationStatusListMetadata: {}, + } + + const registry = this.findRegistryForIdentifier(revocationRegistryDefinitionId) + if (!registry) { + failedReturnBase.resolutionMetadata.error = 'unsupportedAnonCredsMethod' + failedReturnBase.resolutionMetadata.message = `Unable to resolve revocation status list for revocation registry ${revocationRegistryDefinitionId}: No registry found for identifier ${revocationRegistryDefinitionId}` + return failedReturnBase + } + + try { + const result = await registry.getRevocationStatusList( + this.agentContext, + revocationRegistryDefinitionId, + timestamp + ) + return result + } catch (error) { + failedReturnBase.resolutionMetadata.message = `Unable to resolve revocation status list for revocation registry ${revocationRegistryDefinitionId}: ${error.message}` + return failedReturnBase + } + } + + private async storeCredentialDefinitionRecord( + result: RegisterCredentialDefinitionReturn, + credentialDefinitionPrivate?: Record, + keyCorrectnessProof?: Record + ): Promise { + try { + // If we have both the credentialDefinition and the credentialDefinitionId we will store a copy of the credential definition. We may need to handle an + // edge case in the future where we e.g. don't have the id yet, and it is registered through a different channel + if ( + result.credentialDefinitionState.credentialDefinition && + result.credentialDefinitionState.credentialDefinitionId + ) { + const credentialDefinitionRecord = new AnonCredsCredentialDefinitionRecord({ + credentialDefinitionId: result.credentialDefinitionState.credentialDefinitionId, + credentialDefinition: result.credentialDefinitionState.credentialDefinition, + }) + + // TODO: do we need to store this metadata? For indy, the registration metadata contains e.g. + // the indyLedgerSeqNo and the didIndyNamespace, but it can get quite big if complete transactions + // are stored in the metadata + credentialDefinitionRecord.metadata.set( + AnonCredsCredentialDefinitionRecordMetadataKeys.CredentialDefinitionMetadata, + result.credentialDefinitionMetadata + ) + credentialDefinitionRecord.metadata.set( + AnonCredsCredentialDefinitionRecordMetadataKeys.CredentialDefinitionRegistrationMetadata, + result.registrationMetadata + ) + + await this.anonCredsCredentialDefinitionRepository.save(this.agentContext, credentialDefinitionRecord) + + // Store Credential Definition private data (if provided by issuer service) + if (credentialDefinitionPrivate) { + const credentialDefinitionPrivateRecord = new AnonCredsCredentialDefinitionPrivateRecord({ + credentialDefinitionId: result.credentialDefinitionState.credentialDefinitionId, + value: credentialDefinitionPrivate, + }) + await this.anonCredsCredentialDefinitionPrivateRepository.save( + this.agentContext, + credentialDefinitionPrivateRecord + ) + } + + if (keyCorrectnessProof) { + const keyCorrectnessProofRecord = new AnonCredsKeyCorrectnessProofRecord({ + credentialDefinitionId: result.credentialDefinitionState.credentialDefinitionId, + value: keyCorrectnessProof, + }) + await this.anonCredsKeyCorrectnessProofRepository.save(this.agentContext, keyCorrectnessProofRecord) + } + } + } catch (error) { + throw new AnonCredsStoreRecordError(`Error storing credential definition records`, { cause: error }) + } + } + + private async storeSchemaRecord(result: RegisterSchemaReturn): Promise { + try { + // If we have both the schema and the schemaId we will store a copy of the schema. We may need to handle an + // edge case in the future where we e.g. don't have the id yet, and it is registered through a different channel + if (result.schemaState.schema && result.schemaState.schemaId) { + const schemaRecord = new AnonCredsSchemaRecord({ + schemaId: result.schemaState.schemaId, + schema: result.schemaState.schema, + }) + + await this.anonCredsSchemaRepository.save(this.agentContext, schemaRecord) + } + } catch (error) { + throw new AnonCredsStoreRecordError(`Error storing schema record`, { cause: error }) + } + } + + private findRegistryForIdentifier(identifier: string) { + try { + return this.anonCredsRegistryService.getRegistryForIdentifier(this.agentContext, identifier) + } catch { + return null + } + } +} diff --git a/packages/anoncreds/src/AnonCredsApiOptions.ts b/packages/anoncreds/src/AnonCredsApiOptions.ts new file mode 100644 index 0000000000..78a8e77728 --- /dev/null +++ b/packages/anoncreds/src/AnonCredsApiOptions.ts @@ -0,0 +1,4 @@ +export interface AnonCredsCreateLinkSecretOptions { + linkSecretId?: string + setAsDefault?: boolean +} diff --git a/packages/anoncreds/src/AnonCredsModule.ts b/packages/anoncreds/src/AnonCredsModule.ts index 0da6e242f7..3d6eff0b74 100644 --- a/packages/anoncreds/src/AnonCredsModule.ts +++ b/packages/anoncreds/src/AnonCredsModule.ts @@ -1,7 +1,15 @@ import type { AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' import type { DependencyManager, Module } from '@aries-framework/core' +import { AnonCredsApi } from './AnonCredsApi' import { AnonCredsModuleConfig } from './AnonCredsModuleConfig' +import { + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsLinkSecretRepository, +} from './repository' +import { AnonCredsCredentialDefinitionRepository } from './repository/AnonCredsCredentialDefinitionRepository' +import { AnonCredsSchemaRepository } from './repository/AnonCredsSchemaRepository' import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' /** @@ -9,6 +17,7 @@ import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryS */ export class AnonCredsModule implements Module { public readonly config: AnonCredsModuleConfig + public api = AnonCredsApi public constructor(config: AnonCredsModuleConfigOptions) { this.config = new AnonCredsModuleConfig(config) @@ -19,5 +28,12 @@ export class AnonCredsModule implements Module { dependencyManager.registerInstance(AnonCredsModuleConfig, this.config) dependencyManager.registerSingleton(AnonCredsRegistryService) + + // Repositories + dependencyManager.registerSingleton(AnonCredsSchemaRepository) + dependencyManager.registerSingleton(AnonCredsCredentialDefinitionRepository) + dependencyManager.registerSingleton(AnonCredsCredentialDefinitionPrivateRepository) + dependencyManager.registerSingleton(AnonCredsKeyCorrectnessProofRepository) + dependencyManager.registerSingleton(AnonCredsLinkSecretRepository) } } diff --git a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts index 90aa51ce66..f9c868c14c 100644 --- a/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts +++ b/packages/anoncreds/src/__tests__/AnonCredsModule.test.ts @@ -3,6 +3,13 @@ import type { DependencyManager } from '@aries-framework/core' import { AnonCredsModule } from '../AnonCredsModule' import { AnonCredsModuleConfig } from '../AnonCredsModuleConfig' +import { + AnonCredsSchemaRepository, + AnonCredsCredentialDefinitionRepository, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsLinkSecretRepository, +} from '../repository' import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' const dependencyManager = { @@ -19,8 +26,13 @@ describe('AnonCredsModule', () => { }) anonCredsModule.register(dependencyManager) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(6) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsRegistryService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsSchemaRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsCredentialDefinitionRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsCredentialDefinitionPrivateRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsKeyCorrectnessProofRepository) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsLinkSecretRepository) expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(AnonCredsModuleConfig, anonCredsModule.config) diff --git a/packages/anoncreds/src/error/AnonCredsStoreRecordError.ts b/packages/anoncreds/src/error/AnonCredsStoreRecordError.ts new file mode 100644 index 0000000000..11437d7b64 --- /dev/null +++ b/packages/anoncreds/src/error/AnonCredsStoreRecordError.ts @@ -0,0 +1,7 @@ +import { AnonCredsError } from './AnonCredsError' + +export class AnonCredsStoreRecordError extends AnonCredsError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/anoncreds/src/error/index.ts b/packages/anoncreds/src/error/index.ts index d9786950bf..6d25bc4dbb 100644 --- a/packages/anoncreds/src/error/index.ts +++ b/packages/anoncreds/src/error/index.ts @@ -1 +1,2 @@ export * from './AnonCredsError' +export * from './AnonCredsStoreRecordError' diff --git a/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts b/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts index 7e1e1909da..2449c81124 100644 --- a/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts +++ b/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts @@ -77,7 +77,7 @@ describe('LegacyIndyCredentialFormatService', () => { options: {}, }) - const credentialDefinition = await anonCredsIssuerService.createCredentialDefinition( + const { credentialDefinition } = await anonCredsIssuerService.createCredentialDefinition( agentContext, { issuerId: indyDid, diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 759e343c2c..9ef264f501 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -1,5 +1,9 @@ export * from './models' export * from './services' export * from './error' +export * from './repository' export { AnonCredsModule } from './AnonCredsModule' export { AnonCredsModuleConfig, AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' +export { AnonCredsApi } from './AnonCredsApi' +export { LegacyIndyCredentialFormatService } from './formats/LegacyIndyCredentialFormatService' +export { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index 40713b227d..b0e960afb8 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -1,4 +1,4 @@ -interface AnonCredsProofRequestRestriction { +export interface AnonCredsProofRequestRestriction { schema_id?: string schema_issuer_id?: string schema_name?: string diff --git a/packages/anoncreds/src/models/registry.ts b/packages/anoncreds/src/models/registry.ts index 1e5e6d7879..f4f3429ec2 100644 --- a/packages/anoncreds/src/models/registry.ts +++ b/packages/anoncreds/src/models/registry.ts @@ -32,7 +32,7 @@ export interface AnonCredsRevocationRegistryDefinition { tailsHash: string } -export interface AnonCredsRevocationList { +export interface AnonCredsRevocationStatusList { issuerId: string revRegId: string revocationList: number[] diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts new file mode 100644 index 0000000000..bc0c1c99ee --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRecord.ts @@ -0,0 +1,41 @@ +import type { TagsBase } from '@aries-framework/core' + +import { BaseRecord, utils } from '@aries-framework/core' + +export interface AnonCredsCredentialDefinitionPrivateRecordProps { + id?: string + credentialDefinitionId: string + value: Record +} + +export type DefaultAnonCredsCredentialDefinitionPrivateTags = { + credentialDefinitionId: string +} + +export class AnonCredsCredentialDefinitionPrivateRecord extends BaseRecord< + DefaultAnonCredsCredentialDefinitionPrivateTags, + TagsBase +> { + public static readonly type = 'AnonCredsCredentialDefinitionPrivateRecord' + public readonly type = AnonCredsCredentialDefinitionPrivateRecord.type + + public readonly credentialDefinitionId!: string + public readonly value!: Record // TODO: Define structure + + public constructor(props: AnonCredsCredentialDefinitionPrivateRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.credentialDefinitionId = props.credentialDefinitionId + this.value = props.value + } + } + + public getTags() { + return { + ...this._tags, + credentialDefinitionId: this.credentialDefinitionId, + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRepository.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRepository.ts new file mode 100644 index 0000000000..31c7737143 --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionPrivateRepository.ts @@ -0,0 +1,23 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' + +import { AnonCredsCredentialDefinitionPrivateRecord } from './AnonCredsCredentialDefinitionPrivateRecord' + +@injectable() +export class AnonCredsCredentialDefinitionPrivateRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsCredentialDefinitionPrivateRecord, storageService, eventEmitter) + } + + public async getByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.getSingleByQuery(agentContext, { credentialDefinitionId }) + } + + public async findByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.findSingleByQuery(agentContext, { credentialDefinitionId }) + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts new file mode 100644 index 0000000000..f9c7df43f7 --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts @@ -0,0 +1,50 @@ +import type { AnonCredsCredentialDefinitionRecordMetadata } from './anonCredsCredentialDefinitionRecordMetadataTypes' +import type { AnonCredsCredentialDefinition } from '../models' +import type { TagsBase } from '@aries-framework/core' + +import { BaseRecord, utils } from '@aries-framework/core' + +export interface AnonCredsCredentialDefinitionRecordProps { + id?: string + credentialDefinitionId: string + credentialDefinition: AnonCredsCredentialDefinition +} + +export type DefaultAnonCredsCredentialDefinitionTags = { + schemaId: string + credentialDefinitionId: string + issuerId: string + tag: string +} + +export class AnonCredsCredentialDefinitionRecord extends BaseRecord< + DefaultAnonCredsCredentialDefinitionTags, + TagsBase, + AnonCredsCredentialDefinitionRecordMetadata +> { + public static readonly type = 'AnonCredsCredentialDefinitionRecord' + public readonly type = AnonCredsCredentialDefinitionRecord.type + + public readonly credentialDefinitionId!: string + public readonly credentialDefinition!: AnonCredsCredentialDefinition + + public constructor(props: AnonCredsCredentialDefinitionRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.credentialDefinitionId = props.credentialDefinitionId + this.credentialDefinition = props.credentialDefinition + } + } + + public getTags() { + return { + ...this._tags, + credentialDefinitionId: this.credentialDefinitionId, + schemaId: this.credentialDefinition.schemaId, + issuerId: this.credentialDefinition.issuerId, + tag: this.credentialDefinition.tag, + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts new file mode 100644 index 0000000000..7677dd76b8 --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts @@ -0,0 +1,23 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' + +import { AnonCredsCredentialDefinitionRecord } from './AnonCredsCredentialDefinitionRecord' + +@injectable() +export class AnonCredsCredentialDefinitionRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsCredentialDefinitionRecord, storageService, eventEmitter) + } + + public async getByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.getSingleByQuery(agentContext, { credentialDefinitionId }) + } + + public async findByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.findSingleByQuery(agentContext, { credentialDefinitionId }) + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts new file mode 100644 index 0000000000..cac331bd6c --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRecord.ts @@ -0,0 +1,41 @@ +import type { TagsBase } from '@aries-framework/core' + +import { BaseRecord, utils } from '@aries-framework/core' + +export interface AnonCredsKeyCorrectnessProofRecordProps { + id?: string + credentialDefinitionId: string + value: Record +} + +export type DefaultAnonCredsKeyCorrectnessProofPrivateTags = { + credentialDefinitionId: string +} + +export class AnonCredsKeyCorrectnessProofRecord extends BaseRecord< + DefaultAnonCredsKeyCorrectnessProofPrivateTags, + TagsBase +> { + public static readonly type = 'AnonCredsKeyCorrectnessProofRecord' + public readonly type = AnonCredsKeyCorrectnessProofRecord.type + + public readonly credentialDefinitionId!: string + public readonly value!: Record // TODO: Define structure + + public constructor(props: AnonCredsKeyCorrectnessProofRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.credentialDefinitionId = props.credentialDefinitionId + this.value = props.value + } + } + + public getTags() { + return { + ...this._tags, + credentialDefinitionId: this.credentialDefinitionId, + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRepository.ts b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRepository.ts new file mode 100644 index 0000000000..959ba8b4a5 --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsKeyCorrectnessProofRepository.ts @@ -0,0 +1,23 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' + +import { AnonCredsKeyCorrectnessProofRecord } from './AnonCredsKeyCorrectnessProofRecord' + +@injectable() +export class AnonCredsKeyCorrectnessProofRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsKeyCorrectnessProofRecord, storageService, eventEmitter) + } + + public async getByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.getSingleByQuery(agentContext, { credentialDefinitionId }) + } + + public async findByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.findSingleByQuery(agentContext, { credentialDefinitionId }) + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsLinkSecretRecord.ts b/packages/anoncreds/src/repository/AnonCredsLinkSecretRecord.ts new file mode 100644 index 0000000000..ffb775526e --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsLinkSecretRecord.ts @@ -0,0 +1,42 @@ +import type { TagsBase } from '@aries-framework/core' + +import { BaseRecord, utils } from '@aries-framework/core' + +export interface AnonCredsLinkSecretRecordProps { + id?: string + linkSecretId: string + value?: string // If value is not provided, only reference to link secret is stored in regular storage +} + +export type DefaultAnonCredsLinkSecretTags = { + linkSecretId: string +} + +export type CustomAnonCredsLinkSecretTags = TagsBase & { + isDefault?: boolean +} + +export class AnonCredsLinkSecretRecord extends BaseRecord { + public static readonly type = 'AnonCredsLinkSecretRecord' + public readonly type = AnonCredsLinkSecretRecord.type + + public readonly linkSecretId!: string + public readonly value?: string + + public constructor(props: AnonCredsLinkSecretRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.linkSecretId = props.linkSecretId + this.value = props.value + } + } + + public getTags() { + return { + ...this._tags, + linkSecretId: this.linkSecretId, + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsLinkSecretRepository.ts b/packages/anoncreds/src/repository/AnonCredsLinkSecretRepository.ts new file mode 100644 index 0000000000..a4b69b08db --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsLinkSecretRepository.ts @@ -0,0 +1,31 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' + +import { AnonCredsLinkSecretRecord } from './AnonCredsLinkSecretRecord' + +@injectable() +export class AnonCredsLinkSecretRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsLinkSecretRecord, storageService, eventEmitter) + } + + public async getDefault(agentContext: AgentContext) { + return this.getSingleByQuery(agentContext, { isDefault: true }) + } + + public async findDefault(agentContext: AgentContext) { + return this.findSingleByQuery(agentContext, { isDefault: true }) + } + + public async getByLinkSecretId(agentContext: AgentContext, linkSecretId: string) { + return this.getSingleByQuery(agentContext, { linkSecretId }) + } + + public async findByLinkSecretId(agentContext: AgentContext, linkSecretId: string) { + return this.findSingleByQuery(agentContext, { linkSecretId }) + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts new file mode 100644 index 0000000000..13ad5d757c --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts @@ -0,0 +1,50 @@ +import type { AnonCredsSchemaRecordMetadata } from './anonCredsSchemaRecordMetadataTypes' +import type { AnonCredsSchema } from '../models' +import type { TagsBase } from '@aries-framework/core' + +import { BaseRecord, utils } from '@aries-framework/core' + +export interface AnonCredsSchemaRecordProps { + id?: string + schemaId: string + schema: AnonCredsSchema +} + +export type DefaultAnonCredsSchemaTags = { + schemaId: string + issuerId: string + schemaName: string + schemaVersion: string +} + +export class AnonCredsSchemaRecord extends BaseRecord< + DefaultAnonCredsSchemaTags, + TagsBase, + AnonCredsSchemaRecordMetadata +> { + public static readonly type = 'AnonCredsSchemaRecord' + public readonly type = AnonCredsSchemaRecord.type + + public readonly schemaId!: string + public readonly schema!: AnonCredsSchema + + public constructor(props: AnonCredsSchemaRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.schema = props.schema + this.schemaId = props.schemaId + } + } + + public getTags() { + return { + ...this._tags, + schemaId: this.schemaId, + issuerId: this.schema.issuerId, + schemaName: this.schema.name, + schemaVersion: this.schema.version, + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsSchemaRepository.ts b/packages/anoncreds/src/repository/AnonCredsSchemaRepository.ts new file mode 100644 index 0000000000..0d0ab84b9f --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsSchemaRepository.ts @@ -0,0 +1,23 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, inject, injectable } from '@aries-framework/core' + +import { AnonCredsSchemaRecord } from './AnonCredsSchemaRecord' + +@injectable() +export class AnonCredsSchemaRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsSchemaRecord, storageService, eventEmitter) + } + + public async getBySchemaId(agentContext: AgentContext, schemaId: string) { + return this.getSingleByQuery(agentContext, { schemaId: schemaId }) + } + + public async findBySchemaId(agentContext: AgentContext, schemaId: string) { + return await this.findSingleByQuery(agentContext, { schemaId: schemaId }) + } +} diff --git a/packages/anoncreds/src/repository/anonCredsCredentialDefinitionRecordMetadataTypes.ts b/packages/anoncreds/src/repository/anonCredsCredentialDefinitionRecordMetadataTypes.ts new file mode 100644 index 0000000000..05806802e4 --- /dev/null +++ b/packages/anoncreds/src/repository/anonCredsCredentialDefinitionRecordMetadataTypes.ts @@ -0,0 +1,11 @@ +import type { Extensible } from '../services/registry/base' + +export enum AnonCredsCredentialDefinitionRecordMetadataKeys { + CredentialDefinitionRegistrationMetadata = '_internal/anonCredsCredentialDefinitionRegistrationMetadata', + CredentialDefinitionMetadata = '_internal/anonCredsCredentialDefinitionMetadata', +} + +export type AnonCredsCredentialDefinitionRecordMetadata = { + [AnonCredsCredentialDefinitionRecordMetadataKeys.CredentialDefinitionRegistrationMetadata]: Extensible + [AnonCredsCredentialDefinitionRecordMetadataKeys.CredentialDefinitionMetadata]: Extensible +} diff --git a/packages/anoncreds/src/repository/anonCredsSchemaRecordMetadataTypes.ts b/packages/anoncreds/src/repository/anonCredsSchemaRecordMetadataTypes.ts new file mode 100644 index 0000000000..9880a50625 --- /dev/null +++ b/packages/anoncreds/src/repository/anonCredsSchemaRecordMetadataTypes.ts @@ -0,0 +1,11 @@ +import type { Extensible } from '../services/registry/base' + +export enum AnonCredsSchemaRecordMetadataKeys { + SchemaRegistrationMetadata = '_internal/anonCredsSchemaRegistrationMetadata', + SchemaMetadata = '_internal/anonCredsSchemaMetadata', +} + +export type AnonCredsSchemaRecordMetadata = { + [AnonCredsSchemaRecordMetadataKeys.SchemaRegistrationMetadata]: Extensible + [AnonCredsSchemaRecordMetadataKeys.SchemaMetadata]: Extensible +} diff --git a/packages/anoncreds/src/repository/index.ts b/packages/anoncreds/src/repository/index.ts new file mode 100644 index 0000000000..5e17e19941 --- /dev/null +++ b/packages/anoncreds/src/repository/index.ts @@ -0,0 +1,10 @@ +export * from './AnonCredsCredentialDefinitionRecord' +export * from './AnonCredsCredentialDefinitionRepository' +export * from './AnonCredsCredentialDefinitionPrivateRecord' +export * from './AnonCredsCredentialDefinitionPrivateRepository' +export * from './AnonCredsKeyCorrectnessProofRecord' +export * from './AnonCredsKeyCorrectnessProofRepository' +export * from './AnonCredsLinkSecretRecord' +export * from './AnonCredsLinkSecretRepository' +export * from './AnonCredsSchemaRecord' +export * from './AnonCredsSchemaRepository' diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts index a7c0dcb22e..85e51ce529 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderService.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -6,6 +6,8 @@ import type { StoreCredentialOptions, GetCredentialsForProofRequestOptions, GetCredentialsForProofRequestReturn, + CreateLinkSecretReturn, + CreateLinkSecretOptions, } from './AnonCredsHolderServiceOptions' import type { AnonCredsCredentialInfo } from '../models' import type { AnonCredsProof } from '../models/exchange' @@ -14,6 +16,8 @@ import type { AgentContext } from '@aries-framework/core' export const AnonCredsHolderServiceSymbol = Symbol('AnonCredsHolderService') export interface AnonCredsHolderService { + createLinkSecret(agentContext: AgentContext, options: CreateLinkSecretOptions): Promise + createProof(agentContext: AgentContext, options: CreateProofOptions): Promise storeCredential( agentContext: AgentContext, diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index 728482ff33..fcbc5e913c 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -12,7 +12,7 @@ import type { } from '../models/exchange' import type { AnonCredsCredentialDefinition, - AnonCredsRevocationList, + AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition, AnonCredsSchema, } from '../models/registry' @@ -36,8 +36,8 @@ export interface CreateProofOptions { // tails file MUST already be downloaded on a higher level and stored tailsFilePath: string definition: AnonCredsRevocationRegistryDefinition - revocationLists: { - [timestamp: string]: AnonCredsRevocationList + revocationStatusLists: { + [timestamp: string]: AnonCredsRevocationStatusList } } } @@ -81,9 +81,19 @@ export type GetCredentialsForProofRequestReturn = Array<{ export interface CreateCredentialRequestOptions { credentialOffer: AnonCredsCredentialOffer credentialDefinition: AnonCredsCredentialDefinition + linkSecretId?: string } export interface CreateCredentialRequestReturn { credentialRequest: AnonCredsCredentialRequest credentialRequestMetadata: AnonCredsCredentialRequestMetadata } + +export interface CreateLinkSecretOptions { + linkSecretId?: string +} + +export interface CreateLinkSecretReturn { + linkSecretId: string + linkSecretValue?: string +} diff --git a/packages/anoncreds/src/services/AnonCredsIssuerService.ts b/packages/anoncreds/src/services/AnonCredsIssuerService.ts index 41cb4ebf9f..3090b1759b 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerService.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerService.ts @@ -4,9 +4,10 @@ import type { CreateCredentialOfferOptions, CreateCredentialReturn, CreateCredentialOptions, + CreateCredentialDefinitionReturn, } from './AnonCredsIssuerServiceOptions' import type { AnonCredsCredentialOffer } from '../models/exchange' -import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../models/registry' +import type { AnonCredsSchema } from '../models/registry' import type { AgentContext } from '@aries-framework/core' export const AnonCredsIssuerServiceSymbol = Symbol('AnonCredsIssuerService') @@ -20,7 +21,7 @@ export interface AnonCredsIssuerService { agentContext: AgentContext, options: CreateCredentialDefinitionOptions, metadata?: Record - ): Promise + ): Promise createCredentialOffer( agentContext: AgentContext, diff --git a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts index 58d6cd9048..c7da246b9b 100644 --- a/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsIssuerServiceOptions.ts @@ -4,7 +4,7 @@ import type { AnonCredsCredentialRequest, AnonCredsCredentialValues, } from '../models/exchange' -import type { AnonCredsSchema } from '../models/registry' +import type { AnonCredsCredentialDefinition, AnonCredsSchema } from '../models/registry' export interface CreateSchemaOptions { issuerId: string @@ -39,3 +39,9 @@ export interface CreateCredentialReturn { credential: AnonCredsCredential credentialRevocationId?: string } + +export interface CreateCredentialDefinitionReturn { + credentialDefinition: AnonCredsCredentialDefinition + credentialDefinitionPrivate?: Record + keyCorrectnessProof?: Record +} diff --git a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts index f3ecb3b70c..85593764af 100644 --- a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts @@ -1,7 +1,7 @@ import type { AnonCredsProof, AnonCredsProofRequest } from '../models/exchange' import type { AnonCredsCredentialDefinition, - AnonCredsRevocationList, + AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition, AnonCredsSchema, } from '../models/registry' @@ -23,8 +23,8 @@ export interface VerifyProofOptions { // as a verifier. This is just following the data models from the AnonCreds spec, but for e.g. indy // this means we need to retrieve _ALL_ deltas from the ledger to verify a proof. While currently we // only need to fetch the registry. - revocationLists: { - [timestamp: number]: AnonCredsRevocationList + revocationStatusLists: { + [timestamp: number]: AnonCredsRevocationStatusList } } } diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts index e3061043dd..870eb90571 100644 --- a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts @@ -3,8 +3,8 @@ import type { RegisterCredentialDefinitionOptions, RegisterCredentialDefinitionReturn, } from './CredentialDefinitionOptions' -import type { GetRevocationListReturn } from './RevocationListOptions' import type { GetRevocationRegistryDefinitionReturn } from './RevocationRegistryDefinitionOptions' +import type { GetRevocationStatusListReturn } from './RevocationStatusListOptions' import type { GetSchemaReturn, RegisterSchemaOptions, RegisterSchemaReturn } from './SchemaOptions' import type { AgentContext } from '@aries-framework/core' @@ -37,12 +37,11 @@ export interface AnonCredsRegistry { // options: RegisterRevocationRegistryDefinitionOptions // ): Promise - // TODO: The name of this data model is still tbd. - getRevocationList( + getRevocationStatusList( agentContext: AgentContext, revocationRegistryId: string, timestamp: number - ): Promise + ): Promise // TODO: issuance of revocable credentials // registerRevocationList( diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts index a860d1e8f5..23c393bb38 100644 --- a/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistryService.ts @@ -20,7 +20,7 @@ export class AnonCredsRegistryService { const registry = registries.find((registry) => registry.supportedIdentifier.test(identifier)) if (!registry) { - throw new AnonCredsError(`No AnonCredsRegistry registered for identifier '${registry}'`) + throw new AnonCredsError(`No AnonCredsRegistry registered for identifier '${identifier}'`) } return registry diff --git a/packages/anoncreds/src/services/registry/RevocationListOptions.ts b/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts similarity index 70% rename from packages/anoncreds/src/services/registry/RevocationListOptions.ts rename to packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts index f3a07dc686..6396fe6df0 100644 --- a/packages/anoncreds/src/services/registry/RevocationListOptions.ts +++ b/packages/anoncreds/src/services/registry/RevocationStatusListOptions.ts @@ -1,10 +1,10 @@ import type { AnonCredsResolutionMetadata, Extensible } from './base' -import type { AnonCredsRevocationList } from '../../models/registry' +import type { AnonCredsRevocationStatusList } from '../../models/registry' -export interface GetRevocationListReturn { - revocationList?: AnonCredsRevocationList +export interface GetRevocationStatusListReturn { + revocationStatusList?: AnonCredsRevocationStatusList resolutionMetadata: AnonCredsResolutionMetadata - revocationListMetadata: Extensible + revocationStatusListMetadata: Extensible } // TODO: Support for issuance of revocable credentials diff --git a/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts index 553b9e626c..2cb39bc2e5 100644 --- a/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts +++ b/packages/anoncreds/src/services/registry/__tests__/AnonCredsRegistryService.test.ts @@ -32,7 +32,7 @@ describe('AnonCredsRegistryService', () => { expect(anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'b')).toEqual(registryTwo) }) - test('throws AnonCredsError if no registry is found for the given identifier', () => { + test('throws AnonCredsError if no registry is found for the given identifier', async () => { expect(() => anonCredsRegistryService.getRegistryForIdentifier(agentContext, 'c')).toThrow(AnonCredsError) }) }) diff --git a/packages/anoncreds/src/services/registry/index.ts b/packages/anoncreds/src/services/registry/index.ts index 5d36ce3dd9..fd154074fd 100644 --- a/packages/anoncreds/src/services/registry/index.ts +++ b/packages/anoncreds/src/services/registry/index.ts @@ -2,5 +2,5 @@ export * from './AnonCredsRegistry' export * from './CredentialDefinitionOptions' export * from './SchemaOptions' export * from './RevocationRegistryDefinitionOptions' -export * from './RevocationListOptions' +export * from './RevocationStatusListOptions' export { AnonCredsResolutionMetadata } from './base' diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index a1426fad46..18bd9cfaab 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -8,7 +8,9 @@ import type { RegisterCredentialDefinitionOptions, RegisterCredentialDefinitionReturn, GetRevocationRegistryDefinitionReturn, - GetRevocationListReturn, + GetRevocationStatusListReturn, + AnonCredsRevocationStatusList, + AnonCredsRevocationRegistryDefinition, AnonCredsSchema, AnonCredsCredentialDefinition, } from '../src' @@ -26,8 +28,27 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { // we want, but the indy-sdk is picky about the identifier format. public readonly supportedIdentifier = /^[a-zA-Z0-9]{21,22}/ - private schemas: Record = {} - private credentialDefinitions: Record = {} + private schemas: Record + private credentialDefinitions: Record + private revocationRegistryDefinitions: Record + private revocationStatusLists: Record> + + public constructor({ + existingSchemas = {}, + existingCredentialDefinitions = {}, + existingRevocationRegistryDefinitions = {}, + existingRevocationStatusLists = {}, + }: { + existingSchemas?: Record + existingCredentialDefinitions?: Record + existingRevocationRegistryDefinitions?: Record + existingRevocationStatusLists?: Record> + } = {}) { + this.schemas = existingSchemas + this.credentialDefinitions = existingCredentialDefinitions + this.revocationRegistryDefinitions = existingRevocationRegistryDefinitions + this.revocationStatusLists = existingRevocationStatusLists + } public async getSchema(agentContext: AgentContext, schemaId: string): Promise { const schema = this.schemas[schemaId] @@ -40,11 +61,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { message: `Schema with id ${schemaId} not found in memory registry`, }, schemaId, - schemaMetadata: { - // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. - // For this reason we return it in the metadata. - indyLedgerSeqNo, - }, + schemaMetadata: {}, } } @@ -52,7 +69,11 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { resolutionMetadata: {}, schema, schemaId, - schemaMetadata: {}, + schemaMetadata: { + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo, + }, } } @@ -125,19 +146,53 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { } } - public getRevocationRegistryDefinition( + public async getRevocationRegistryDefinition( agentContext: AgentContext, revocationRegistryDefinitionId: string ): Promise { - throw new Error('Method not implemented.') + const revocationRegistryDefinition = this.revocationRegistryDefinitions[revocationRegistryDefinitionId] + + if (!revocationRegistryDefinition) { + return { + resolutionMetadata: { + error: 'notFound', + message: `Revocation registry definition with id ${revocationRegistryDefinition} not found in memory registry`, + }, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } + } + + return { + resolutionMetadata: {}, + revocationRegistryDefinition, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } } - public getRevocationList( + public async getRevocationStatusList( agentContext: AgentContext, revocationRegistryId: string, timestamp: number - ): Promise { - throw new Error('Method not implemented.') + ): Promise { + const revocationStatusLists = this.revocationStatusLists[revocationRegistryId] + + if (!revocationStatusLists || !revocationStatusLists[timestamp]) { + return { + resolutionMetadata: { + error: 'notFound', + message: `Revocation status list for revocation registry with id ${revocationRegistryId} not found in memory registry`, + }, + revocationStatusListMetadata: {}, + } + } + + return { + resolutionMetadata: {}, + revocationStatusList: revocationStatusLists[timestamp], + revocationStatusListMetadata: {}, + } } } diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts new file mode 100644 index 0000000000..e7abd466c4 --- /dev/null +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -0,0 +1,312 @@ +import { Agent, KeyDerivationMethod } from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' + +import { IndySdkModule } from '../../indy-sdk/src/IndySdkModule' +import { AnonCredsCredentialDefinitionRepository, AnonCredsModule, AnonCredsSchemaRepository } from '../src' + +import { InMemoryAnonCredsRegistry } from './InMemoryAnonCredsRegistry' + +const existingSchemas = { + '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0': { + attrNames: ['one', 'two'], + issuerId: '7Cd2Yj9yEZNcmNoH54tq9i', + name: 'Test Schema', + version: '1.0.0', + }, +} + +const existingCredentialDefinitions = { + 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG': { + issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + tag: 'TAG', + schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + one: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + two: '60366631925664005237432731340682977203246802182440530784833565276111958129922833461368205267143124766208499918438803966972947830682551774196763124331578934778868938718942789067536194229546670608604626738087066151521062180022991840618459591148096543440942293686250499935227881144460486543061212259250663566176469333982946568767707989969471450673037590849807300874360022327312564559087769485266016496010132793446151658150957771177955095876947792797176338483943233433284791481746843006255371654617950568875773118157773566188096075078351362095061968279597354733768049622048871890495958175847017320945873812850638157518451', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, +} as const + +const existingRevocationRegistryDefinitions = { + 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG': { + credDefId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + maxCredNum: 100, + type: 'CL_ACCUM', + publicKeys: { + accumKey: { + z: 'ab81257c-be63-4051-9e21-c7d384412f64', + }, + }, + tag: 'TAG', + tailsHash: 'ab81257c-be63-4051-9e21-c7d384412f64', + tailsLocation: 'http://localhost:7200/tails', + }, +} as const + +const existingRevocationStatusLists = { + 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG': { + 10123: { + currentAccumulator: 'ab81257c-be63-4051-9e21-c7d384412f64', + issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + revocationList: [1, 0, 1], + revRegId: 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG', + timestamp: 10123, + }, + }, +} + +const agent = new Agent({ + config: { + label: '@aries-framework/anoncreds', + walletConfig: { + id: '@aries-framework/anoncreds', + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, + }, + }, + modules: { + indySdk: new IndySdkModule({ + indySdk: agentDependencies.indy, + }), + anoncreds: new AnonCredsModule({ + registries: [ + new InMemoryAnonCredsRegistry({ + existingSchemas, + existingCredentialDefinitions, + existingRevocationRegistryDefinitions, + existingRevocationStatusLists, + }), + ], + }), + }, + dependencies: agentDependencies, +}) + +describe('AnonCreds API', () => { + beforeEach(async () => { + await agent.initialize() + }) + + afterEach(async () => { + await agent.wallet.delete() + await agent.shutdown() + }) + + test('create and get link secret', async () => { + await agent.modules.anoncreds.createLinkSecret({ + linkSecretId: 'anoncreds-link-secret', + }) + + const linkSecretIds = await agent.modules.anoncreds.getLinkSecretIds() + + expect(linkSecretIds).toEqual(['anoncreds-link-secret']) + }) + + test('register a schema', async () => { + const schemaResult = await agent.modules.anoncreds.registerSchema({ + options: {}, + schema: { + attrNames: ['name', 'age'], + issuerId: '6xDN7v3AiGgusRp4bqZACZ', + name: 'Employee Credential', + version: '1.0.0', + }, + }) + + expect(schemaResult).toEqual({ + registrationMetadata: {}, + schemaMetadata: { indyLedgerSeqNo: 16908 }, + schemaState: { + state: 'finished', + schema: { + attrNames: ['name', 'age'], + issuerId: '6xDN7v3AiGgusRp4bqZACZ', + name: 'Employee Credential', + version: '1.0.0', + }, + schemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', + }, + }) + + // Check if record was created + const anonCredsSchemaRepository = agent.dependencyManager.resolve(AnonCredsSchemaRepository) + const schemaRecord = await anonCredsSchemaRepository.getBySchemaId( + agent.context, + '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0' + ) + + expect(schemaRecord).toMatchObject({ + schemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', + schema: { + attrNames: ['name', 'age'], + issuerId: '6xDN7v3AiGgusRp4bqZACZ', + name: 'Employee Credential', + version: '1.0.0', + }, + }) + + expect(schemaRecord.getTags()).toEqual({ + schemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', + issuerId: '6xDN7v3AiGgusRp4bqZACZ', + schemaName: 'Employee Credential', + schemaVersion: '1.0.0', + }) + }) + + test('resolve a schema', async () => { + const schemaResult = await agent.modules.anoncreds.getSchema('7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0') + + expect(schemaResult).toEqual({ + resolutionMetadata: {}, + schemaMetadata: { indyLedgerSeqNo: 75206 }, + schema: { + attrNames: ['one', 'two'], + issuerId: '7Cd2Yj9yEZNcmNoH54tq9i', + name: 'Test Schema', + version: '1.0.0', + }, + schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', + }) + }) + + test('register a credential definition', async () => { + // NOTE: the indy-sdk MUST have a did created, we can't just create a key + await agent.context.wallet.initPublicDid({ seed: '00000000000000000000000000000My1' }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const issuerId = agent.context.wallet.publicDid!.did + + const credentialDefinitionResult = await agent.modules.anoncreds.registerCredentialDefinition({ + credentialDefinition: { + issuerId, + schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', + tag: 'TAG', + }, + options: {}, + }) + + expect(credentialDefinitionResult).toEqual({ + registrationMetadata: {}, + credentialDefinitionMetadata: {}, + credentialDefinitionState: { + state: 'finished', + credentialDefinition: { + issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + tag: 'TAG', + schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', + type: 'CL', + value: { + primary: { + n: expect.any(String), + s: expect.any(String), + r: { + one: expect.any(String), + master_secret: expect.any(String), + two: expect.any(String), + }, + rctxt: expect.any(String), + z: expect.any(String), + }, + }, + }, + credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + }, + }) + + // Check if record was created + const anonCredsCredentialDefinitionRepository = agent.dependencyManager.resolve( + AnonCredsCredentialDefinitionRepository + ) + const credentialDefinitionRecord = await anonCredsCredentialDefinitionRepository.getByCredentialDefinitionId( + agent.context, + 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG' + ) + + expect(credentialDefinitionRecord).toMatchObject({ + credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + credentialDefinition: { + issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + tag: 'TAG', + schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', + type: 'CL', + value: { + primary: { + n: expect.any(String), + s: expect.any(String), + r: { + one: expect.any(String), + master_secret: expect.any(String), + two: expect.any(String), + }, + rctxt: expect.any(String), + z: expect.any(String), + }, + }, + }, + }) + + expect(credentialDefinitionRecord.getTags()).toEqual({ + credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', + issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + tag: 'TAG', + }) + }) + + test('resolve a credential definition', async () => { + const credentialDefinitionResult = await agent.modules.anoncreds.getCredentialDefinition( + 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG' + ) + + expect(credentialDefinitionResult).toEqual({ + resolutionMetadata: {}, + credentialDefinitionMetadata: {}, + credentialDefinition: existingCredentialDefinitions['VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG'], + credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + }) + }) + + test('resolve a revocation regsitry definition', async () => { + const revocationRegistryDefinition = await agent.modules.anoncreds.getRevocationRegistryDefinition( + 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG' + ) + + expect(revocationRegistryDefinition).toEqual({ + revocationRegistryDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG', + revocationRegistryDefinition: + existingRevocationRegistryDefinitions[ + 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG' + ], + resolutionMetadata: {}, + revocationRegistryDefinitionMetadata: {}, + }) + }) + + test('resolve a revocation status list', async () => { + const revocationStatusList = await agent.modules.anoncreds.getRevocationStatusList( + 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG', + 10123 + ) + + expect(revocationStatusList).toEqual({ + revocationStatusList: + existingRevocationStatusLists[ + 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG' + ][10123], + resolutionMetadata: {}, + revocationStatusListMetadata: {}, + }) + }) +}) diff --git a/packages/anoncreds/tests/setup.ts b/packages/anoncreds/tests/setup.ts index 719a473b6e..b60b932be5 100644 --- a/packages/anoncreds/tests/setup.ts +++ b/packages/anoncreds/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(10000) +jest.setTimeout(25000) diff --git a/packages/indy-sdk/src/IndySdkModule.ts b/packages/indy-sdk/src/IndySdkModule.ts index ea3baa5a9a..20574f3d46 100644 --- a/packages/indy-sdk/src/IndySdkModule.ts +++ b/packages/indy-sdk/src/IndySdkModule.ts @@ -1,8 +1,18 @@ import type { IndySdkModuleConfigOptions } from './IndySdkModuleConfig' import type { DependencyManager, Module } from '@aries-framework/core' +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, +} from '@aries-framework/anoncreds' +import { InjectionSymbols } from '@aries-framework/core' + import { IndySdkModuleConfig } from './IndySdkModuleConfig' +import { IndySdkHolderService, IndySdkIssuerService, IndySdkVerifierService } from './anoncreds' +import { IndySdkStorageService } from './storage' import { IndySdkSymbol } from './types' +import { IndySdkWallet } from './wallet' export class IndySdkModule implements Module { public readonly config: IndySdkModuleConfig @@ -13,5 +23,13 @@ export class IndySdkModule implements Module { public register(dependencyManager: DependencyManager) { dependencyManager.registerInstance(IndySdkSymbol, this.config.indySdk) + + // NOTE: for now we are registering the needed indy services. We may want to make this + // more explicit and require the user to register the services they need on the specific modules. + dependencyManager.registerSingleton(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) + dependencyManager.registerSingleton(AnonCredsIssuerServiceSymbol, IndySdkIssuerService) + dependencyManager.registerSingleton(AnonCredsHolderServiceSymbol, IndySdkHolderService) + dependencyManager.registerSingleton(AnonCredsVerifierServiceSymbol, IndySdkVerifierService) } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index ffe975b7e1..7ddb4a5db5 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -2,7 +2,7 @@ import type { IndySdk } from '../../types' import type { AnonCredsRegistry, GetCredentialDefinitionReturn, - GetRevocationListReturn, + GetRevocationStatusListReturn, GetRevocationRegistryDefinitionReturn, GetSchemaReturn, RegisterCredentialDefinitionOptions, @@ -25,7 +25,7 @@ import { indySdkAnonCredsRegistryIdentifierRegex, } from '../utils/identifiers' import { - anonCredsRevocationListFromIndySdk, + anonCredsRevocationStatusListFromIndySdk, anonCredsRevocationRegistryDefinitionFromIndySdk, } from '../utils/transform' @@ -417,11 +417,11 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } } - public async getRevocationList( + public async getRevocationStatusList( agentContext: AgentContext, revocationRegistryId: string, timestamp: number - ): Promise { + ): Promise { try { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) @@ -470,7 +470,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { resolutionMetadata: { error: `error resolving revocation registry definition with id ${revocationRegistryId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, }, - revocationListMetadata: { + revocationStatusListMetadata: { didIndyNamespace: pool.didIndyNamespace, }, } @@ -480,14 +480,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { return { resolutionMetadata: {}, - revocationList: anonCredsRevocationListFromIndySdk( + revocationStatusList: anonCredsRevocationStatusListFromIndySdk( revocationRegistryId, revocationRegistryDefinition, revocationRegistryDelta, deltaTimestamp, isIssuanceByDefault ), - revocationListMetadata: { + revocationStatusListMetadata: { didIndyNamespace: pool.didIndyNamespace, }, } @@ -505,7 +505,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { error: 'notFound', message: `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation: ${error.message}`, }, - revocationListMetadata: {}, + revocationStatusListMetadata: {}, } } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index e472d1c1c4..2e6e63ccc0 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -11,6 +11,8 @@ import type { GetCredentialsForProofRequestReturn, AnonCredsRequestedCredentials, AnonCredsCredentialRequestMetadata, + CreateLinkSecretOptions, + CreateLinkSecretReturn, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { @@ -23,7 +25,7 @@ import type { IndyProofRequest, } from 'indy-sdk' -import { inject } from '@aries-framework/core' +import { injectable, inject, utils } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' @@ -38,6 +40,7 @@ import { import { IndySdkRevocationService } from './IndySdkRevocationService' +@injectable() export class IndySdkHolderService implements AnonCredsHolderService { private indySdk: IndySdk private indyRevocationService: IndySdkRevocationService @@ -47,6 +50,31 @@ export class IndySdkHolderService implements AnonCredsHolderService { this.indyRevocationService = indyRevocationService } + public async createLinkSecret( + agentContext: AgentContext, + options: CreateLinkSecretOptions + ): Promise { + assertIndySdkWallet(agentContext.wallet) + + const linkSecretId = options.linkSecretId ?? utils.uuid() + + try { + await this.indySdk.proverCreateMasterSecret(agentContext.wallet.handle, linkSecretId) + + // We don't have the value for the link secret when using the indy-sdk so we can't return it. + return { + linkSecretId, + } + } catch (error) { + agentContext.config.logger.error(`Error creating link secret`, { + error, + linkSecretId, + }) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } + public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise { const { credentialDefinitions, proofRequest, requestedCredentials, schemas } = options diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index 96e9ef266a..ba6c2a1780 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -8,11 +8,11 @@ import type { CreateSchemaOptions, AnonCredsCredentialOffer, AnonCredsSchema, - AnonCredsCredentialDefinition, + CreateCredentialDefinitionReturn, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' -import { AriesFrameworkError, inject } from '@aries-framework/core' +import { injectable, AriesFrameworkError, inject } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' @@ -21,6 +21,7 @@ import { generateLegacyProverDidLikeString } from '../utils/proverDid' import { createTailsReader } from '../utils/tails' import { indySdkSchemaFromAnonCreds } from '../utils/transform' +@injectable() export class IndySdkIssuerService implements AnonCredsIssuerService { private indySdk: IndySdk @@ -50,7 +51,7 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { agentContext: AgentContext, options: CreateCredentialDefinitionOptions, metadata?: CreateCredentialDefinitionMetadata - ): Promise { + ): Promise { const { tag, supportRevocation, schema, issuerId, schemaId } = options if (!metadata) @@ -70,11 +71,13 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { ) return { - issuerId, - tag: credentialDefinition.tag, - schemaId, - type: 'CL', - value: credentialDefinition.value, + credentialDefinition: { + issuerId, + tag: credentialDefinition.tag, + schemaId, + type: 'CL', + value: credentialDefinition.value, + }, } } catch (error) { throw isIndyError(error) ? new IndySdkError(error) : error diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts index 4f7eb6ef42..30f78bcbff 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts @@ -1,6 +1,6 @@ import type { AnonCredsRevocationRegistryDefinition, - AnonCredsRevocationList, + AnonCredsRevocationStatusList, AnonCredsProofRequest, AnonCredsRequestedCredentials, AnonCredsCredentialInfo, @@ -50,8 +50,8 @@ export class IndySdkRevocationService { // Tails is already downloaded tailsFilePath: string definition: AnonCredsRevocationRegistryDefinition - revocationLists: { - [timestamp: string]: AnonCredsRevocationList + revocationStatusLists: { + [timestamp: string]: AnonCredsRevocationStatusList } } } @@ -106,18 +106,18 @@ export class IndySdkRevocationService { this.assertRevocationInterval(requestRevocationInterval) - const { definition, revocationLists, tailsFilePath } = revocationRegistries[revocationRegistryId] - // NOTE: we assume that the revocationLists have been added based on timestamps of the `to` query. On a higher level it means we'll find the - // most accurate revocation list for a given timestamp. It doesn't have to be that the revocationList is from the `to` timestamp however. - const revocationList = revocationLists[requestRevocationInterval.to] + const { definition, revocationStatusLists, tailsFilePath } = revocationRegistries[revocationRegistryId] + // NOTE: we assume that the revocationStatusLists have been added based on timestamps of the `to` query. On a higher level it means we'll find the + // most accurate revocation list for a given timestamp. It doesn't have to be that the revocationStatusList is from the `to` timestamp however. + const revocationStatusList = revocationStatusLists[requestRevocationInterval.to] const tails = await createTailsReader(agentContext, tailsFilePath) const revocationState = await this.indySdk.createRevocationState( tails, indySdkRevocationRegistryDefinitionFromAnonCreds(revocationRegistryId, definition), - indySdkRevocationDeltaFromAnonCreds(revocationList), - revocationList.timestamp, + indySdkRevocationDeltaFromAnonCreds(revocationStatusList), + revocationStatusList.timestamp, credentialRevocationId ) const timestamp = revocationState.timestamp diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts index d07a4ef1ef..3e76fc6bc9 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -1,7 +1,7 @@ import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs, IndyProofRequest } from 'indy-sdk' -import { inject } from '@aries-framework/core' +import { inject, injectable } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' @@ -13,6 +13,7 @@ import { indySdkSchemaFromAnonCreds, } from '../utils/transform' +@injectable() export class IndySdkVerifierService implements AnonCredsVerifierService { private indySdk: IndySdk @@ -53,7 +54,7 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { const indyRevocationRegistries: RevRegs = {} for (const revocationRegistryDefinitionId in options.revocationStates) { - const { definition, revocationLists } = options.revocationStates[revocationRegistryDefinitionId] + const { definition, revocationStatusLists } = options.revocationStates[revocationRegistryDefinitionId] indyRevocationDefinitions[revocationRegistryDefinitionId] = indySdkRevocationRegistryDefinitionFromAnonCreds( revocationRegistryDefinitionId, definition @@ -64,10 +65,10 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { // Also transform the revocation lists for the specified timestamps into the revocation registry // format Indy expects - for (const timestamp in revocationLists) { - const revocationList = revocationLists[timestamp] + for (const timestamp in revocationStatusLists) { + const revocationStatusList = revocationStatusLists[timestamp] indyRevocationRegistries[revocationRegistryDefinitionId][timestamp] = - indySdkRevocationRegistryFromAnonCreds(revocationList) + indySdkRevocationRegistryFromAnonCreds(revocationStatusList) } } diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts index 20b16fa0ff..7930bfb2fb 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts @@ -108,7 +108,7 @@ describe('transform', () => { test.todo( 'indySdkRevocationRegistryDefinitionFromAnonCreds should return a valid indy sdk revocation registry definition' ) - test.todo('anonCredsRevocationListFromIndySdk should return a valid anoncreds revocation list') + test.todo('anonCredsRevocationStatusListFromIndySdk should return a valid anoncreds revocation list') test.todo('indySdkRevocationRegistryFromAnonCreds should return a valid indy sdk revocation registry') test.todo('indySdkRevocationDeltaFromAnonCreds should return a valid indy sdk revocation delta') }) diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index a5ad8afd60..6a91928f70 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -1,6 +1,6 @@ import type { AnonCredsCredentialDefinition, - AnonCredsRevocationList, + AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition, AnonCredsSchema, } from '@aries-framework/anoncreds' @@ -92,13 +92,13 @@ export function indySdkRevocationRegistryDefinitionFromAnonCreds( } } -export function anonCredsRevocationListFromIndySdk( +export function anonCredsRevocationStatusListFromIndySdk( revocationRegistryDefinitionId: string, revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition, delta: RevocRegDelta, timestamp: number, isIssuanceByDefault: boolean -): AnonCredsRevocationList { +): AnonCredsRevocationStatusList { // 0 means unrevoked, 1 means revoked const defaultState = isIssuanceByDefault ? 0 : 1 @@ -124,25 +124,27 @@ export function anonCredsRevocationListFromIndySdk( } } -export function indySdkRevocationRegistryFromAnonCreds(revocationList: AnonCredsRevocationList): RevocReg { +export function indySdkRevocationRegistryFromAnonCreds(revocationStatusList: AnonCredsRevocationStatusList): RevocReg { return { ver: '1.0', value: { - accum: revocationList.currentAccumulator, + accum: revocationStatusList.currentAccumulator, }, } } -export function indySdkRevocationDeltaFromAnonCreds(revocationList: AnonCredsRevocationList): RevocRegDelta { - // Get all indices from the revocationList that are revoked (so have value '1') - const revokedIndices = revocationList.revocationList.reduce( +export function indySdkRevocationDeltaFromAnonCreds( + revocationStatusList: AnonCredsRevocationStatusList +): RevocRegDelta { + // Get all indices from the revocationStatusList that are revoked (so have value '1') + const revokedIndices = revocationStatusList.revocationList.reduce( (revoked, current, index) => (current === 1 ? [...revoked, index] : revoked), [] ) return { value: { - accum: revocationList.currentAccumulator, + accum: revocationStatusList.currentAccumulator, issued: [], revoked: revokedIndices, // NOTE: I don't think this is used? From 7f65ba999ad1f49065d24966a1d7f3b82264ea55 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Mon, 6 Feb 2023 22:27:03 +0100 Subject: [PATCH 024/139] feat: optional routing for legacy connectionless invitation (#1271) Signed-off-by: Jim Ezesinachi --- packages/core/src/modules/oob/OutOfBandApi.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index aee58655da..5f1e0c00b7 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -247,9 +247,10 @@ export class OutOfBandApi { recordId: string message: Message domain: string + routing?: Routing }): Promise<{ message: Message; invitationUrl: string }> { // Create keys (and optionally register them at the mediator) - const routing = await this.routingService.getRouting(this.agentContext) + const routing = config.routing ?? (await this.routingService.getRouting(this.agentContext)) // Set the service on the message config.message.service = new ServiceDecorator({ From 3d86e78a4df87869aa5df4e28b79cd91787b61fb Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Tue, 7 Feb 2023 00:09:24 +0100 Subject: [PATCH 025/139] feat(openid4vc-client): pre-authorized (#1243) This PR adds support for the `pre-authorized` OpenID for Verifiable Credentials issuance flow to the new `openid4vc-client` module. Here are some highlights of the work: - Allows the user to execute the entire `pre-authorized` flow by calling a single method. - Adds a happy-flow test - HTTP(S) requests and responses are mocked using a network mocking library called [nock](https://github.com/nock/nock) - Because the JSON-LD credential that is received is expanded by the `W3cCredentialService`, I've added a few new contexts to our test document loader. - Not-so-happy-flow tests will be added later on. If you have any suggestions for edge cases that deserve testing, feel free to drop a comment. - Modifies the `JwsService` - The `JwsService` was geared towards a very specific use case. I've generalized its API so it's usable for a wider range of applications. - All pre-existing tests and calls to the `JwsService` have been updated. It's worth noting that I have had to add some `@ts-ignore` statements here and there to get around some incomplete types in the `OpenID4VCI-Client` library we're using. Once these issues have been resolved in the client library, they will be removed. **Work funded by the government of Ontario** --------- Signed-off-by: Karim Stekelenburg Co-authored-by: Timo Glastra --- packages/core/src/crypto/JwkTypes.ts | 6 + packages/core/src/crypto/JwsService.ts | 111 +++-- packages/core/src/crypto/Key.ts | 23 +- .../src/crypto/__tests__/JwsService.test.ts | 30 +- packages/core/src/crypto/index.ts | 6 + packages/core/src/crypto/jwtUtils.ts | 13 + .../connections/DidExchangeProtocol.ts | 12 +- packages/core/src/modules/dids/DidsApi.ts | 4 +- .../modules/dids/repository/DidRepository.ts | 3 +- .../src/modules/vc/W3cCredentialService.ts | 11 +- .../contexts/mattr_vc_extension_v1.ts | 17 + .../vc/__tests__/contexts/purl_ob_v3po.ts | 438 ++++++++++++++++++ .../contexts/vc_revocation_list_2020.ts | 37 ++ .../vc/__tests__/dids/did_web_launchpad.ts | 24 + .../modules/vc/__tests__/documentLoader.ts | 9 + .../vc/models/credential/W3cCredential.ts | 4 +- .../vc/repository/W3cCredentialRecord.ts | 6 +- packages/core/src/modules/vc/validators.ts | 7 +- packages/openid4vc-client/README.md | 132 +++++- packages/openid4vc-client/package.json | 8 +- .../src/OpenId4VcClientApi.ts | 18 + .../src/OpenId4VcClientApiOptions.ts | 0 .../src/OpenId4VcClientService.ts | 230 ++++++++- packages/openid4vc-client/tests/fixtures.ts | 134 ++++++ .../tests/openid4vc-client.e2e.test.ts | 110 +++++ packages/openid4vc-client/tests/setup.ts | 2 - packages/openid4vc-client/tsconfig.build.json | 3 +- packages/openid4vc-client/tsconfig.json | 3 +- yarn.lock | 23 +- 29 files changed, 1352 insertions(+), 72 deletions(-) create mode 100644 packages/core/src/crypto/JwkTypes.ts create mode 100644 packages/core/src/crypto/jwtUtils.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/mattr_vc_extension_v1.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/purl_ob_v3po.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/vc_revocation_list_2020.ts create mode 100644 packages/core/src/modules/vc/__tests__/dids/did_web_launchpad.ts delete mode 100644 packages/openid4vc-client/src/OpenId4VcClientApiOptions.ts create mode 100644 packages/openid4vc-client/tests/fixtures.ts create mode 100644 packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts diff --git a/packages/core/src/crypto/JwkTypes.ts b/packages/core/src/crypto/JwkTypes.ts new file mode 100644 index 0000000000..144e771f16 --- /dev/null +++ b/packages/core/src/crypto/JwkTypes.ts @@ -0,0 +1,6 @@ +export interface Jwk { + kty: 'EC' | 'OKP' + crv: 'Ed25519' | 'X25519' | 'P-256' | 'P-384' | 'secp256k1' + x: string + y?: string +} diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index ffad03c128..e81be473b8 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -1,3 +1,4 @@ +import type { Jwk } from './JwkTypes' import type { Jws, JwsGeneralFormat } from './JwsTypes' import type { AgentContext } from '../agent' import type { Buffer } from '../utils' @@ -17,25 +18,63 @@ const JWS_ALG = 'EdDSA' @injectable() export class JwsService { - public async createJws( - agentContext: AgentContext, - { payload, verkey, header }: CreateJwsOptions - ): Promise { - const base64Payload = TypedArrayEncoder.toBase64URL(payload) - const base64Protected = JsonEncoder.toBase64URL(this.buildProtected(verkey)) - const key = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) + public static supportedKeyTypes = [KeyType.Ed25519] + + private async createJwsBase(agentContext: AgentContext, options: CreateJwsBaseOptions) { + if (!JwsService.supportedKeyTypes.includes(options.key.keyType)) { + throw new AriesFrameworkError( + `Only ${JwsService.supportedKeyTypes.join(',')} key type(s) supported for creating JWS` + ) + } + const base64Payload = TypedArrayEncoder.toBase64URL(options.payload) + const base64UrlProtectedHeader = JsonEncoder.toBase64URL(this.buildProtected(options.protectedHeaderOptions)) const signature = TypedArrayEncoder.toBase64URL( - await agentContext.wallet.sign({ data: TypedArrayEncoder.fromString(`${base64Protected}.${base64Payload}`), key }) + await agentContext.wallet.sign({ + data: TypedArrayEncoder.fromString(`${base64UrlProtectedHeader}.${base64Payload}`), + key: options.key, + }) ) return { - protected: base64Protected, + base64Payload, + base64UrlProtectedHeader, + signature, + } + } + + public async createJws( + agentContext: AgentContext, + { payload, key, header, protectedHeaderOptions }: CreateJwsOptions + ): Promise { + const { base64UrlProtectedHeader, signature } = await this.createJwsBase(agentContext, { + payload, + key, + protectedHeaderOptions, + }) + + return { + protected: base64UrlProtectedHeader, signature, header, } } + /** + * @see {@link https://www.rfc-editor.org/rfc/rfc7515#section-3.1} + * */ + public async createJwsCompact( + agentContext: AgentContext, + { payload, key, protectedHeaderOptions }: CreateCompactJwsOptions + ): Promise { + const { base64Payload, base64UrlProtectedHeader, signature } = await this.createJwsBase(agentContext, { + payload, + key, + protectedHeaderOptions, + }) + return `${base64UrlProtectedHeader}.${base64Payload}.${signature}` + } + /** * Verify a JWS */ @@ -47,7 +86,7 @@ export class JwsService { throw new AriesFrameworkError('Unable to verify JWS: No entries in JWS signatures array.') } - const signerVerkeys = [] + const signerKeys: Key[] = [] for (const jws of signatures) { const protectedJson = JsonEncoder.fromBase64(jws.protected) @@ -62,9 +101,9 @@ export class JwsService { const data = TypedArrayEncoder.fromString(`${jws.protected}.${base64Payload}`) const signature = TypedArrayEncoder.fromBase64(jws.signature) - const verkey = TypedArrayEncoder.toBase58(TypedArrayEncoder.fromBase64(protectedJson?.jwk?.x)) - const key = Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) - signerVerkeys.push(verkey) + const publicKey = TypedArrayEncoder.fromBase64(protectedJson?.jwk?.x) + const key = Key.fromPublicKey(publicKey, KeyType.Ed25519) + signerKeys.push(key) try { const isValid = await agentContext.wallet.verify({ key, data, signature }) @@ -72,7 +111,7 @@ export class JwsService { if (!isValid) { return { isValid: false, - signerVerkeys: [], + signerKeys: [], } } } catch (error) { @@ -81,7 +120,7 @@ export class JwsService { if (error instanceof WalletError) { return { isValid: false, - signerVerkeys: [], + signerKeys: [], } } @@ -89,31 +128,36 @@ export class JwsService { } } - return { isValid: true, signerVerkeys } + return { isValid: true, signerKeys: signerKeys } } - /** - * @todo This currently only work with a single alg, key type and curve - * This needs to be extended with other formats in the future - */ - private buildProtected(verkey: string) { + private buildProtected(options: ProtectedHeaderOptions) { + if (!options.jwk && !options.kid) { + throw new AriesFrameworkError('Both JWK and kid are undefined. Please provide one or the other.') + } + if (options.jwk && options.kid) { + throw new AriesFrameworkError('Both JWK and kid are provided. Please only provide one of the two.') + } + return { - alg: 'EdDSA', - jwk: { - kty: 'OKP', - crv: 'Ed25519', - x: TypedArrayEncoder.toBase64URL(TypedArrayEncoder.fromBase58(verkey)), - }, + alg: options.alg, + jwk: options.jwk, + kid: options.kid, } } } export interface CreateJwsOptions { - verkey: string + key: Key payload: Buffer header: Record + protectedHeaderOptions: ProtectedHeaderOptions } +type CreateJwsBaseOptions = Omit + +type CreateCompactJwsOptions = Omit + export interface VerifyJwsOptions { jws: Jws payload: Buffer @@ -121,5 +165,14 @@ export interface VerifyJwsOptions { export interface VerifyJwsResult { isValid: boolean - signerVerkeys: string[] + signerKeys: Key[] +} + +export type kid = string + +export interface ProtectedHeaderOptions { + alg: string + jwk?: Jwk + kid?: kid + [key: string]: any } diff --git a/packages/core/src/crypto/Key.ts b/packages/core/src/crypto/Key.ts index c5d03507f0..47576e3ffa 100644 --- a/packages/core/src/crypto/Key.ts +++ b/packages/core/src/crypto/Key.ts @@ -1,7 +1,9 @@ -import type { KeyType } from './KeyType' +import type { Jwk } from './JwkTypes' +import { AriesFrameworkError } from '../error' import { Buffer, MultiBaseEncoder, TypedArrayEncoder, VarintEncoder } from '../utils' +import { KeyType } from './KeyType' import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeytype } from './multiCodecKey' export class Key { @@ -50,4 +52,23 @@ export class Key { public get publicKeyBase58() { return TypedArrayEncoder.toBase58(this.publicKey) } + + public toJwk(): Jwk { + if (this.keyType !== KeyType.Ed25519) { + throw new AriesFrameworkError(`JWK creation is only supported for Ed25519 key types. Received ${this.keyType}`) + } + + return { + kty: 'OKP', + crv: 'Ed25519', + x: TypedArrayEncoder.toBase64URL(this.publicKey), + } + } + + public static fromJwk(jwk: Jwk) { + if (jwk.crv !== 'Ed25519') { + throw new AriesFrameworkError('Only JWKs with Ed25519 key type is supported.') + } + return Key.fromPublicKeyBase58(TypedArrayEncoder.toBase58(TypedArrayEncoder.fromBase64(jwk.x)), KeyType.Ed25519) + } } diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index b0311e396d..6b5d5fb258 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -1,5 +1,5 @@ import type { AgentContext } from '../../agent' -import type { Wallet } from '@aries-framework/core' +import type { Key, Wallet } from '@aries-framework/core' import { getAgentConfig, getAgentContext } from '../../../tests/helpers' import { DidKey } from '../../modules/dids' @@ -16,7 +16,8 @@ describe('JwsService', () => { let wallet: Wallet let agentContext: AgentContext let jwsService: JwsService - + let didJwsz6MkfKey: Key + let didJwsz6MkvKey: Key beforeAll(async () => { const config = getAgentConfig('JwsService') wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) @@ -27,6 +28,8 @@ describe('JwsService', () => { await wallet.createAndOpen(config.walletConfig!) jwsService = new JwsService() + didJwsz6MkfKey = await wallet.createKey({ seed: didJwsz6Mkf.SEED, keyType: KeyType.Ed25519 }) + didJwsz6MkvKey = await wallet.createKey({ seed: didJwsz6Mkv.SEED, keyType: KeyType.Ed25519 }) }) afterAll(async () => { @@ -35,16 +38,17 @@ describe('JwsService', () => { describe('createJws', () => { it('creates a jws for the payload with the key associated with the verkey', async () => { - const key = await wallet.createKey({ seed: didJwsz6Mkf.SEED, keyType: KeyType.Ed25519 }) - const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) - const kid = new DidKey(key).did + const kid = new DidKey(didJwsz6MkfKey).did const jws = await jwsService.createJws(agentContext, { payload, - // FIXME: update to use key instance instead of verkey - verkey: key.publicKeyBase58, + key: didJwsz6MkfKey, header: { kid }, + protectedHeaderOptions: { + alg: 'EdDSA', + jwk: didJwsz6MkfKey.toJwk(), + }, }) expect(jws).toEqual(didJwsz6Mkf.JWS_JSON) @@ -55,37 +59,37 @@ describe('JwsService', () => { it('returns true if the jws signature matches the payload', async () => { const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) - const { isValid, signerVerkeys } = await jwsService.verifyJws(agentContext, { + const { isValid, signerKeys } = await jwsService.verifyJws(agentContext, { payload, jws: didJwsz6Mkf.JWS_JSON, }) expect(isValid).toBe(true) - expect(signerVerkeys).toEqual([didJwsz6Mkf.VERKEY]) + expect(signerKeys).toEqual([didJwsz6MkfKey]) }) it('returns all verkeys that signed the jws', async () => { const payload = JsonEncoder.toBuffer(didJwsz6Mkf.DATA_JSON) - const { isValid, signerVerkeys } = await jwsService.verifyJws(agentContext, { + const { isValid, signerKeys } = await jwsService.verifyJws(agentContext, { payload, jws: { signatures: [didJwsz6Mkf.JWS_JSON, didJwsz6Mkv.JWS_JSON] }, }) expect(isValid).toBe(true) - expect(signerVerkeys).toEqual([didJwsz6Mkf.VERKEY, didJwsz6Mkv.VERKEY]) + expect(signerKeys).toEqual([didJwsz6MkfKey, didJwsz6MkvKey]) }) it('returns false if the jws signature does not match the payload', async () => { const payload = JsonEncoder.toBuffer({ ...didJwsz6Mkf.DATA_JSON, did: 'another_did' }) - const { isValid, signerVerkeys } = await jwsService.verifyJws(agentContext, { + const { isValid, signerKeys } = await jwsService.verifyJws(agentContext, { payload, jws: didJwsz6Mkf.JWS_JSON, }) expect(isValid).toBe(false) - expect(signerVerkeys).toMatchObject([]) + expect(signerKeys).toMatchObject([]) }) it('throws an error if the jws signatures array does not contain a JWS', async () => { diff --git a/packages/core/src/crypto/index.ts b/packages/core/src/crypto/index.ts index 49b83878ad..449f3e537f 100644 --- a/packages/core/src/crypto/index.ts +++ b/packages/core/src/crypto/index.ts @@ -1,3 +1,9 @@ +export { Jwk } from './JwkTypes' +export { JwsService } from './JwsService' + +export * from './jwtUtils' + export { KeyType } from './KeyType' export { Key } from './Key' + export * from './signing-provider' diff --git a/packages/core/src/crypto/jwtUtils.ts b/packages/core/src/crypto/jwtUtils.ts new file mode 100644 index 0000000000..f60958fdf4 --- /dev/null +++ b/packages/core/src/crypto/jwtUtils.ts @@ -0,0 +1,13 @@ +export const jwtKeyAlgMapping = { + HMAC: ['HS256', 'HS384', 'HS512'], + RSA: ['RS256', 'RS384', 'RS512'], + ECDSA: ['ES256', 'ES384', 'ES512'], + 'RSA-PSS': ['PS256', 'PS384', 'PS512'], + EdDSA: ['Ed25519'], +} + +export type JwtAlgorithm = keyof typeof jwtKeyAlgMapping + +export function isJwtAlgorithm(value: string): value is JwtAlgorithm { + return Object.keys(jwtKeyAlgMapping).includes(value) +} diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index c577a260f7..d337a818de 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -464,10 +464,14 @@ export class DidExchangeProtocol { const jws = await this.jwsService.createJws(agentContext, { payload, - verkey, + key, header: { kid, }, + protectedHeaderOptions: { + alg: 'EdDSA', + jwk: key.toJwk(), + }, }) didDocAttach.addJws(jws) }) @@ -510,7 +514,7 @@ export class DidExchangeProtocol { this.logger.trace('DidDocument JSON', json) const payload = JsonEncoder.toBuffer(json) - const { isValid, signerVerkeys } = await this.jwsService.verifyJws(agentContext, { jws, payload }) + const { isValid, signerKeys } = await this.jwsService.verifyJws(agentContext, { jws, payload }) const didDocument = JsonTransformer.fromJSON(json, DidDocument) const didDocumentKeysBase58 = didDocument.authentication @@ -525,9 +529,9 @@ export class DidExchangeProtocol { }) .concat(invitationKeysBase58) - this.logger.trace('JWS verification result', { isValid, signerVerkeys, didDocumentKeysBase58 }) + this.logger.trace('JWS verification result', { isValid, signerKeys, didDocumentKeysBase58 }) - if (!isValid || !signerVerkeys.every((verkey) => didDocumentKeysBase58?.includes(verkey))) { + if (!isValid || !signerKeys.every((key) => didDocumentKeysBase58?.includes(key.publicKeyBase58))) { const problemCode = message instanceof DidExchangeRequestMessage ? DidExchangeProblemReportReason.RequestNotAccepted diff --git a/packages/core/src/modules/dids/DidsApi.ts b/packages/core/src/modules/dids/DidsApi.ts index 59134e5f6d..49b997d8e5 100644 --- a/packages/core/src/modules/dids/DidsApi.ts +++ b/packages/core/src/modules/dids/DidsApi.ts @@ -96,7 +96,7 @@ export class DidsApi { * * You can call `${@link DidsModule.resolve} to resolve the did document based on the did itself. */ - public getCreatedDids({ method }: { method?: string } = {}) { - return this.didRepository.getCreatedDids(this.agentContext, { method }) + public getCreatedDids({ method, did }: { method?: string; did?: string } = {}) { + return this.didRepository.getCreatedDids(this.agentContext, { method, did }) } } diff --git a/packages/core/src/modules/dids/repository/DidRepository.ts b/packages/core/src/modules/dids/repository/DidRepository.ts index 80fad9cf52..538270eac5 100644 --- a/packages/core/src/modules/dids/repository/DidRepository.ts +++ b/packages/core/src/modules/dids/repository/DidRepository.ts @@ -57,10 +57,11 @@ export class DidRepository extends Repository { return this.findSingleByQuery(agentContext, { did: createdDid, role: DidDocumentRole.Created }) } - public getCreatedDids(agentContext: AgentContext, { method }: { method?: string }) { + public getCreatedDids(agentContext: AgentContext, { method, did }: { method?: string; did?: string }) { return this.findByQuery(agentContext, { role: DidDocumentRole.Created, method, + did, }) } } diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index e841a7162d..a1896abb26 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -103,7 +103,8 @@ export class W3cCredentialService { */ public async verifyCredential( agentContext: AgentContext, - options: VerifyCredentialOptions + options: VerifyCredentialOptions, + verifyRevocationState = true ): Promise { const suites = this.getSignatureSuitesForCredential(agentContext, options.credential) @@ -111,6 +112,14 @@ export class W3cCredentialService { credential: JsonTransformer.toJSON(options.credential), suite: suites, documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + checkStatus: () => { + if (verifyRevocationState) { + throw new AriesFrameworkError('Revocation for W3C credentials is currently not supported') + } + return { + verified: true, + } + }, } // this is a hack because vcjs throws if purpose is passed as undefined or null diff --git a/packages/core/src/modules/vc/__tests__/contexts/mattr_vc_extension_v1.ts b/packages/core/src/modules/vc/__tests__/contexts/mattr_vc_extension_v1.ts new file mode 100644 index 0000000000..aaadf21bb5 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/mattr_vc_extension_v1.ts @@ -0,0 +1,17 @@ +export const MATTR_VC_EXTENSION_V1 = { + '@context': { + '@version': 1.1, + '@protected': true, + VerifiableCredentialExtension: { + '@id': 'https://mattr.global/contexts/vc-extensions/v1#VerifiableCredentialExtension', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + name: 'https://mattr.global/contexts/vc-extensions/v1#name', + description: 'https://mattr.global/contexts/vc-extensions/v1#description', + }, + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/purl_ob_v3po.ts b/packages/core/src/modules/vc/__tests__/contexts/purl_ob_v3po.ts new file mode 100644 index 0000000000..3b2ffe28f6 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/purl_ob_v3po.ts @@ -0,0 +1,438 @@ +export const PURL_OB_V3P0 = { + '@context': { + id: '@id', + type: '@type', + xsd: 'https://www.w3.org/2001/XMLSchema#', + OpenBadgeCredential: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#OpenBadgeCredential', + }, + Achievement: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Achievement', + '@context': { + achievementType: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#achievementType', + '@type': 'xsd:string', + }, + alignment: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#alignment', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Alignment', + '@container': '@set', + }, + creator: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Profile', + }, + creditsAvailable: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#creditsAvailable', + '@type': 'xsd:float', + }, + criteria: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Criteria', + '@type': '@id', + }, + fieldOfStudy: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#fieldOfStudy', + '@type': 'xsd:string', + }, + humanCode: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#humanCode', + '@type': 'xsd:string', + }, + otherIdentifier: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#otherIdentifier', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#IdentifierEntry', + '@container': '@set', + }, + related: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#related', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Related', + '@container': '@set', + }, + resultDescription: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#resultDescription', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#ResultDescription', + '@container': '@set', + }, + specialization: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#specialization', + '@type': 'xsd:string', + }, + tag: { + '@id': 'https://schema.org/keywords', + '@type': 'xsd:string', + '@container': '@set', + }, + version: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#version', + '@type': 'xsd:string', + }, + }, + }, + AchievementCredential: { + '@id': 'OpenBadgeCredential', + }, + AchievementSubject: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#AchievementSubject', + '@context': { + achievement: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Achievement', + }, + activityEndDate: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#activityEndDate', + '@type': 'xsd:date', + }, + activityStartDate: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#activityStartDate', + '@type': 'xsd:date', + }, + creditsEarned: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#creditsEarned', + '@type': 'xsd:float', + }, + identifier: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#identifier', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#IdentityObject', + '@container': '@set', + }, + licenseNumber: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#licenseNumber', + '@type': 'xsd:string', + }, + result: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#result', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Result', + '@container': '@set', + }, + role: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#role', + '@type': 'xsd:string', + }, + source: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#source', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Profile', + }, + term: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#term', + '@type': 'xsd:string', + }, + }, + }, + Address: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Address', + '@context': { + addressCountry: { + '@id': 'https://schema.org/addressCountry', + '@type': 'xsd:string', + }, + addressCountryCode: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#CountryCode', + '@type': 'xsd:string', + }, + addressLocality: { + '@id': 'https://schema.org/addressLocality', + '@type': 'xsd:string', + }, + addressRegion: { + '@id': 'https://schema.org/addressRegion', + '@type': 'xsd:string', + }, + geo: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#GeoCoordinates', + }, + postOfficeBoxNumber: { + '@id': 'https://schema.org/postOfficeBoxNumber', + '@type': 'xsd:string', + }, + postalCode: { + '@id': 'https://schema.org/postalCode', + '@type': 'xsd:string', + }, + streetAddress: { + '@id': 'https://schema.org/streetAddress', + '@type': 'xsd:string', + }, + }, + }, + Alignment: { + '@id': 'https://schema.org/Alignment', + '@context': { + targetCode: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#targetCode', + '@type': 'xsd:string', + }, + targetDescription: { + '@id': 'https://schema.org/targetDescription', + '@type': 'xsd:string', + }, + targetFramework: { + '@id': 'https://schema.org/targetFramework', + '@type': 'xsd:string', + }, + targetName: { + '@id': 'https://schema.org/targetName', + '@type': 'xsd:string', + }, + targetType: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#targetType', + '@type': 'xsd:string', + }, + targetUrl: { + '@id': 'https://schema.org/targetUrl', + '@type': 'xsd:anyURI', + }, + }, + }, + Criteria: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Criteria', + }, + EndorsementCredential: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#EndorsementCredential', + }, + EndorsementSubject: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#EndorsementSubject', + '@context': { + endorsementComment: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#endorsementComment', + '@type': 'xsd:string', + }, + }, + }, + Evidence: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Evidence', + '@context': { + audience: { + '@id': 'https://schema.org/audience', + '@type': 'xsd:string', + }, + genre: { + '@id': 'https://schema.org/genre', + '@type': 'xsd:string', + }, + }, + }, + GeoCoordinates: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#GeoCoordinates', + '@context': { + latitude: { + '@id': 'https://schema.org/latitude', + '@type': 'xsd:string', + }, + longitude: { + '@id': 'https://schema.org/longitude', + '@type': 'xsd:string', + }, + }, + }, + IdentifierEntry: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#IdentifierEntry', + '@context': { + identifier: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#identifier', + '@type': 'xsd:string', + }, + identifierType: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#identifierType', + '@type': 'xsd:string', + }, + }, + }, + IdentityObject: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#IdentityObject', + '@context': { + hashed: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#hashed', + '@type': 'xsd:boolean', + }, + identityHash: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#identityHash', + '@type': 'xsd:string', + }, + identityType: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#identityType', + '@type': 'xsd:string', + }, + salt: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#salt', + '@type': 'xsd:string', + }, + }, + }, + Image: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Image', + '@context': { + caption: { + '@id': 'https://schema.org/caption', + '@type': 'xsd:string', + }, + }, + }, + Profile: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Profile', + '@context': { + additionalName: { + '@id': 'https://schema.org/additionalName', + '@type': 'xsd:string', + }, + address: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Address', + }, + dateOfBirth: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#dateOfBirth', + '@type': 'xsd:date', + }, + email: { + '@id': 'https://schema.org/email', + '@type': 'xsd:string', + }, + familyName: { + '@id': 'https://schema.org/familyName', + '@type': 'xsd:string', + }, + familyNamePrefix: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#familyNamePrefix', + '@type': 'xsd:string', + }, + givenName: { + '@id': 'https://schema.org/givenName', + '@type': 'xsd:string', + }, + honorificPrefix: { + '@id': 'https://schema.org/honorificPrefix', + '@type': 'xsd:string', + }, + honorificSuffix: { + '@id': 'https://schema.org/honorificSuffix', + '@type': 'xsd:string', + }, + otherIdentifier: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#otherIdentifier', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#IdentifierEntry', + '@container': '@set', + }, + parentOrg: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#parentOrg', + '@type': 'xsd:string', + }, + patronymicName: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#patronymicName', + '@type': 'xsd:string', + }, + phone: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#PhoneNumber', + '@type': 'xsd:string', + }, + official: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#official', + '@type': 'xsd:string', + }, + }, + }, + Related: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Related', + '@context': { + version: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#version', + '@type': 'xsd:string', + }, + }, + }, + Result: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Result', + '@context': { + achievedLevel: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#achievedLevel', + '@type': 'xsd:anyURI', + }, + resultDescription: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#resultDescription', + '@type': 'xsd:anyURI', + }, + status: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#status', + '@type': 'xsd:string', + }, + value: { + '@id': 'https://schema.org/value', + '@type': 'xsd:string', + }, + }, + }, + ResultDescription: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#ResultDescription', + '@context': { + allowedValue: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#allowedValue', + '@type': 'xsd:string', + '@container': '@set', + }, + requiredLevel: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#requiredLevel', + '@type': 'xsd:anyURI', + }, + requiredValue: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#requiredValue', + '@type': 'xsd:string', + }, + resultType: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#resultType', + '@type': 'xsd:string', + }, + rubricCriterionLevel: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#rubricCriterionLevel', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#RubricCriterionLevel', + '@container': '@set', + }, + valueMax: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#valueMax', + '@type': 'xsd:string', + }, + valueMin: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#valueMin', + '@type': 'xsd:string', + }, + }, + }, + RubricCriterionLevel: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#RubricCriterionLevel', + '@context': { + level: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#level', + '@type': 'xsd:string', + }, + points: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#points', + '@type': 'xsd:string', + }, + }, + }, + alignment: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#alignment', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Alignment', + '@container': '@set', + }, + description: { + '@id': 'https://schema.org/description', + '@type': 'xsd:string', + }, + endorsement: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#endorsement', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#EndorsementCredential', + '@container': '@set', + }, + image: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#image', + '@type': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#Image', + }, + name: { + '@id': 'https://schema.org/name', + '@type': 'xsd:string', + }, + narrative: { + '@id': 'https://purl.imsglobal.org/spec/vc/ob/vocab.html#narrative', + '@type': 'xsd:string', + }, + url: { + '@id': 'https://schema.org/url', + '@type': 'xsd:anyURI', + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/vc_revocation_list_2020.ts b/packages/core/src/modules/vc/__tests__/contexts/vc_revocation_list_2020.ts new file mode 100644 index 0000000000..d0646eaa25 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/vc_revocation_list_2020.ts @@ -0,0 +1,37 @@ +export const VC_REVOCATION_LIST_2020 = { + '@context': { + '@protected': true, + RevocationList2020Credential: { + '@id': 'https://w3id.org/vc-revocation-list-2020#RevocationList2020Credential', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + description: 'http://schema.org/description', + name: 'http://schema.org/name', + }, + }, + RevocationList2020: { + '@id': 'https://w3id.org/vc-revocation-list-2020#RevocationList2020', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + encodedList: 'https://w3id.org/vc-revocation-list-2020#encodedList', + }, + }, + RevocationList2020Status: { + '@id': 'https://w3id.org/vc-revocation-list-2020#RevocationList2020Status', + '@context': { + '@protected': true, + id: '@id', + type: '@type', + revocationListCredential: { + '@id': 'https://w3id.org/vc-revocation-list-2020#revocationListCredential', + '@type': '@id', + }, + revocationListIndex: 'https://w3id.org/vc-revocation-list-2020#revocationListIndex', + }, + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/dids/did_web_launchpad.ts b/packages/core/src/modules/vc/__tests__/dids/did_web_launchpad.ts new file mode 100644 index 0000000000..81c02d5555 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/dids/did_web_launchpad.ts @@ -0,0 +1,24 @@ +export const DID_WEB_LAUNCHPAD = { + id: 'did:web:launchpad.vii.electron.mattrlabs.io', + '@context': ['https://w3.org/ns/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + verificationMethod: [ + { + id: 'did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg', + type: 'Ed25519VerificationKey2018', + controller: 'did:web:launchpad.vii.electron.mattrlabs.io', + publicKeyBase58: '6BhFMCGTJg9DnpXZe7zbiTrtuwion5FVV6Z2NUpwDMVT', + }, + ], + keyAgreement: [ + { + id: 'did:web:launchpad.vii.electron.mattrlabs.io#9eS8Tqsus1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:web:launchpad.vii.electron.mattrlabs.io', + publicKeyBase58: '9eS8Tqsus1uJmQpf37S8CnEeBrEehsC3qz8RMq67KoLB', + }, + ], + authentication: ['did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg'], + assertionMethod: ['did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg'], + capabilityDelegation: ['did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg'], + capabilityInvocation: ['did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg'], +} diff --git a/packages/core/src/modules/vc/__tests__/documentLoader.ts b/packages/core/src/modules/vc/__tests__/documentLoader.ts index 544ae02972..4d7aa89f0d 100644 --- a/packages/core/src/modules/vc/__tests__/documentLoader.ts +++ b/packages/core/src/modules/vc/__tests__/documentLoader.ts @@ -10,11 +10,15 @@ import { CITIZENSHIP_V1 } from './contexts/citizenship_v1' import { CREDENTIALS_V1 } from './contexts/credentials_v1' import { DID_V1 } from './contexts/did_v1' import { ED25519_V1 } from './contexts/ed25519_v1' +import { MATTR_VC_EXTENSION_V1 } from './contexts/mattr_vc_extension_v1' +import { PURL_OB_V3P0 } from './contexts/purl_ob_v3po' import { SECURITY_V1 } from './contexts/security_v1' import { SECURITY_V2 } from './contexts/security_v2' import { SECURITY_V3_UNSTABLE } from './contexts/security_v3_unstable' +import { VC_REVOCATION_LIST_2020 } from './contexts/vc_revocation_list_2020' import { DID_EXAMPLE_48939859 } from './dids/did_example_489398593' import { DID_SOV_QqEfJxe752NCmWqR5TssZ5 } from './dids/did_sov_QqEfJxe752NCmWqR5TssZ5' +import { DID_WEB_LAUNCHPAD } from './dids/did_web_launchpad' import { DID_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL } from './dids/did_z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL' import { DID_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV } from './dids/did_z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV' import { DID_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa } from './dids/did_zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa' @@ -72,6 +76,7 @@ export const DOCUMENTS = { ]]: DID_zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y2cgguc8e9hsGBifnVK67pQ4gve3m6iSboDkmJjxVEb1d6mRAx5fpMAejooNzNqqbTMVeUN, [DID_SOV_QqEfJxe752NCmWqR5TssZ5['id']]: DID_SOV_QqEfJxe752NCmWqR5TssZ5, + [DID_WEB_LAUNCHPAD['id']]: DID_WEB_LAUNCHPAD, SECURITY_CONTEXT_V1_URL: SECURITY_V1, SECURITY_CONTEXT_V2_URL: SECURITY_V2, SECURITY_CONTEXT_V3_URL: SECURITY_V3_UNSTABLE, @@ -88,10 +93,14 @@ export const DOCUMENTS = { 'https://www.w3.org/2018/credentials/v1': CREDENTIALS_V1, 'https://w3id.org/did/v1': DID_V1, 'https://www.w3.org/ns/did/v1': DID_V1, + 'https://w3.org/ns/did/v1': DID_V1, 'https://w3id.org/citizenship/v1': CITIZENSHIP_V1, 'https://www.w3.org/ns/odrl.jsonld': ODRL, 'http://schema.org/': SCHEMA_ORG, 'https://w3id.org/vaccination/v1': VACCINATION_V1, + 'https://mattr.global/contexts/vc-extensions/v1': MATTR_VC_EXTENSION_V1, + 'https://purl.imsglobal.org/spec/ob/v3p0/context.json': PURL_OB_V3P0, + 'https://w3c-ccg.github.io/vc-status-rl-2020/contexts/vc-revocation-list-2020/v1.jsonld': VC_REVOCATION_LIST_2020, } async function _customDocumentLoader(url: string): Promise { diff --git a/packages/core/src/modules/vc/models/credential/W3cCredential.ts b/packages/core/src/modules/vc/models/credential/W3cCredential.ts index ca5a1398bc..16f0b3f71c 100644 --- a/packages/core/src/modules/vc/models/credential/W3cCredential.ts +++ b/packages/core/src/modules/vc/models/credential/W3cCredential.ts @@ -40,7 +40,7 @@ export class W3cCredential { @Expose({ name: '@context' }) @IsJsonLdContext() - public context!: Array | JsonObject + public context!: Array | JsonObject @IsOptional() @IsUri() @@ -91,7 +91,7 @@ export class W3cCredential { return [this.credentialSubject.id] } - public get contexts(): Array { + public get contexts(): Array { if (Array.isArray(this.context)) { return this.context.filter((x) => typeof x === 'string') } diff --git a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts index 3c10234210..6ba94480be 100644 --- a/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts +++ b/packages/core/src/modules/vc/repository/W3cCredentialRecord.ts @@ -44,12 +44,16 @@ export class W3cCredentialRecord extends BaseRecord typeof ctx === 'string') as string[] + return { ...this._tags, issuerId: this.credential.issuerId, subjectIds: this.credential.credentialSubjectIds, schemaIds: this.credential.credentialSchemaIds, - contexts: this.credential.contexts, + contexts: stringContexts, proofTypes: this.credential.proofTypes, givenId: this.credential.id, } diff --git a/packages/core/src/modules/vc/validators.ts b/packages/core/src/modules/vc/validators.ts index 0bce78fa79..317b286cf6 100644 --- a/packages/core/src/modules/vc/validators.ts +++ b/packages/core/src/modules/vc/validators.ts @@ -2,6 +2,8 @@ import type { ValidationOptions } from 'class-validator' import { buildMessage, isString, isURL, ValidateBy } from 'class-validator' +import { isJsonObject } from '../../utils/type' + import { CREDENTIALS_CONTEXT_V1_URL } from './constants' export function IsJsonLdContext(validationOptions?: ValidationOptions): PropertyDecorator { @@ -13,8 +15,11 @@ export function IsJsonLdContext(validationOptions?: ValidationOptions): Property // If value is an array, check if all items are strings, are URLs and that // the first entry is a verifiable credential context if (Array.isArray(value)) { - return value.every((v) => isString(v) && isURL(v)) && value[0] === CREDENTIALS_CONTEXT_V1_URL + return value.every( + (v) => (isString(v) && isURL(v)) || (isJsonObject(v) && value[0] === CREDENTIALS_CONTEXT_V1_URL) + ) } + // If value is not an array, check if it is an object (assuming it's a JSON-LD context definition) if (typeof value === 'object') { return true diff --git a/packages/openid4vc-client/README.md b/packages/openid4vc-client/README.md index e89f6cab7a..540339fef7 100644 --- a/packages/openid4vc-client/README.md +++ b/packages/openid4vc-client/README.md @@ -32,6 +32,136 @@ Open ID Connect For Verifiable Credentials Client Module for [Aries Framework Ja ### Installation +Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. + +```sh +yarn add @aries-framework/openid4vc-client +``` + ### Quick start -### Example of usage +#### Requirements + +Before a credential can be requested, you need the issuer URI. This URI starts with `openid-initiate-issuance://` and is provided by the issuer. The issuer URI is commonly acquired by scanning a QR code. + +#### Module registration + +In order to get this module to work, we need to inject it into the agent. This makes the module's functionality accessible through the agent's `modules` api. + +```ts +import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' + +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + openId4VcClient: new OpenId4VcClientModule(), + /* other custom modules */ + }, +}) + +await agent.initialize() +``` + +How the module is injected and the agent has been initialized, you can access the module's functionality through `agent.modules.openId4VcClient`. + +#### Preparing a DID + +In order to request a credential, you'll need to provide a DID that the issuer will use for setting the credential subject. In the following snippet we create one for the sake of the example, but this can be any DID that has a _authentication verification method_ with key type `Ed25519`. + +```ts +// first we create the DID +const did = await agent.dids.create({ + method: 'key', + options: { + keyType: KeyType.Ed25519, + }, +}) + +// next we do some assertions and extract the key identifier (kid) + +if ( + !did.didState.didDocument || + !did.didState.didDocument.authentication || + did.didState.didDocument.authentication.length === 0 +) { + throw new Error("Error creating did document, or did document has no 'authentication' verificationMethods") +} + +const [verificationMethod] = did.didState.didDocument.authentication +const kid = typeof verificationMethod === 'string' ? verificationMethod : verificationMethod.id +``` + +#### Requesting the credential (Pre-Authorized) + +Now a credential issuance can be requested as follows. + +```ts +const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialPreAuthorized({ + issuerUri, + kid, + checkRevocationState: false, +}) + +console.log(w3cCredentialRecord) +``` + +#### Full example + +```ts +import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' +import { agentDependencies } from '@aries-framework/node' // use @aries-framework/react-native for React Native +import { Agent, KeyDidCreateOptions } from '@aries-framework/core' + +const run = async () => { + const issuerUri = '' // The obtained issuer URI + + // Create the Agent + const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + openId4VcClient: new OpenId4VcClientModule(), + /* other custom modules */ + }, + }) + + // Initialize the Agent + await agent.initialize() + + // Create a DID + const did = await agent.dids.create({ + method: 'key', + options: { + keyType: KeyType.Ed25519, + }, + }) + + // Assert DIDDocument is valid + if ( + !did.didState.didDocument || + !did.didState.didDocument.authentication || + did.didState.didDocument.authentication.length === 0 + ) { + throw new Error("Error creating did document, or did document has no 'authentication' verificationMethods") + } + + // Extract key identified (kid) for authentication verification method + const [verificationMethod] = did.didState.didDocument.authentication + const kid = typeof verificationMethod === 'string' ? verificationMethod : verificationMethod.id + + // Request the credential + const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialPreAuthorized({ + issuerUri, + kid, + checkRevocationState: false, + }) + + // Log the received credential + console.log(w3cCredentialRecord) +} +``` diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index 3f7a671015..97d83446a2 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -6,7 +6,6 @@ "files": [ "build" ], - "private": true, "license": "Apache-2.0", "publishConfig": { "access": "public" @@ -26,14 +25,11 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@sphereon/openid4vci-client": "^0.3.6", - "class-transformer": "0.5.1", - "class-validator": "0.13.1" + "@sphereon/openid4vci-client": "^0.3.6" }, - "peerDependencies": {}, "devDependencies": { "@aries-framework/node": "0.3.3", - "reflect-metadata": "^0.1.13", + "nock": "^13.3.0", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/openid4vc-client/src/OpenId4VcClientApi.ts b/packages/openid4vc-client/src/OpenId4VcClientApi.ts index ccf2cb84f3..ab671c46e1 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientApi.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientApi.ts @@ -1,7 +1,15 @@ +import type { W3cCredentialRecord } from '@aries-framework/core' + import { AgentContext, injectable } from '@aries-framework/core' import { OpenId4VcClientService } from './OpenId4VcClientService' +interface PreAuthorizedOptions { + issuerUri: string + kid: string + checkRevocationState?: boolean // default = true +} + /** * @public */ @@ -14,4 +22,14 @@ export class OpenId4VcClientApi { this.agentContext = agentContext this.openId4VcClientService = openId4VcClientService } + + public async requestCredentialPreAuthorized(options: PreAuthorizedOptions): Promise { + // set defaults + const checkRevocationState = options.checkRevocationState ?? true + + return this.openId4VcClientService.requestCredentialPreAuthorized(this.agentContext, { + ...options, + checkRevocationState: checkRevocationState, + }) + } } diff --git a/packages/openid4vc-client/src/OpenId4VcClientApiOptions.ts b/packages/openid4vc-client/src/OpenId4VcClientApiOptions.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts index 9c54e9b81c..9193b9d219 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -1,10 +1,236 @@ -import { injectable, W3cCredentialService } from '@aries-framework/core' +import type { AgentContext, W3cCredentialRecord } from '@aries-framework/core' +import type { EndpointMetadata, Jwt } from '@sphereon/openid4vci-client' + +import { + inject, + InjectionSymbols, + isJwtAlgorithm, + Logger, + DidsApi, + getKeyDidMappingByVerificationMethod, + AriesFrameworkError, + injectable, + JsonEncoder, + JsonTransformer, + W3cCredentialService, + W3cVerifiableCredential, + JwsService, + jwtKeyAlgMapping, +} from '@aries-framework/core' +import { + Alg, + AuthzFlowType, + CredentialRequestClientBuilder, + OpenID4VCIClient, + ProofOfPossessionBuilder, +} from '@sphereon/openid4vci-client' + +export interface PreAuthorizedOptions { + issuerUri: string + kid: string + checkRevocationState: boolean +} @injectable() export class OpenId4VcClientService { + private logger: Logger private w3cCredentialService: W3cCredentialService + private jwsService: JwsService - public constructor(w3cCredentialService: W3cCredentialService) { + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + w3cCredentialService: W3cCredentialService, + jwsService: JwsService + ) { this.w3cCredentialService = w3cCredentialService + this.jwsService = jwsService + this.logger = logger + } + + private signCallback(agentContext: AgentContext) { + return async (jwt: Jwt, kid: string) => { + if (!jwt.header) { + throw new AriesFrameworkError('No header present on JWT') + } + + if (!jwt.payload) { + throw new AriesFrameworkError('No payload present on JWT') + } + if (!kid.startsWith('did:')) { + throw new AriesFrameworkError(`kid '${kid}' is not a valid did. Only dids are supported as kid.`) + } + + if (!kid.includes('#')) { + throw new AriesFrameworkError( + `kid '${kid}' does not include a reference to the verificationMethod. The kid must specify a specific verificationMethod within the did document .` + ) + } + + const did = kid.split('#')[0] + + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const [didRecord] = await didsApi.getCreatedDids({ did }) + + if (!didRecord) { + throw new AriesFrameworkError(`No did record found for did ${did}. Is the did created by this agent?`) + } + + const didResult = await didsApi.resolve(did) + + if (!didResult.didDocument) { + throw new AriesFrameworkError( + `No did document found for did ${did}. ${didResult.didResolutionMetadata.error} - ${didResult.didResolutionMetadata.message}` + ) + } + + // TODO: which purposes are allowed? + const verificationMethod = didResult.didDocument.dereferenceKey(kid, ['authentication']) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const key = getKeyFromVerificationMethod(verificationMethod) + + const payload = JsonEncoder.toBuffer(jwt.payload) + + if (!isJwtAlgorithm(jwt.header.alg)) { + throw new AriesFrameworkError(`Unknown JWT algorithm: ${jwt.header.alg}`) + } + + if (jwtKeyAlgMapping[jwt.header.alg].includes(key.keyType)) { + throw new AriesFrameworkError( + `The retreived key's type does't match the JWT algorithm. Key type: ${key.keyType}, JWT algorithm: ${jwt.header.alg}` + ) + } + + const jws = await this.jwsService.createJwsCompact(agentContext, { + key, + payload, + protectedHeaderOptions: { + alg: jwt.header.alg, + kid: jwt.header.kid, + }, + }) + + return jws + } + } + + private getSignCallback(agentContext: AgentContext) { + return { + signCallback: this.signCallback(agentContext), + } + } + + private assertCredentialHasFormat(format: string, scope: string, metadata: EndpointMetadata) { + if (!metadata.openid4vci_metadata) { + throw new AriesFrameworkError( + `Server metadata doesn't include OpenID4VCI metadata. Unable to verify if the issuer supports the requested credential format: ${format}` + ) + } + + const supportedFomats = Object.keys(metadata.openid4vci_metadata?.credentials_supported[scope].formats) + + if (!supportedFomats.includes(format)) { + throw new AriesFrameworkError( + `Issuer doesn't support the requested credential format '${format}'' for requested credential type '${scope}'. Supported formats are: ${supportedFomats}` + ) + } + } + + public async requestCredentialPreAuthorized( + agentContext: AgentContext, + options: PreAuthorizedOptions + ): Promise { + this.logger.debug('Running pre-authorized flow with options', options) + + // this value is hardcoded as it's the only supported format at this point + const credentialFormat = 'ldp_vc' + + const client = await OpenID4VCIClient.initiateFromURI({ + issuanceInitiationURI: options.issuerUri, + flowType: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW, + kid: options.kid, + alg: Alg.EdDSA, + }) + + const accessToken = await client.acquireAccessToken({}) + + this.logger.info('Fetched server accessToken', accessToken) + + // We currently need the ts-ignore because the type + // inside of OpenID4VCIClient needs to be updated. + // @ts-ignore + if (!accessToken.scope) { + throw new AriesFrameworkError( + "Access token response doesn't contain a scope. Only scoped issuer URIs are supported at this time." + ) + } + + const serverMetadata = await client.retrieveServerMetadata() + + // @ts-ignore + this.assertCredentialHasFormat(credentialFormat, accessToken.scope, serverMetadata) + + this.logger.info('Fetched server metadata', { + issuer: serverMetadata.issuer, + credentialEndpoint: serverMetadata.credential_endpoint, + tokenEndpoint: serverMetadata.token_endpoint, + }) + + this.logger.debug('Full server metadata', serverMetadata) + + // proof of possession + const callbacks = this.getSignCallback(agentContext) + + const proofInput = await ProofOfPossessionBuilder.fromAccessTokenResponse({ + accessTokenResponse: accessToken, + callbacks: callbacks, + }) + .withEndpointMetadata(serverMetadata) + .withAlg(Alg.EdDSA) + .withKid(options.kid) + .build() + + this.logger.debug('Generated JWS', proofInput) + + const credentialRequestClient = CredentialRequestClientBuilder.fromIssuanceInitiationURI({ + uri: options.issuerUri, + metadata: serverMetadata, + }) + .withTokenFromResponse(accessToken) + .build() + + const credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({ + proofInput, + // @ts-ignore + credentialType: accessToken.scope, + format: 'ldp_vc', // Allows us to override the format + }) + + this.logger.debug('Credential request response', credentialResponse) + + if (!credentialResponse.successBody) { + throw new AriesFrameworkError('Did not receive a successful credential response') + } + + const credential = JsonTransformer.fromJSON(credentialResponse.successBody.credential, W3cVerifiableCredential) + + // verify the signature + const result = await this.w3cCredentialService.verifyCredential( + agentContext, + { credential }, + options.checkRevocationState + ) + + if (result && !result.verified) { + throw new AriesFrameworkError(`Failed to validate credential, error = ${result.error}`) + } + + const storedCredential = await this.w3cCredentialService.storeCredential(agentContext, { + credential, + }) + + this.logger.info(`Stored credential with id: ${storedCredential.id}`) + this.logger.debug('Full credential', storedCredential) + + return storedCredential } } diff --git a/packages/openid4vc-client/tests/fixtures.ts b/packages/openid4vc-client/tests/fixtures.ts new file mode 100644 index 0000000000..44936b9169 --- /dev/null +++ b/packages/openid4vc-client/tests/fixtures.ts @@ -0,0 +1,134 @@ +export const getMetadataResponse = { + authorization_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/authorize', + token_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/token', + jwks_uri: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/jwks', + token_endpoint_auth_methods_supported: [ + 'none', + 'client_secret_basic', + 'client_secret_jwt', + 'client_secret_post', + 'private_key_jwt', + ], + code_challenge_methods_supported: ['S256'], + grant_types_supported: ['authorization_code', 'urn:ietf:params:oauth:grant-type:pre-authorized_code'], + response_modes_supported: ['form_post', 'fragment', 'query'], + response_types_supported: ['code id_token', 'code', 'id_token', 'none'], + scopes_supported: ['OpenBadgeCredential', 'AcademicAward', 'LearnerProfile', 'PermanentResidentCard'], + token_endpoint_auth_signing_alg_values_supported: ['HS256', 'RS256', 'PS256', 'ES256', 'EdDSA'], + credential_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/credential', + credentials_supported: { + OpenBadgeCredential: { + formats: { + ldp_vc: { + name: 'JFF x vc-edu PlugFest 2', + description: "MATTR's submission for JFF Plugfest 2", + types: ['OpenBadgeCredential'], + binding_methods_supported: ['did'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + }, + }, + }, + AcademicAward: { + formats: { + ldp_vc: { + name: 'Example Academic Award', + description: 'Microcredential from the MyCreds Network.', + types: ['AcademicAward'], + binding_methods_supported: ['did'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + }, + }, + }, + LearnerProfile: { + formats: { + ldp_vc: { + name: 'Digitary Learner Profile', + description: 'Example', + types: ['LearnerProfile'], + binding_methods_supported: ['did'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + }, + }, + }, + PermanentResidentCard: { + formats: { + ldp_vc: { + name: 'Permanent Resident Card', + description: 'Government of Kakapo', + types: ['PermanentResidentCard'], + binding_methods_supported: ['did'], + cryptographic_suites_supported: ['Ed25519Signature2018'], + }, + }, + }, + }, +} + +export const aquireAccessTokenResponse = { + access_token: '7nikUotMQefxn7oRX56R7MDNE7KJTGfwGjOkHzGaUIG', + expires_in: 3600, + scope: 'OpenBadgeCredential', + token_type: 'Bearer', +} + +export const credentialRequestResponse = { + format: 'w3cvc-jsonld', + credential: { + type: ['VerifiableCredential', 'VerifiableCredentialExtension', 'OpenBadgeCredential'], + issuer: { + id: 'did:web:launchpad.vii.electron.mattrlabs.io', + name: 'Jobs for the Future (JFF)', + iconUrl: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', + image: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', + }, + name: 'JFF x vc-edu PlugFest 2', + description: "MATTR's submission for JFF Plugfest 2", + credentialBranding: { + backgroundColor: '#464c49', + }, + issuanceDate: '2023-01-25T16:58:06.292Z', + credentialSubject: { + id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', + type: ['AchievementSubject'], + achievement: { + id: 'urn:uuid:bd6d9316-f7ae-4073-a1e5-2f7f5bd22922', + name: 'JFF x vc-edu PlugFest 2 Interoperability', + type: ['Achievement'], + image: { + id: 'https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png', + type: 'Image', + }, + criteria: { + type: 'Criteria', + narrative: + 'Solutions providers earned this badge by demonstrating interoperability between multiple providers based on the OBv3 candidate final standard, with some additional required fields. Credential issuers earning this badge successfully issued a credential into at least two wallets. Wallet implementers earning this badge successfully displayed credentials issued by at least two different credential issuers.', + }, + description: + 'This credential solution supports the use of OBv3 and w3c Verifiable Credentials and is interoperable with at least two other solutions. This was demonstrated successfully during JFF x vc-edu PlugFest 2.', + }, + }, + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + { + '@vocab': 'https://w3id.org/security/undefinedTerm#', + }, + 'https://mattr.global/contexts/vc-extensions/v1', + 'https://purl.imsglobal.org/spec/ob/v3p0/context.json', + 'https://w3c-ccg.github.io/vc-status-rl-2020/contexts/vc-revocation-list-2020/v1.jsonld', + ], + credentialStatus: { + id: 'https://launchpad.vii.electron.mattrlabs.io/core/v1/revocation-lists/b4aa46a0-5539-4a6b-aa03-8f6791c22ce3#49', + type: 'RevocationList2020Status', + revocationListIndex: '49', + revocationListCredential: + 'https://launchpad.vii.electron.mattrlabs.io/core/v1/revocation-lists/b4aa46a0-5539-4a6b-aa03-8f6791c22ce3', + }, + proof: { + type: 'Ed25519Signature2018', + created: '2023-01-25T16:58:07Z', + jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..PrpRKt60yXOzMNiQY5bELX40F6Svwm-FyQ-Jv02VJDfTTH8GPPByjtOb_n3YfWidQVgySfGQ_H7VmCGjvsU6Aw', + proofPurpose: 'assertionMethod', + verificationMethod: 'did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg', + }, + }, +} diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts new file mode 100644 index 0000000000..fe2d5b1dbc --- /dev/null +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -0,0 +1,110 @@ +import type { KeyDidCreateOptions } from '@aries-framework/core' + +import { Agent, KeyType, LogLevel, W3cCredentialRecord, W3cVcModule } from '@aries-framework/core' +import nock, { cleanAll, enableNetConnect } from 'nock' + +import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' +import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader' +import { getAgentOptions } from '../../core/tests/helpers' + +import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' + +import { TestLogger } from '../../core/tests/logger' + +import { aquireAccessTokenResponse, credentialRequestResponse, getMetadataResponse } from './fixtures' + +describe('OpenId4VcClient', () => { + let agent: Agent<{ + openId4VcClient: OpenId4VcClientModule + w3cVc: W3cVcModule + }> + + beforeEach(async () => { + const agentOptions = getAgentOptions( + 'OpenId4VcClient Agent', + { + logger: new TestLogger(LogLevel.test), + }, + { + openId4VcClient: new OpenId4VcClientModule(), + w3cVc: new W3cVcModule({ + documentLoader: customDocumentLoader, + }), + } + ) + + agent = new Agent(agentOptions) + await agent.initialize() + }) + + afterEach(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + describe('Pre-authorized flow', () => { + const issuerUri = + 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential&pre-authorized_code=krBcsBIlye2T-G4-rHHnRZUCah9uzDKwohJK6ABNvL-' + beforeAll(async () => { + /** + * Below we're setting up some mock HTTP responses. + * These responses are based on the openid-initiate-issuance URI above + * */ + + // setup temporary redirect mock + nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { + Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', + }) + + // setup server metadata response + const httpMock = nock('https://launchpad.vii.electron.mattrlabs.io') + .get('/.well-known/openid-credential-issuer') + .reply(200, getMetadataResponse) + + // setup access token response + httpMock.post('/oidc/v1/auth/token').reply(200, aquireAccessTokenResponse) + + // setup credential request response + httpMock.post('/oidc/v1/auth/credential').reply(200, credentialRequestResponse) + }) + + afterAll(async () => { + cleanAll() + enableNetConnect() + }) + + it('Should successfully execute the pre-authorized flow', async () => { + const did = await agent.dids.create({ + method: 'key', + options: { + keyType: KeyType.Ed25519, + }, + secret: { + seed: '96213c3d7fc8d4d6754c7a0fd969598e', + }, + }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const keyInstance = didKeyToInstanceOfKey(did.didState.did!) + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const kid = `${did.didState.did!}#${keyInstance.fingerprint}` + + const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialPreAuthorized({ + issuerUri, + kid, + checkRevocationState: false, + }) + + expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) + + expect(w3cCredentialRecord.credential.type).toEqual([ + 'VerifiableCredential', + 'VerifiableCredentialExtension', + 'OpenBadgeCredential', + ]) + + // @ts-ignore + expect(w3cCredentialRecord.credential.credentialSubject.id).toEqual(did.didState.did) + }) + }) +}) diff --git a/packages/openid4vc-client/tests/setup.ts b/packages/openid4vc-client/tests/setup.ts index 4955aeb601..226f7031fa 100644 --- a/packages/openid4vc-client/tests/setup.ts +++ b/packages/openid4vc-client/tests/setup.ts @@ -1,3 +1 @@ -import 'reflect-metadata' - jest.setTimeout(20000) diff --git a/packages/openid4vc-client/tsconfig.build.json b/packages/openid4vc-client/tsconfig.build.json index 2b75d0adab..2b075bbd85 100644 --- a/packages/openid4vc-client/tsconfig.build.json +++ b/packages/openid4vc-client/tsconfig.build.json @@ -1,7 +1,8 @@ { "extends": "../../tsconfig.build.json", "compilerOptions": { - "outDir": "./build" + "outDir": "./build", + "skipLibCheck": true }, "include": ["src/**/*"] } diff --git a/packages/openid4vc-client/tsconfig.json b/packages/openid4vc-client/tsconfig.json index 46efe6f721..c1aca0e050 100644 --- a/packages/openid4vc-client/tsconfig.json +++ b/packages/openid4vc-client/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "types": ["jest"] + "types": ["jest"], + "skipLibCheck": true } } diff --git a/yarn.lock b/yarn.lock index 945e21eff3..4f5d4a3bbb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2280,9 +2280,9 @@ uint8arrays "^3.1.1" "@sphereon/ssi-types@^0.8.1-next.123": - version "0.8.1-unstable.145" - resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.8.1-unstable.145.tgz#418cf00ebb077ccb9644e652bc4e7fb0eb23b645" - integrity sha512-ixT8z5bwDWKJaMQTsUeRs7vMg5fz68BRJhxn10Tkeg68nJUEUHck44QJOhog0MmjNJKw2k6U/IqIS0oOdxTSHQ== + version "0.8.1-unstable.179" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.8.1-unstable.179.tgz#9583ea0e1011d03876a9108eb863dce83502ada3" + integrity sha512-Se8n7sh3UEO+LGfUcO946TaQaGJf7ozY5tRo9V3Ssax0Rg5MMSOdlf+YE0tgZ7X84WZOrFTdzUVxpN2tpoYRlQ== dependencies: jwt-decode "^3.1.2" @@ -7574,7 +7574,7 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.7.0: +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -8377,6 +8377,16 @@ nocache@^2.1.0: resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== +nock@^13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.3.0.tgz#b13069c1a03f1ad63120f994b04bfd2556925768" + integrity sha512-HHqYQ6mBeiMc+N038w8LkMpDCRquCHWeNmN3v6645P3NhN2+qXOBqvPqo7Rt1VyCMzKhJ733wZqw5B7cQVFNPg== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + lodash "^4.17.21" + propagate "^2.0.0" + node-addon-api@^3.0.0: version "3.2.1" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" @@ -9289,6 +9299,11 @@ prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.13.1" +propagate@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== + proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" From 115d89736a8f529034ed0f64c655656bffbe6c9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 23:45:01 +0000 Subject: [PATCH 026/139] build(deps): bump http-cache-semantics from 4.1.0 to 4.1.1 (#1258) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4f5d4a3bbb..3fe3c222e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6007,9 +6007,9 @@ html-escaper@^2.0.0: integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== http-cache-semantics@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http-errors@2.0.0: version "2.0.0" From f18d1890546f7d66571fe80f2f3fc1fead1cd4c3 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 6 Feb 2023 23:34:21 -0300 Subject: [PATCH 027/139] feat: add initial askar package (#1211) Signed-off-by: Ariel Gentile --- packages/askar/README.md | 31 + packages/askar/jest.config.ts | 14 + packages/askar/package.json | 41 + packages/askar/src/AskarModule.ts | 33 + packages/askar/src/AskarModuleConfig.ts | 44 ++ packages/askar/src/index.ts | 9 + .../askar/src/storage/AskarStorageService.ts | 177 +++++ .../__tests__/AskarStorageService.test.ts | 307 +++++++ packages/askar/src/storage/index.ts | 1 + packages/askar/src/storage/utils.ts | 110 +++ packages/askar/src/types.ts | 3 + packages/askar/src/utils/askarError.ts | 16 + packages/askar/src/utils/askarKeyTypes.ts | 6 + packages/askar/src/utils/askarWalletConfig.ts | 76 ++ packages/askar/src/utils/assertAskarWallet.ts | 13 + packages/askar/src/utils/index.ts | 3 + packages/askar/src/wallet/AskarWallet.ts | 748 ++++++++++++++++++ .../AskarWalletPostgresStorageConfig.ts | 22 + packages/askar/src/wallet/JweEnvelope.ts | 62 ++ .../src/wallet/__tests__/AskarWallet.test.ts | 252 ++++++ .../src/wallet/__tests__/packing.test.ts | 52 ++ packages/askar/src/wallet/index.ts | 2 + .../askar/tests/askar-postgres.e2e.test.ts | 102 +++ packages/askar/tests/helpers.ts | 49 ++ packages/askar/tests/setup.ts | 11 + packages/askar/tsconfig.build.json | 7 + packages/askar/tsconfig.json | 6 + packages/core/src/agent/Agent.ts | 6 +- packages/core/src/storage/FileSystem.ts | 1 + packages/core/src/types.ts | 10 +- packages/core/src/utils/TypedArrayEncoder.ts | 2 +- packages/node/src/NodeFileSystem.ts | 4 + .../react-native/src/ReactNativeFileSystem.ts | 4 + .../e2e-askar-indy-sdk-wallet-subject.test.ts | 135 ++++ yarn.lock | 32 + 35 files changed, 2383 insertions(+), 8 deletions(-) create mode 100644 packages/askar/README.md create mode 100644 packages/askar/jest.config.ts create mode 100644 packages/askar/package.json create mode 100644 packages/askar/src/AskarModule.ts create mode 100644 packages/askar/src/AskarModuleConfig.ts create mode 100644 packages/askar/src/index.ts create mode 100644 packages/askar/src/storage/AskarStorageService.ts create mode 100644 packages/askar/src/storage/__tests__/AskarStorageService.test.ts create mode 100644 packages/askar/src/storage/index.ts create mode 100644 packages/askar/src/storage/utils.ts create mode 100644 packages/askar/src/types.ts create mode 100644 packages/askar/src/utils/askarError.ts create mode 100644 packages/askar/src/utils/askarKeyTypes.ts create mode 100644 packages/askar/src/utils/askarWalletConfig.ts create mode 100644 packages/askar/src/utils/assertAskarWallet.ts create mode 100644 packages/askar/src/utils/index.ts create mode 100644 packages/askar/src/wallet/AskarWallet.ts create mode 100644 packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts create mode 100644 packages/askar/src/wallet/JweEnvelope.ts create mode 100644 packages/askar/src/wallet/__tests__/AskarWallet.test.ts create mode 100644 packages/askar/src/wallet/__tests__/packing.test.ts create mode 100644 packages/askar/src/wallet/index.ts create mode 100644 packages/askar/tests/askar-postgres.e2e.test.ts create mode 100644 packages/askar/tests/helpers.ts create mode 100644 packages/askar/tests/setup.ts create mode 100644 packages/askar/tsconfig.build.json create mode 100644 packages/askar/tsconfig.json create mode 100644 tests/e2e-askar-indy-sdk-wallet-subject.test.ts diff --git a/packages/askar/README.md b/packages/askar/README.md new file mode 100644 index 0000000000..5f68099a30 --- /dev/null +++ b/packages/askar/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript Askar Module

+

+ License + typescript + @aries-framework/askar version + +

+
+ +Askar module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). diff --git a/packages/askar/jest.config.ts b/packages/askar/jest.config.ts new file mode 100644 index 0000000000..55c67d70a6 --- /dev/null +++ b/packages/askar/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/askar/package.json b/packages/askar/package.json new file mode 100644 index 0000000000..5ed1b8b150 --- /dev/null +++ b/packages/askar/package.json @@ -0,0 +1,41 @@ +{ + "name": "@aries-framework/askar", + "main": "build/index", + "types": "build/index", + "version": "0.3.3", + "private": true, + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/askar", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/askar" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "0.3.3", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "rxjs": "^7.2.0", + "tsyringe": "^4.7.0" + }, + "devDependencies": { + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.1", + "reflect-metadata": "^0.1.13", + "rimraf": "^4.0.7", + "typescript": "~4.9.4" + } +} diff --git a/packages/askar/src/AskarModule.ts b/packages/askar/src/AskarModule.ts new file mode 100644 index 0000000000..5eccb13b3d --- /dev/null +++ b/packages/askar/src/AskarModule.ts @@ -0,0 +1,33 @@ +import type { DependencyManager, Module } from '@aries-framework/core' + +import { AriesFrameworkError, InjectionSymbols } from '@aries-framework/core' + +import { AskarStorageService } from './storage' +import { AskarWallet } from './wallet' + +export class AskarModule implements Module { + public register(dependencyManager: DependencyManager) { + try { + // eslint-disable-next-line import/no-extraneous-dependencies + require('@hyperledger/aries-askar-nodejs') + } catch (error) { + try { + require('@hyperledger/aries-askar-react-native') + } catch (error) { + throw new Error('Could not load aries-askar bindings') + } + } + + if (dependencyManager.isRegistered(InjectionSymbols.Wallet)) { + throw new AriesFrameworkError('There is an instance of Wallet already registered') + } else { + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, AskarWallet) + } + + if (dependencyManager.isRegistered(InjectionSymbols.StorageService)) { + throw new AriesFrameworkError('There is an instance of StorageService already registered') + } else { + dependencyManager.registerSingleton(InjectionSymbols.StorageService, AskarStorageService) + } + } +} diff --git a/packages/askar/src/AskarModuleConfig.ts b/packages/askar/src/AskarModuleConfig.ts new file mode 100644 index 0000000000..c2104eff8e --- /dev/null +++ b/packages/askar/src/AskarModuleConfig.ts @@ -0,0 +1,44 @@ +import type { AriesAskar } from './types' + +/** + * AskarModuleConfigOptions defines the interface for the options of the AskarModuleConfig class. + */ +export interface AskarModuleConfigOptions { + /** + * Implementation of the Askar interface according to aries-askar JavaScript wrapper. + * + * + * ## Node.JS + * + * ```ts + * import { NodeJSAriesAskar } from 'aries-askar-nodejs' + * + * const askarModule = new AskarModule({ + * askar: new NodeJSAriesAskar() + * }) + * ``` + * + * ## React Native + * + * ```ts + * import { ReactNativeAriesAskar } from 'aries-askar-react-native' + * + * const askarModule = new AskarModule({ + * askar: new ReactNativeAriesAskar() + * }) + * ``` + */ + askar: AriesAskar +} + +export class AskarModuleConfig { + private options: AskarModuleConfigOptions + + public constructor(options: AskarModuleConfigOptions) { + this.options = options + } + + public get askar() { + return this.options.askar + } +} diff --git a/packages/askar/src/index.ts b/packages/askar/src/index.ts new file mode 100644 index 0000000000..d7afa60eab --- /dev/null +++ b/packages/askar/src/index.ts @@ -0,0 +1,9 @@ +// Wallet +export { AskarWallet } from './wallet' + +// Storage +export { AskarStorageService } from './storage' + +// Module +export { AskarModule } from './AskarModule' +export { AskarModuleConfig } from './AskarModuleConfig' diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts new file mode 100644 index 0000000000..e7c96399c2 --- /dev/null +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -0,0 +1,177 @@ +import type { BaseRecordConstructor, AgentContext, BaseRecord, Query, StorageService } from '@aries-framework/core' + +import { + RecordDuplicateError, + WalletError, + RecordNotFoundError, + injectable, + JsonTransformer, +} from '@aries-framework/core' +import { Scan } from '@hyperledger/aries-askar-shared' + +import { askarErrors, isAskarError } from '../utils/askarError' +import { assertAskarWallet } from '../utils/assertAskarWallet' + +import { askarQueryFromSearchQuery, recordToInstance, transformFromRecordTagValues } from './utils' + +@injectable() +export class AskarStorageService implements StorageService { + /** @inheritDoc */ + public async save(agentContext: AgentContext, record: T) { + assertAskarWallet(agentContext.wallet) + const session = agentContext.wallet.session + + const value = JsonTransformer.serialize(record) + const tags = transformFromRecordTagValues(record.getTags()) as Record + + try { + await session.insert({ category: record.type, name: record.id, value, tags }) + } catch (error) { + if (isAskarError(error) && error.code === askarErrors.Duplicate) { + throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) + } + + throw new WalletError('Error saving record', { cause: error }) + } + } + + /** @inheritDoc */ + public async update(agentContext: AgentContext, record: T): Promise { + assertAskarWallet(agentContext.wallet) + const session = agentContext.wallet.session + + const value = JsonTransformer.serialize(record) + const tags = transformFromRecordTagValues(record.getTags()) as Record + + try { + await session.replace({ category: record.type, name: record.id, value, tags }) + } catch (error) { + if (isAskarError(error) && error.code === askarErrors.NotFound) { + throw new RecordNotFoundError(`record with id ${record.id} not found.`, { + recordType: record.type, + cause: error, + }) + } + + throw new WalletError('Error updating record', { cause: error }) + } + } + + /** @inheritDoc */ + public async delete(agentContext: AgentContext, record: T) { + assertAskarWallet(agentContext.wallet) + const session = agentContext.wallet.session + + try { + await session.remove({ category: record.type, name: record.id }) + } catch (error) { + if (isAskarError(error) && error.code === askarErrors.NotFound) { + throw new RecordNotFoundError(`record with id ${record.id} not found.`, { + recordType: record.type, + cause: error, + }) + } + throw new WalletError('Error deleting record', { cause: error }) + } + } + + /** @inheritDoc */ + public async deleteById( + agentContext: AgentContext, + recordClass: BaseRecordConstructor, + id: string + ): Promise { + assertAskarWallet(agentContext.wallet) + const session = agentContext.wallet.session + + try { + await session.remove({ category: recordClass.type, name: id }) + } catch (error) { + if (isAskarError(error) && error.code === askarErrors.NotFound) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + cause: error, + }) + } + throw new WalletError('Error deleting record', { cause: error }) + } + } + + /** @inheritDoc */ + public async getById(agentContext: AgentContext, recordClass: BaseRecordConstructor, id: string): Promise { + assertAskarWallet(agentContext.wallet) + const session = agentContext.wallet.session + + try { + const record = await session.fetch({ category: recordClass.type, name: id }) + if (!record) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + }) + } + return recordToInstance(record, recordClass) + } catch (error) { + if ( + isAskarError(error) && + (error.code === askarErrors.NotFound || + // FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario + error.message === 'Received null pointer. The native library could not find the value.') + ) { + throw new RecordNotFoundError(`record with id ${id} not found.`, { + recordType: recordClass.type, + cause: error, + }) + } + throw new WalletError(`Error getting record`, { cause: error }) + } + } + + /** @inheritDoc */ + public async getAll(agentContext: AgentContext, recordClass: BaseRecordConstructor): Promise { + assertAskarWallet(agentContext.wallet) + const session = agentContext.wallet.session + + const records = await session.fetchAll({ category: recordClass.type }) + + const instances = [] + for (const record of records) { + instances.push(recordToInstance(record, recordClass)) + } + return instances + } + + /** @inheritDoc */ + public async findByQuery( + agentContext: AgentContext, + recordClass: BaseRecordConstructor, + query: Query + ): Promise { + assertAskarWallet(agentContext.wallet) + const store = agentContext.wallet.store + + const askarQuery = askarQueryFromSearchQuery(query) + + const scan = new Scan({ + category: recordClass.type, + store, + tagFilter: askarQuery, + }) + + const instances = [] + try { + const records = await scan.fetchAll() + for (const record of records) { + instances.push(recordToInstance(record, recordClass)) + } + return instances + } catch (error) { + if ( + isAskarError(error) && // FIXME: this is current output from askar wrapper but does not describe specifically a 0 length scenario + error.message === 'Received null pointer. The native library could not find the value.' + ) { + return instances + } + throw new WalletError(`Error executing query`, { cause: error }) + } + } +} diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts new file mode 100644 index 0000000000..1ba1bf329f --- /dev/null +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -0,0 +1,307 @@ +import type { AgentContext, TagsBase } from '@aries-framework/core' + +import { + TypedArrayEncoder, + SigningProviderRegistry, + RecordDuplicateError, + RecordNotFoundError, +} from '@aries-framework/core' +import { ariesAskar } from '@hyperledger/aries-askar-shared' + +import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { AskarWallet } from '../../wallet/AskarWallet' +import { AskarStorageService } from '../AskarStorageService' +import { askarQueryFromSearchQuery } from '../utils' + +describe('AskarStorageService', () => { + let wallet: AskarWallet + let storageService: AskarStorageService + let agentContext: AgentContext + + beforeEach(async () => { + const agentConfig = getAgentConfig('AskarStorageServiceTest') + + wallet = new AskarWallet(agentConfig.logger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + agentContext = getAgentContext({ + wallet, + agentConfig, + }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await wallet.createAndOpen(agentConfig.walletConfig!) + storageService = new AskarStorageService() + }) + + afterEach(async () => { + await wallet.delete() + }) + + const insertRecord = async ({ id, tags }: { id?: string; tags?: TagsBase }) => { + const props = { + id, + foo: 'bar', + tags: tags ?? { myTag: 'foobar' }, + } + const record = new TestRecord(props) + await storageService.save(agentContext, record) + return record + } + + describe('tag transformation', () => { + it('should correctly transform tag values to string before storing', async () => { + const record = await insertRecord({ + id: 'test-id', + tags: { + someBoolean: true, + someOtherBoolean: false, + someStringValue: 'string', + anArrayValue: ['foo', 'bar'], + // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' + someStringNumberValue: '1', + anotherStringNumberValue: '0', + }, + }) + + const retrieveRecord = await ariesAskar.sessionFetch({ + category: record.type, + name: record.id, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + sessionHandle: wallet.session.handle!, + forUpdate: false, + }) + + expect(JSON.parse(retrieveRecord.getTags(0))).toEqual({ + someBoolean: '1', + someOtherBoolean: '0', + someStringValue: 'string', + 'anArrayValue:foo': '1', + 'anArrayValue:bar': '1', + someStringNumberValue: 'n__1', + anotherStringNumberValue: 'n__0', + }) + }) + + it('should correctly transform tag values from string after retrieving', async () => { + await ariesAskar.sessionUpdate({ + category: TestRecord.type, + name: 'some-id', + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + sessionHandle: wallet.session.handle!, + value: TypedArrayEncoder.fromString('{}'), + tags: { + someBoolean: '1', + someOtherBoolean: '0', + someStringValue: 'string', + 'anArrayValue:foo': '1', + 'anArrayValue:bar': '1', + // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' + someStringNumberValue: 'n__1', + anotherStringNumberValue: 'n__0', + }, + operation: 0, // EntryOperation.Insert + }) + + const record = await storageService.getById(agentContext, TestRecord, 'some-id') + + expect(record.getTags()).toEqual({ + someBoolean: true, + someOtherBoolean: false, + someStringValue: 'string', + anArrayValue: expect.arrayContaining(['bar', 'foo']), + someStringNumberValue: '1', + anotherStringNumberValue: '0', + }) + }) + }) + + describe('save()', () => { + it('should throw RecordDuplicateError if a record with the id already exists', async () => { + const record = await insertRecord({ id: 'test-id' }) + + return expect(() => storageService.save(agentContext, record)).rejects.toThrowError(RecordDuplicateError) + }) + + it('should save the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + const found = await storageService.getById(agentContext, TestRecord, 'test-id') + + expect(record).toEqual(found) + }) + }) + + describe('getById()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + return expect(() => storageService.getById(agentContext, TestRecord, 'does-not-exist')).rejects.toThrowError( + RecordNotFoundError + ) + }) + + it('should return the record by id', async () => { + const record = await insertRecord({ id: 'test-id' }) + const found = await storageService.getById(agentContext, TestRecord, 'test-id') + + expect(found).toEqual(record) + }) + }) + + describe('update()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + const record = new TestRecord({ + id: 'test-id', + foo: 'test', + tags: { some: 'tag' }, + }) + + return expect(() => storageService.update(agentContext, record)).rejects.toThrowError(RecordNotFoundError) + }) + + it('should update the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + + record.replaceTags({ ...record.getTags(), foo: 'bar' }) + record.foo = 'foobaz' + await storageService.update(agentContext, record) + + const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) + expect(retrievedRecord).toEqual(record) + }) + }) + + describe('delete()', () => { + it('should throw RecordNotFoundError if the record does not exist', async () => { + const record = new TestRecord({ + id: 'test-id', + foo: 'test', + tags: { some: 'tag' }, + }) + + return expect(() => storageService.delete(agentContext, record)).rejects.toThrowError(RecordNotFoundError) + }) + + it('should delete the record', async () => { + const record = await insertRecord({ id: 'test-id' }) + await storageService.delete(agentContext, record) + + return expect(() => storageService.getById(agentContext, TestRecord, record.id)).rejects.toThrowError( + RecordNotFoundError + ) + }) + }) + + describe('getAll()', () => { + it('should retrieve all records', async () => { + const createdRecords = await Promise.all( + Array(5) + .fill(undefined) + .map((_, index) => insertRecord({ id: `record-${index}` })) + ) + + const records = await storageService.getAll(agentContext, TestRecord) + + expect(records).toEqual(expect.arrayContaining(createdRecords)) + }) + }) + + describe('findByQuery()', () => { + it('should retrieve all records that match the query', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foobar' } }) + const expectedRecord2 = await insertRecord({ tags: { myTag: 'foobar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { myTag: 'foobar' }) + + expect(records.length).toBe(2) + expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) + }) + + it('finds records using $and statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo', anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { + $and: [{ myTag: 'foo' }, { anotherTag: 'bar' }], + }) + + expect(records.length).toBe(1) + expect(records[0]).toEqual(expectedRecord) + }) + + it('finds records using $or statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) + const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { + $or: [{ myTag: 'foo' }, { anotherTag: 'bar' }], + }) + + expect(records.length).toBe(2) + expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) + }) + + it('finds records using $not statements', async () => { + const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) + const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) + await insertRecord({ tags: { myTag: 'notfoobar' } }) + + const records = await storageService.findByQuery(agentContext, TestRecord, { + $not: { myTag: 'notfoobar' }, + }) + + expect(records.length).toBe(2) + expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) + }) + + it('correctly transforms an advanced query into a valid WQL query', async () => { + const expectedQuery = { + $and: [ + { + $and: undefined, + $not: undefined, + $or: [ + { myTag: '1', $and: undefined, $or: undefined, $not: undefined }, + { myTag: '0', $and: undefined, $or: undefined, $not: undefined }, + ], + }, + { + $or: undefined, + $not: undefined, + $and: [ + { theNumber: 'n__0', $and: undefined, $or: undefined, $not: undefined }, + { theNumber: 'n__1', $and: undefined, $or: undefined, $not: undefined }, + ], + }, + ], + $or: [ + { + 'aValue:foo': '1', + 'aValue:bar': '1', + $and: undefined, + $or: undefined, + $not: undefined, + }, + ], + $not: { myTag: 'notfoobar', $and: undefined, $or: undefined, $not: undefined }, + } + + expect( + askarQueryFromSearchQuery({ + $and: [ + { + $or: [{ myTag: true }, { myTag: false }], + }, + { + $and: [{ theNumber: '0' }, { theNumber: '1' }], + }, + ], + $or: [ + { + aValue: ['foo', 'bar'], + }, + ], + $not: { myTag: 'notfoobar' }, + }) + ).toEqual(expectedQuery) + }) + }) +}) diff --git a/packages/askar/src/storage/index.ts b/packages/askar/src/storage/index.ts new file mode 100644 index 0000000000..ac0265f1ea --- /dev/null +++ b/packages/askar/src/storage/index.ts @@ -0,0 +1 @@ +export * from './AskarStorageService' diff --git a/packages/askar/src/storage/utils.ts b/packages/askar/src/storage/utils.ts new file mode 100644 index 0000000000..381bd98dd7 --- /dev/null +++ b/packages/askar/src/storage/utils.ts @@ -0,0 +1,110 @@ +import type { BaseRecord, BaseRecordConstructor, Query, TagsBase } from '@aries-framework/core' +import type { EntryObject } from '@hyperledger/aries-askar-shared' + +import { JsonTransformer } from '@aries-framework/core' + +export function recordToInstance(record: EntryObject, recordClass: BaseRecordConstructor): T { + const instance = JsonTransformer.deserialize(record.value as string, recordClass) + instance.id = record.name + + const tags = record.tags ? transformToRecordTagValues(record.tags) : {} + instance.replaceTags(tags) + + return instance +} + +export function transformToRecordTagValues(tags: Record): TagsBase { + const transformedTags: TagsBase = {} + + for (const [key, value] of Object.entries(tags)) { + // If the value is a boolean string ('1' or '0') + // use the boolean val + if (value === '1' && key?.includes(':')) { + const [tagName, tagValue] = key.split(':') + + const transformedValue = transformedTags[tagName] + + if (Array.isArray(transformedValue)) { + transformedTags[tagName] = [...transformedValue, tagValue] + } else { + transformedTags[tagName] = [tagValue] + } + } + // Transform '1' and '0' to boolean + else if (value === '1' || value === '0') { + transformedTags[key] = value === '1' + } + // If 1 or 0 is prefixed with 'n__' we need to remove it. This is to prevent + // casting the value to a boolean + else if (value === 'n__1' || value === 'n__0') { + transformedTags[key] = value === 'n__1' ? '1' : '0' + } + // Otherwise just use the value + else { + transformedTags[key] = value as string + } + } + + return transformedTags +} + +export function transformFromRecordTagValues(tags: TagsBase): { [key: string]: string | undefined } { + const transformedTags: { [key: string]: string | undefined } = {} + + for (const [key, value] of Object.entries(tags)) { + // If the value is of type null we use the value undefined + // Askar doesn't support null as a value + if (value === null) { + transformedTags[key] = undefined + } + // If the value is a boolean use the Askar + // '1' or '0' syntax + else if (typeof value === 'boolean') { + transformedTags[key] = value ? '1' : '0' + } + // If the value is 1 or 0, we need to add something to the value, otherwise + // the next time we deserialize the tag values it will be converted to boolean + else if (value === '1' || value === '0') { + transformedTags[key] = `n__${value}` + } + // If the value is an array we create a tag for each array + // item ("tagName:arrayItem" = "1") + else if (Array.isArray(value)) { + value.forEach((item) => { + const tagName = `${key}:${item}` + transformedTags[tagName] = '1' + }) + } + // Otherwise just use the value + else { + transformedTags[key] = value + } + } + + return transformedTags +} + +/** + * Transforms the search query into a wallet query compatible with Askar WQL. + * + * The format used by AFJ is almost the same as the WQL query, with the exception of + * the encoding of values, however this is handled by the {@link AskarStorageServiceUtil.transformToRecordTagValues} + * method. + */ +export function askarQueryFromSearchQuery(query: Query): Record { + // eslint-disable-next-line prefer-const + let { $and, $or, $not, ...tags } = query + + $and = ($and as Query[] | undefined)?.map((q) => askarQueryFromSearchQuery(q)) + $or = ($or as Query[] | undefined)?.map((q) => askarQueryFromSearchQuery(q)) + $not = $not ? askarQueryFromSearchQuery($not as Query) : undefined + + const askarQuery = { + ...transformFromRecordTagValues(tags as unknown as TagsBase), + $and, + $or, + $not, + } + + return askarQuery +} diff --git a/packages/askar/src/types.ts b/packages/askar/src/types.ts new file mode 100644 index 0000000000..bc0baa2947 --- /dev/null +++ b/packages/askar/src/types.ts @@ -0,0 +1,3 @@ +import type { AriesAskar } from '@hyperledger/aries-askar-shared' + +export type { AriesAskar } diff --git a/packages/askar/src/utils/askarError.ts b/packages/askar/src/utils/askarError.ts new file mode 100644 index 0000000000..2cfcbd90cf --- /dev/null +++ b/packages/askar/src/utils/askarError.ts @@ -0,0 +1,16 @@ +import { AriesAskarError } from '@hyperledger/aries-askar-shared' + +export enum askarErrors { + Success = 0, + Backend = 1, + Busy = 2, + Duplicate = 3, + Encryption = 4, + Input = 5, + NotFound = 6, + Unexpected = 7, + Unsupported = 8, + Custom = 100, +} + +export const isAskarError = (error: Error) => error instanceof AriesAskarError diff --git a/packages/askar/src/utils/askarKeyTypes.ts b/packages/askar/src/utils/askarKeyTypes.ts new file mode 100644 index 0000000000..bb837f962e --- /dev/null +++ b/packages/askar/src/utils/askarKeyTypes.ts @@ -0,0 +1,6 @@ +import type { KeyType } from '@aries-framework/core' + +import { KeyAlgs } from '@hyperledger/aries-askar-shared' + +export const keyTypeSupportedByAskar = (keyType: KeyType) => + Object.entries(KeyAlgs).find(([, value]) => value === keyType.toString()) !== undefined diff --git a/packages/askar/src/utils/askarWalletConfig.ts b/packages/askar/src/utils/askarWalletConfig.ts new file mode 100644 index 0000000000..dcf1d15ab1 --- /dev/null +++ b/packages/askar/src/utils/askarWalletConfig.ts @@ -0,0 +1,76 @@ +import type { AskarWalletPostgresStorageConfig } from '../wallet/AskarWalletPostgresStorageConfig' +import type { WalletConfig } from '@aries-framework/core' + +import { KeyDerivationMethod, WalletError } from '@aries-framework/core' +import { StoreKeyMethod } from '@hyperledger/aries-askar-shared' + +export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod?: KeyDerivationMethod) => { + if (!keyDerivationMethod) { + return undefined + } + + const correspondanceTable = { + [KeyDerivationMethod.Raw]: StoreKeyMethod.Raw, + [KeyDerivationMethod.Argon2IInt]: `${StoreKeyMethod.Kdf}:argon2i:int`, + [KeyDerivationMethod.Argon2IMod]: `${StoreKeyMethod.Kdf}:argon2i:mod`, + } + + return correspondanceTable[keyDerivationMethod] as StoreKeyMethod +} + +export const uriFromWalletConfig = (walletConfig: WalletConfig, basePath: string): { uri: string; path?: string } => { + let uri = '' + let path + + // By default use sqlite as database backend + if (!walletConfig.storage) { + walletConfig.storage = { type: 'sqlite' } + } + + if (walletConfig.storage.type === 'sqlite') { + if (walletConfig.storage.inMemory) { + uri = 'sqlite://:memory:' + } else { + path = `${(walletConfig.storage.path as string) ?? basePath + '/wallet'}/${walletConfig.id}/sqlite.db` + uri = `sqlite://${path}` + } + } else if (walletConfig.storage.type === 'postgres') { + const storageConfig = walletConfig.storage as unknown as AskarWalletPostgresStorageConfig + + if (!storageConfig.config || !storageConfig.credentials) { + throw new WalletError('Invalid storage configuration for postgres wallet') + } + + const urlParams = [] + if (storageConfig.config.connectTimeout !== undefined) { + urlParams.push(`connect_timeout=${encodeURIComponent(storageConfig.config.connectTimeout)}`) + } + if (storageConfig.config.idleTimeout !== undefined) { + urlParams.push(`idle_timeout=${encodeURIComponent(storageConfig.config.idleTimeout)}`) + } + if (storageConfig.config.maxConnections !== undefined) { + urlParams.push(`max_connections=${encodeURIComponent(storageConfig.config.maxConnections)}`) + } + if (storageConfig.config.minConnections !== undefined) { + urlParams.push(`min_connections=${encodeURIComponent(storageConfig.config.minConnections)}`) + } + if (storageConfig.credentials.adminAccount !== undefined) { + urlParams.push(`admin_account=${encodeURIComponent(storageConfig.credentials.adminAccount)}`) + } + if (storageConfig.credentials.adminPassword !== undefined) { + urlParams.push(`admin_password=${encodeURIComponent(storageConfig.credentials.adminPassword)}`) + } + + uri = `postgres://${encodeURIComponent(storageConfig.credentials.account)}:${encodeURIComponent( + storageConfig.credentials.password + )}@${storageConfig.config.host}/${encodeURIComponent(walletConfig.id)}` + + if (urlParams.length > 0) { + uri = `${uri}?${urlParams.join('&')}` + } + } else { + throw new WalletError(`Storage type not supported: ${walletConfig.storage.type}`) + } + + return { uri, path } +} diff --git a/packages/askar/src/utils/assertAskarWallet.ts b/packages/askar/src/utils/assertAskarWallet.ts new file mode 100644 index 0000000000..37213e3d28 --- /dev/null +++ b/packages/askar/src/utils/assertAskarWallet.ts @@ -0,0 +1,13 @@ +import type { Wallet } from '@aries-framework/core' + +import { AriesFrameworkError } from '@aries-framework/core' + +import { AskarWallet } from '../wallet/AskarWallet' + +export function assertAskarWallet(wallet: Wallet): asserts wallet is AskarWallet { + if (!(wallet instanceof AskarWallet)) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const walletClassName = (wallet as any).constructor?.name ?? 'unknown' + throw new AriesFrameworkError(`Expected wallet to be instance of AskarWallet, found ${walletClassName}`) + } +} diff --git a/packages/askar/src/utils/index.ts b/packages/askar/src/utils/index.ts new file mode 100644 index 0000000000..b9f658de82 --- /dev/null +++ b/packages/askar/src/utils/index.ts @@ -0,0 +1,3 @@ +export * from './askarError' +export * from './askarKeyTypes' +export * from './askarWalletConfig' diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts new file mode 100644 index 0000000000..432e50cdda --- /dev/null +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -0,0 +1,748 @@ +import type { + EncryptedMessage, + WalletConfig, + WalletCreateKeyOptions, + DidConfig, + DidInfo, + WalletSignOptions, + UnpackedMessageContext, + WalletVerifyOptions, + Wallet, + WalletExportImportConfig, + WalletConfigRekey, + KeyPair, + KeyDerivationMethod, +} from '@aries-framework/core' +import type { Session } from '@hyperledger/aries-askar-shared' + +import { + JsonTransformer, + RecordNotFoundError, + RecordDuplicateError, + WalletInvalidKeyError, + WalletDuplicateError, + JsonEncoder, + KeyType, + Buffer, + AriesFrameworkError, + Logger, + WalletError, + InjectionSymbols, + Key, + SigningProviderRegistry, + TypedArrayEncoder, + FileSystem, + WalletNotFoundError, +} from '@aries-framework/core' +// eslint-disable-next-line import/order +import { + StoreKeyMethod, + KeyAlgs, + CryptoBox, + Store, + Key as AskarKey, + keyAlgFromString, +} from '@hyperledger/aries-askar-shared' + +const isError = (error: unknown): error is Error => error instanceof Error + +import { inject, injectable } from 'tsyringe' + +import { encodeToBase58, decodeFromBase58 } from '../../../core/src/utils/base58' +import { + askarErrors, + isAskarError, + keyDerivationMethodToStoreKeyMethod, + keyTypeSupportedByAskar, + uriFromWalletConfig, +} from '../utils' + +import { JweEnvelope, JweRecipient } from './JweEnvelope' + +@injectable() +export class AskarWallet implements Wallet { + private walletConfig?: WalletConfig + private _session?: Session + + private _store?: Store + + private logger: Logger + private fileSystem: FileSystem + + private signingKeyProviderRegistry: SigningProviderRegistry + private publicDidInfo: DidInfo | undefined + + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, + signingKeyProviderRegistry: SigningProviderRegistry + ) { + this.logger = logger + this.fileSystem = fileSystem + this.signingKeyProviderRegistry = signingKeyProviderRegistry + } + + public get isProvisioned() { + return this.walletConfig !== undefined + } + + public get isInitialized() { + return this._store !== undefined + } + + public get publicDid() { + return this.publicDidInfo + } + + public get store() { + if (!this._store) { + throw new AriesFrameworkError( + 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' + ) + } + + return this._store + } + + public get session() { + if (!this._session) { + throw new AriesFrameworkError('No Wallet Session is opened') + } + + return this._session + } + + public get masterSecretId() { + if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) { + throw new AriesFrameworkError( + 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' + ) + } + + return this.walletConfig?.masterSecretId ?? this.walletConfig.id + } + + /** + * Dispose method is called when an agent context is disposed. + */ + public async dispose() { + if (this.isInitialized) { + await this.close() + } + } + + /** + * @throws {WalletDuplicateError} if the wallet already exists + * @throws {WalletError} if another error occurs + */ + public async create(walletConfig: WalletConfig): Promise { + await this.createAndOpen(walletConfig) + await this.close() + } + + /** + * @throws {WalletDuplicateError} if the wallet already exists + * @throws {WalletError} if another error occurs + */ + public async createAndOpen(walletConfig: WalletConfig): Promise { + this.logger.debug(`Creating wallet '${walletConfig.id}`) + + const askarWalletConfig = await this.getAskarWalletConfig(walletConfig) + try { + this._store = await Store.provision({ + recreate: false, + uri: askarWalletConfig.uri, + profile: askarWalletConfig.profile, + keyMethod: askarWalletConfig.keyMethod, + passKey: askarWalletConfig.passKey, + }) + this.walletConfig = walletConfig + this._session = await this._store.openSession() + + // TODO: Master Secret creation (now part of IndyCredx/AnonCreds) + } catch (error) { + // FIXME: Askar should throw a Duplicate error code, but is currently returning Encryption + // And if we provide the very same wallet key, it will open it without any error + if (isAskarError(error) && (error.code === askarErrors.Encryption || error.code === askarErrors.Duplicate)) { + const errorMessage = `Wallet '${walletConfig.id}' already exists` + this.logger.debug(errorMessage) + + throw new WalletDuplicateError(errorMessage, { + walletType: 'AskarWallet', + cause: error, + }) + } + + const errorMessage = `Error creating wallet '${walletConfig.id}'` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + + this.logger.debug(`Successfully created wallet '${walletConfig.id}'`) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async open(walletConfig: WalletConfig): Promise { + await this._open(walletConfig) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async rotateKey(walletConfig: WalletConfigRekey): Promise { + if (!walletConfig.rekey) { + throw new WalletError('Wallet rekey undefined!. Please specify the new wallet key') + } + await this._open( + { + id: walletConfig.id, + key: walletConfig.key, + keyDerivationMethod: walletConfig.keyDerivationMethod, + }, + walletConfig.rekey, + walletConfig.rekeyDerivationMethod + ) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + private async _open( + walletConfig: WalletConfig, + rekey?: string, + rekeyDerivation?: KeyDerivationMethod + ): Promise { + if (this._store) { + throw new WalletError( + 'Wallet instance already opened. Close the currently opened wallet before re-opening the wallet' + ) + } + + const askarWalletConfig = await this.getAskarWalletConfig(walletConfig) + + try { + this._store = await Store.open({ + uri: askarWalletConfig.uri, + keyMethod: askarWalletConfig.keyMethod, + passKey: askarWalletConfig.passKey, + }) + + if (rekey) { + await this._store.rekey({ + passKey: rekey, + keyMethod: keyDerivationMethodToStoreKeyMethod(rekeyDerivation) ?? StoreKeyMethod.Raw, + }) + } + this._session = await this._store.openSession() + + this.walletConfig = walletConfig + } catch (error) { + if (isAskarError(error) && error.code === askarErrors.NotFound) { + const errorMessage = `Wallet '${walletConfig.id}' not found` + this.logger.debug(errorMessage) + + throw new WalletNotFoundError(errorMessage, { + walletType: 'AskarWallet', + cause: error, + }) + } else if (isAskarError(error) && error.code === askarErrors.Encryption) { + const errorMessage = `Incorrect key for wallet '${walletConfig.id}'` + this.logger.debug(errorMessage) + throw new WalletInvalidKeyError(errorMessage, { + walletType: 'AskarWallet', + cause: error, + }) + } + throw new WalletError( + `Error opening wallet ${walletConfig.id}. ERROR CODE ${error.code} MESSAGE ${error.message}`, + { cause: error } + ) + } + + this.logger.debug(`Wallet '${walletConfig.id}' opened with handle '${this._store.handle.handle}'`) + } + + /** + * @throws {WalletNotFoundError} if the wallet does not exist + * @throws {WalletError} if another error occurs + */ + public async delete(): Promise { + if (!this.walletConfig) { + throw new WalletError( + 'Can not delete wallet that does not have wallet config set. Make sure to call create wallet before deleting the wallet' + ) + } + + this.logger.info(`Deleting wallet '${this.walletConfig.id}'`) + + if (this._store) { + await this.close() + } + + try { + const { uri } = uriFromWalletConfig(this.walletConfig, this.fileSystem.basePath) + await Store.remove(uri) + } catch (error) { + const errorMessage = `Error deleting wallet '${this.walletConfig.id}': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + public async export(exportConfig: WalletExportImportConfig) { + // TODO + throw new WalletError('AskarWallet Export not yet implemented') + } + + public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig) { + // TODO + throw new WalletError('AskarWallet Import not yet implemented') + } + + /** + * @throws {WalletError} if the wallet is already closed or another error occurs + */ + public async close(): Promise { + this.logger.debug(`Closing wallet ${this.walletConfig?.id}`) + if (!this._store) { + throw new WalletError('Wallet is in invalid state, you are trying to close wallet that has no handle.') + } + + try { + await this.session.close() + await this.store.close() + this._session = undefined + this._store = undefined + this.publicDidInfo = undefined + } catch (error) { + const errorMessage = `Error closing wallet': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } + } + + public async initPublicDid(didConfig: DidConfig) { + // Not implemented, as it does not work with legacy Ledger module + } + + /** + * Create a key with an optional seed and keyType. + * The keypair is also automatically stored in the wallet afterwards + * + * @param seed string The seed for creating a key + * @param keyType KeyType the type of key that should be created + * + * @returns a Key instance with a publicKeyBase58 + * + * @throws {WalletError} When an unsupported keytype is requested + * @throws {WalletError} When the key could not be created + */ + public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { + try { + if (keyTypeSupportedByAskar(keyType)) { + const algorithm = keyAlgFromString(keyType) + + // Create key from seed + const key = seed ? AskarKey.fromSeed({ seed: Buffer.from(seed), algorithm }) : AskarKey.generate(algorithm) + + // Store key + await this.session.insertKey({ key, name: encodeToBase58(key.publicBytes) }) + return Key.fromPublicKey(key.publicBytes, keyType) + } else { + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) + + const keyPair = await signingKeyProvider.createKeyPair({ seed }) + await this.storeKeyPair(keyPair) + return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) + } + throw new WalletError(`Unsupported key type: '${keyType}'`) + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) + } + } + + /** + * sign a Buffer with an instance of a Key class + * + * @param data Buffer The data that needs to be signed + * @param key Key The key that is used to sign the data + * + * @returns A signature for the data + */ + public async sign({ data, key }: WalletSignOptions): Promise { + try { + if (keyTypeSupportedByAskar(key.keyType)) { + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`Currently not supporting signing of multiple messages`) + } + + const keyEntry = await this.session.fetchKey({ name: key.publicKeyBase58 }) + + if (!keyEntry) { + throw new WalletError('Key entry not found') + } + + const signed = keyEntry.key.signMessage({ message: data as Buffer }) + + return Buffer.from(signed) + } else { + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + + const keyPair = await this.retrieveKeyPair(key.publicKeyBase58) + const signed = await signingKeyProvider.sign({ + data, + privateKeyBase58: keyPair.privateKeyBase58, + publicKeyBase58: key.publicKeyBase58, + }) + + return signed + } + throw new WalletError(`Unsupported keyType: ${key.keyType}`) + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}`, { cause: error }) + } + } + + /** + * Verify the signature with the data and the used key + * + * @param data Buffer The data that has to be confirmed to be signed + * @param key Key The key that was used in the signing process + * @param signature Buffer The signature that was created by the signing process + * + * @returns A boolean whether the signature was created with the supplied data and key + * + * @throws {WalletError} When it could not do the verification + * @throws {WalletError} When an unsupported keytype is used + */ + public async verify({ data, key, signature }: WalletVerifyOptions): Promise { + try { + if (keyTypeSupportedByAskar(key.keyType)) { + if (!TypedArrayEncoder.isTypedArray(data)) { + throw new WalletError(`Currently not supporting verification of multiple messages`) + } + + const askarKey = AskarKey.fromPublicBytes({ + algorithm: keyAlgFromString(key.keyType), + publicKey: key.publicKey, + }) + return askarKey.verifySignature({ message: data as Buffer, signature }) + } else { + // Check if there is a signing key provider for the specified key type. + if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + + const signed = await signingKeyProvider.verify({ + data, + signature, + publicKeyBase58: key.publicKeyBase58, + }) + + return signed + } + throw new WalletError(`Unsupported keyType: ${key.keyType}`) + } + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError(`Error verifying signature of data signed with verkey ${key.publicKeyBase58}`, { + cause: error, + }) + } + } + + /** + * Pack a message using DIDComm V1 algorithm + * + * @param payload message to send + * @param recipientKeys array containing recipient keys in base58 + * @param senderVerkey sender key in base58 + * @returns JWE Envelope to send + */ + public async pack( + payload: Record, + recipientKeys: string[], + senderVerkey?: string // in base58 + ): Promise { + const cek = AskarKey.generate(KeyAlgs.Chacha20C20P) + + const senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined + + const senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined + + const recipients: JweRecipient[] = [] + + for (const recipientKey of recipientKeys) { + const targetExchangeKey = AskarKey.fromPublicBytes({ + publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, + algorithm: KeyAlgs.Ed25519, + }).convertkey({ algorithm: KeyAlgs.X25519 }) + + if (senderVerkey && senderExchangeKey) { + const encryptedSender = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: Buffer.from(senderVerkey), + }) + const nonce = CryptoBox.randomNonce() + const encryptedCek = CryptoBox.cryptoBox({ + recipientKey: targetExchangeKey, + senderKey: senderExchangeKey, + message: cek.secretBytes, + nonce, + }) + + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + sender: TypedArrayEncoder.toBase64URL(encryptedSender), + iv: TypedArrayEncoder.toBase64URL(nonce), + }, + }) + ) + } else { + const encryptedCek = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: cek.secretBytes, + }) + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + }, + }) + ) + } + } + + const protectedJson = { + enc: 'xchacha20poly1305_ietf', + typ: 'JWM/1.0', + alg: senderVerkey ? 'Authcrypt' : 'Anoncrypt', + recipients: recipients.map((item) => JsonTransformer.toJSON(item)), + } + + const { ciphertext, tag, nonce } = cek.aeadEncrypt({ + message: Buffer.from(JSON.stringify(payload)), + aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), + }).parts + + const envelope = new JweEnvelope({ + ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), + iv: TypedArrayEncoder.toBase64URL(nonce), + protected: JsonEncoder.toBase64URL(protectedJson), + tag: TypedArrayEncoder.toBase64URL(tag), + }).toJson() + + return envelope as EncryptedMessage + } + + /** + * Unpacks a JWE Envelope coded using DIDComm V1 algorithm + * + * @param messagePackage JWE Envelope + * @returns UnpackedMessageContext with plain text message, sender key and recipient key + */ + public async unpack(messagePackage: EncryptedMessage): Promise { + const protectedJson = JsonEncoder.fromBase64(messagePackage.protected) + + const alg = protectedJson.alg + const isAuthcrypt = alg === 'Authcrypt' + + if (!isAuthcrypt && alg != 'Anoncrypt') { + throw new WalletError(`Unsupported pack algorithm: ${alg}`) + } + + const recipients = [] + + for (const recip of protectedJson.recipients) { + const kid = recip.header.kid + if (!kid) { + throw new WalletError('Blank recipient key') + } + const sender = recip.header.sender ? TypedArrayEncoder.fromBase64(recip.header.sender) : undefined + const iv = recip.header.iv ? TypedArrayEncoder.fromBase64(recip.header.iv) : undefined + if (sender && !iv) { + throw new WalletError('Missing IV') + } else if (!sender && iv) { + throw new WalletError('Unexpected IV') + } + recipients.push({ + kid, + sender, + iv, + encrypted_key: TypedArrayEncoder.fromBase64(recip.encrypted_key), + }) + } + + let payloadKey, senderKey, recipientKey + + for (const recipient of recipients) { + let recipientKeyEntry + try { + recipientKeyEntry = await this.session.fetchKey({ name: recipient.kid }) + } catch (error) { + // TODO: Currently Askar wrapper throws error when key is not found + // In this case we don't need to throw any error because we should + // try with other recipient keys + continue + } + if (recipientKeyEntry) { + const recip_x = recipientKeyEntry.key.convertkey({ algorithm: KeyAlgs.X25519 }) + recipientKey = recipient.kid + + if (recipient.sender && recipient.iv) { + senderKey = TypedArrayEncoder.toUtf8String( + CryptoBox.sealOpen({ + recipientKey: recip_x, + ciphertext: recipient.sender, + }) + ) + const sender_x = AskarKey.fromPublicBytes({ + algorithm: KeyAlgs.Ed25519, + publicKey: decodeFromBase58(senderKey), + }).convertkey({ algorithm: KeyAlgs.X25519 }) + + payloadKey = CryptoBox.open({ + recipientKey: recip_x, + senderKey: sender_x, + message: recipient.encrypted_key, + nonce: recipient.iv, + }) + } + break + } + } + if (!payloadKey) { + throw new WalletError('No corresponding recipient key found') + } + + if (!senderKey && isAuthcrypt) { + throw new WalletError('Sender public key not provided for Authcrypt') + } + + const cek = AskarKey.fromSecretBytes({ algorithm: KeyAlgs.Chacha20C20P, secretKey: payloadKey }) + const message = cek.aeadDecrypt({ + ciphertext: TypedArrayEncoder.fromBase64(messagePackage.ciphertext as any), + nonce: TypedArrayEncoder.fromBase64(messagePackage.iv as any), + tag: TypedArrayEncoder.fromBase64(messagePackage.tag as any), + aad: TypedArrayEncoder.fromString(messagePackage.protected), + }) + return { + plaintextMessage: JsonEncoder.fromBuffer(message), + senderKey, + recipientKey, + } + } + + public async generateNonce(): Promise { + try { + return TypedArrayEncoder.toUtf8String(CryptoBox.randomNonce()) + } catch (error) { + if (!isError(error)) { + throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + } + throw new WalletError('Error generating nonce', { cause: error }) + } + } + + public async generateWalletKey() { + try { + return Store.generateRawKey() + } catch (error) { + throw new WalletError('Error generating wallet key', { cause: error }) + } + } + + private async getAskarWalletConfig(walletConfig: WalletConfig) { + const { uri, path } = uriFromWalletConfig(walletConfig, this.fileSystem.basePath) + + // Make sure path exists before creating the wallet + if (path) { + await this.fileSystem.createDirectory(path) + } + + return { + uri, + profile: walletConfig.id, + // FIXME: Default derivation method should be set somewhere in either agent config or some constants + keyMethod: keyDerivationMethodToStoreKeyMethod(walletConfig.keyDerivationMethod) ?? StoreKeyMethod.None, + passKey: walletConfig.key, + } + } + + private async retrieveKeyPair(publicKeyBase58: string): Promise { + try { + const entryObject = await this.session.fetch({ category: 'KeyPairRecord', name: `key-${publicKeyBase58}` }) + + if (entryObject?.value) { + return JsonEncoder.fromString(entryObject?.value as string) as KeyPair + } else { + throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) + } + } catch (error) { + if ( + isAskarError(error) && + (error.code === askarErrors.NotFound || + // FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario + error.message === 'Received null pointer. The native library could not find the value.') + ) { + throw new RecordNotFoundError(`KeyPairRecord not found for public key: ${publicKeyBase58}.`, { + recordType: 'KeyPairRecord', + cause: error, + }) + } + throw new WalletError('Error retrieving KeyPair record', { cause: error }) + } + } + + private async storeKeyPair(keyPair: KeyPair): Promise { + try { + await this.session.insert({ + category: 'KeyPairRecord', + name: `key-${keyPair.publicKeyBase58}`, + value: JSON.stringify(keyPair), + tags: { + keyType: keyPair.keyType, + }, + }) + } catch (error) { + if (isAskarError(error) && error.code === askarErrors.Duplicate) { + throw new RecordDuplicateError(`Record already exists`, { recordType: 'KeyPairRecord' }) + } + throw new WalletError('Error saving KeyPair record', { cause: error }) + } + } +} diff --git a/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts b/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts new file mode 100644 index 0000000000..a9a9aab91f --- /dev/null +++ b/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts @@ -0,0 +1,22 @@ +import type { WalletStorageConfig } from '../../../core/src/types' + +export interface AskarWalletPostgresConfig { + host: string + connectTimeout?: number + idleTimeout?: number + maxConnections?: number + minConnections?: number +} + +export interface AskarWalletPostgresCredentials { + account: string + password: string + adminAccount?: string + adminPassword?: string +} + +export interface AskarWalletPostgresStorageConfig extends WalletStorageConfig { + type: 'postgres' + config: AskarWalletPostgresConfig + credentials: AskarWalletPostgresCredentials +} diff --git a/packages/askar/src/wallet/JweEnvelope.ts b/packages/askar/src/wallet/JweEnvelope.ts new file mode 100644 index 0000000000..ac4d791f89 --- /dev/null +++ b/packages/askar/src/wallet/JweEnvelope.ts @@ -0,0 +1,62 @@ +import { JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' +import { Expose, Type } from 'class-transformer' + +export class JweRecipient { + @Expose({ name: 'encrypted_key' }) + public encryptedKey!: string + public header?: Record + + public constructor(options: { encryptedKey: Uint8Array; header?: Record }) { + if (options) { + this.encryptedKey = TypedArrayEncoder.toBase64URL(options.encryptedKey) + + this.header = options.header + } + } +} + +export interface JweEnvelopeOptions { + protected: string + unprotected?: string + recipients?: JweRecipient[] + ciphertext: string + iv: string + tag: string + aad?: string + header?: string[] + encryptedKey?: string +} + +export class JweEnvelope { + public protected!: string + public unprotected?: string + + @Type(() => JweRecipient) + public recipients?: JweRecipient[] + public ciphertext!: string + public iv!: string + public tag!: string + public aad?: string + public header?: string[] + + @Expose({ name: 'encrypted_key' }) + public encryptedKey?: string + + public constructor(options: JweEnvelopeOptions) { + if (options) { + this.protected = options.protected + this.unprotected = options.unprotected + this.recipients = options.recipients + this.ciphertext = options.ciphertext + this.iv = options.iv + this.tag = options.tag + this.aad = options.aad + this.header = options.header + this.encryptedKey = options.encryptedKey + } + } + + public toJson() { + return JsonTransformer.toJSON(this) + } +} diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts new file mode 100644 index 0000000000..15bbf174cd --- /dev/null +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -0,0 +1,252 @@ +import type { + SigningProvider, + WalletConfig, + CreateKeyPairOptions, + KeyPair, + SignOptions, + VerifyOptions, +} from '@aries-framework/core' + +import { + WalletError, + WalletDuplicateError, + WalletNotFoundError, + WalletInvalidKeyError, + KeyType, + SigningProviderRegistry, + TypedArrayEncoder, + KeyDerivationMethod, + Buffer, +} from '@aries-framework/core' +import { Store } from '@hyperledger/aries-askar-shared' + +import { encodeToBase58 } from '../../../../core/src/utils/base58' +import { agentDependencies } from '../../../../core/tests/helpers' +import testLogger from '../../../../core/tests/logger' +import { AskarWallet } from '../AskarWallet' + +// use raw key derivation method to speed up wallet creating / opening / closing between tests +const walletConfig: WalletConfig = { + id: 'Wallet: AskarWalletTest', + // generated using indy.generateWalletKey + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, +} + +describe('AskarWallet basic operations', () => { + let askarWallet: AskarWallet + + const seed = 'sample-seed' + const message = TypedArrayEncoder.fromString('sample-message') + + beforeEach(async () => { + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + await askarWallet.createAndOpen(walletConfig) + }) + + afterEach(async () => { + await askarWallet.delete() + }) + + test('Get the Master Secret', () => { + expect(askarWallet.masterSecretId).toEqual('Wallet: AskarWalletTest') + }) + + test('Get the wallet store', () => { + expect(askarWallet.store).toEqual(expect.any(Store)) + }) + + test('Generate Nonce', async () => { + await expect(askarWallet.generateNonce()).resolves.toEqual(expect.any(String)) + }) + + test('Create ed25519 keypair', async () => { + await expect( + askarWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) + ).resolves.toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Create x25519 keypair', async () => { + await expect(askarWallet.createKey({ seed, keyType: KeyType.X25519 })).resolves.toMatchObject({ + keyType: KeyType.X25519, + }) + }) + + describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { + test('Fail to create a Bls12381g1g2 keypair', async () => { + await expect(askarWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) + }) + }) + + test('Create a signature with a ed25519 keypair', async () => { + const ed25519Key = await askarWallet.createKey({ keyType: KeyType.Ed25519 }) + const signature = await askarWallet.sign({ + data: message, + key: ed25519Key, + }) + expect(signature.length).toStrictEqual(64) + }) + + test('Verify a signed message with a ed25519 publicKey', async () => { + const ed25519Key = await askarWallet.createKey({ keyType: KeyType.Ed25519 }) + const signature = await askarWallet.sign({ + data: message, + key: ed25519Key, + }) + await expect(askarWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) + }) + + test('masterSecretId is equal to wallet ID by default', async () => { + expect(askarWallet.masterSecretId).toEqual(walletConfig.id) + }) +}) + +describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { + describe('AskarWallet with custom signing provider', () => { + let askarWallet: AskarWallet + + const seed = 'sample-seed' + const message = TypedArrayEncoder.fromString('sample-message') + + class DummySigningProvider implements SigningProvider { + public keyType: KeyType = KeyType.Bls12381g1g2 + + public async createKeyPair(options: CreateKeyPairOptions): Promise { + return { + publicKeyBase58: encodeToBase58(Buffer.from(options.seed || 'publicKeyBase58')), + privateKeyBase58: 'privateKeyBase58', + keyType: KeyType.Bls12381g1g2, + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async sign(options: SignOptions): Promise { + return new Buffer('signed') + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async verify(options: VerifyOptions): Promise { + return true + } + } + + beforeEach(async () => { + askarWallet = new AskarWallet( + testLogger, + new agentDependencies.FileSystem(), + new SigningProviderRegistry([new DummySigningProvider()]) + ) + await askarWallet.createAndOpen(walletConfig) + }) + + afterEach(async () => { + await askarWallet.delete() + }) + + test('Create custom keypair and use it for signing', async () => { + const key = await askarWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 }) + expect(key.keyType).toBe(KeyType.Bls12381g1g2) + expect(key.publicKeyBase58).toBe(encodeToBase58(Buffer.from(seed))) + + const signature = await askarWallet.sign({ + data: message, + key, + }) + + expect(signature).toBeInstanceOf(Buffer) + }) + + test('Create custom keypair and use it for verifying', async () => { + const key = await askarWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 }) + expect(key.keyType).toBe(KeyType.Bls12381g1g2) + expect(key.publicKeyBase58).toBe(encodeToBase58(Buffer.from(seed))) + + const signature = await askarWallet.verify({ + data: message, + signature: new Buffer('signature'), + key, + }) + + expect(signature).toBeTruthy() + }) + + test('Attempt to create the same custom keypair twice', async () => { + await askarWallet.createKey({ seed: 'keybase58', keyType: KeyType.Bls12381g1g2 }) + + await expect(askarWallet.createKey({ seed: 'keybase58', keyType: KeyType.Bls12381g1g2 })).rejects.toThrow( + WalletError + ) + }) + }) +}) + +describe('AskarWallet management', () => { + let askarWallet: AskarWallet + + afterEach(async () => { + if (askarWallet) { + await askarWallet.delete() + } + }) + + test('Create', async () => { + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + + const initialKey = Store.generateRawKey() + const anotherKey = Store.generateRawKey() + + // Create and open wallet + await askarWallet.createAndOpen({ ...walletConfig, id: 'AskarWallet Create', key: initialKey }) + + // Close and try to re-create it + await askarWallet.close() + await expect( + askarWallet.createAndOpen({ ...walletConfig, id: 'AskarWallet Create', key: anotherKey }) + ).rejects.toThrowError(WalletDuplicateError) + }) + + test('Open', async () => { + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + + const initialKey = Store.generateRawKey() + const wrongKey = Store.generateRawKey() + + // Create and open wallet + await askarWallet.createAndOpen({ ...walletConfig, id: 'AskarWallet Open', key: initialKey }) + + // Close and try to re-opening it with a wrong key + await askarWallet.close() + await expect(askarWallet.open({ ...walletConfig, id: 'AskarWallet Open', key: wrongKey })).rejects.toThrowError( + WalletInvalidKeyError + ) + + // Try to open a non existent wallet + await expect( + askarWallet.open({ ...walletConfig, id: 'AskarWallet Open - Non existent', key: initialKey }) + ).rejects.toThrowError(WalletNotFoundError) + }) + + test('Rotate key', async () => { + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + + const initialKey = Store.generateRawKey() + await askarWallet.createAndOpen({ ...walletConfig, id: 'AskarWallet Key Rotation', key: initialKey }) + + await askarWallet.close() + + const newKey = Store.generateRawKey() + await askarWallet.rotateKey({ ...walletConfig, id: 'AskarWallet Key Rotation', key: initialKey, rekey: newKey }) + + await askarWallet.close() + + await expect( + askarWallet.open({ ...walletConfig, id: 'AskarWallet Key Rotation', key: initialKey }) + ).rejects.toThrowError(WalletInvalidKeyError) + + await askarWallet.open({ ...walletConfig, id: 'AskarWallet Key Rotation', key: newKey }) + + await askarWallet.close() + }) +}) diff --git a/packages/askar/src/wallet/__tests__/packing.test.ts b/packages/askar/src/wallet/__tests__/packing.test.ts new file mode 100644 index 0000000000..2a27e18678 --- /dev/null +++ b/packages/askar/src/wallet/__tests__/packing.test.ts @@ -0,0 +1,52 @@ +import type { WalletConfig } from '@aries-framework/core' + +import { + JsonTransformer, + BasicMessage, + KeyType, + SigningProviderRegistry, + KeyDerivationMethod, +} from '@aries-framework/core' + +import { agentDependencies } from '../../../../core/tests/helpers' +import testLogger from '../../../../core/tests/logger' +import { AskarWallet } from '../AskarWallet' + +// use raw key derivation method to speed up wallet creating / opening / closing between tests +const walletConfig: WalletConfig = { + id: 'Askar Wallet Packing', + // generated using indy.generateWalletKey + key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', + keyDerivationMethod: KeyDerivationMethod.Raw, +} + +describe('askarWallet packing', () => { + let askarWallet: AskarWallet + + beforeEach(async () => { + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + await askarWallet.createAndOpen(walletConfig) + }) + + afterEach(async () => { + await askarWallet.delete() + }) + + test('DIDComm V1 packing and unpacking', async () => { + // Create both sender and recipient keys + const senderKey = await askarWallet.createKey({ keyType: KeyType.Ed25519 }) + const recipientKey = await askarWallet.createKey({ keyType: KeyType.Ed25519 }) + + const message = new BasicMessage({ content: 'hello' }) + + const encryptedMessage = await askarWallet.pack( + message.toJSON(), + [recipientKey.publicKeyBase58], + senderKey.publicKeyBase58 + ) + + const plainTextMessage = await askarWallet.unpack(encryptedMessage) + + expect(JsonTransformer.fromJSON(plainTextMessage.plaintextMessage, BasicMessage)).toEqual(message) + }) +}) diff --git a/packages/askar/src/wallet/index.ts b/packages/askar/src/wallet/index.ts new file mode 100644 index 0000000000..8d569fdf4c --- /dev/null +++ b/packages/askar/src/wallet/index.ts @@ -0,0 +1,2 @@ +export { AskarWallet } from './AskarWallet' +export * from './AskarWalletPostgresStorageConfig' diff --git a/packages/askar/tests/askar-postgres.e2e.test.ts b/packages/askar/tests/askar-postgres.e2e.test.ts new file mode 100644 index 0000000000..dfbc6db600 --- /dev/null +++ b/packages/askar/tests/askar-postgres.e2e.test.ts @@ -0,0 +1,102 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { AskarWalletPostgresStorageConfig } from '../src/wallet' +import type { ConnectionRecord } from '@aries-framework/core' + +import { Agent, HandshakeProtocol } from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { waitForBasicMessage } from '../../core/tests/helpers' + +import { getPostgresAgentOptions } from './helpers' + +const storageConfig: AskarWalletPostgresStorageConfig = { + type: 'postgres', + config: { + host: 'localhost:5432', + }, + credentials: { + account: 'postgres', + password: 'postgres', + }, +} + +const alicePostgresAgentOptions = getPostgresAgentOptions('AgentsAlice', storageConfig, { + endpoints: ['rxjs:alice'], +}) +const bobPostgresAgentOptions = getPostgresAgentOptions('AgentsBob', storageConfig, { + endpoints: ['rxjs:bob'], +}) + +// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved +describe.skip('Askar Postgres agents', () => { + let aliceAgent: Agent + let bobAgent: Agent + let aliceConnection: ConnectionRecord + let bobConnection: ConnectionRecord + + afterAll(async () => { + if (bobAgent) { + await bobAgent.shutdown() + await bobAgent.wallet.delete() + } + + if (aliceAgent) { + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + } + }) + + test('make a connection between postgres agents', async () => { + const aliceMessages = new Subject() + const bobMessages = new Subject() + + const subjectMap = { + 'rxjs:alice': aliceMessages, + 'rxjs:bob': bobMessages, + } + + aliceAgent = new Agent(alicePostgresAgentOptions) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + + bobAgent = new Agent(bobPostgresAgentOptions) + bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) + bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await bobAgent.initialize() + + const aliceBobOutOfBandRecord = await aliceAgent.oob.createInvitation({ + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + const { connectionRecord: bobConnectionAtBobAlice } = await bobAgent.oob.receiveInvitation( + aliceBobOutOfBandRecord.outOfBandInvitation + ) + bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice!.id) + + const [aliceConnectionAtAliceBob] = await aliceAgent.connections.findAllByOutOfBandId(aliceBobOutOfBandRecord.id) + aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob!.id) + }) + + test('send a message to connection', async () => { + const message = 'hello, world' + await aliceAgent.basicMessages.sendMessage(aliceConnection.id, message) + + const basicMessage = await waitForBasicMessage(bobAgent, { + content: message, + }) + + expect(basicMessage.content).toBe(message) + }) + + test('can shutdown and re-initialize the same postgres agent', async () => { + expect(aliceAgent.isInitialized).toBe(true) + await aliceAgent.shutdown() + expect(aliceAgent.isInitialized).toBe(false) + await aliceAgent.initialize() + expect(aliceAgent.isInitialized).toBe(true) + }) +}) diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts new file mode 100644 index 0000000000..17a521a1af --- /dev/null +++ b/packages/askar/tests/helpers.ts @@ -0,0 +1,49 @@ +import type { AskarWalletPostgresStorageConfig } from '../src/wallet' +import type { InitConfig } from '@aries-framework/core' + +import { LogLevel } from '@aries-framework/core' +import path from 'path' + +import { TestLogger } from '../../core/tests/logger' +import { agentDependencies } from '../../node/src' +import { AskarModule } from '../src/AskarModule' + +export const genesisPath = process.env.GENESIS_TXN_PATH + ? path.resolve(process.env.GENESIS_TXN_PATH) + : path.join(__dirname, '../../../../network/genesis/local-genesis.txn') + +export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' + +export function getPostgresAgentOptions( + name: string, + storageConfig: AskarWalletPostgresStorageConfig, + extraConfig: Partial = {} +) { + const config: InitConfig = { + label: `Agent: ${name}`, + walletConfig: { + id: `Wallet${name}`, + key: `Key${name}`, + storage: storageConfig, + }, + connectToIndyLedgersOnStartup: false, + publicDidSeed, + autoAcceptConnections: true, + autoUpdateStorageOnStartup: false, + indyLedgers: [ + { + id: `pool-${name}`, + indyNamespace: `pool:localtest`, + isProduction: false, + genesisPath, + }, + ], + logger: new TestLogger(LogLevel.off, name), + ...extraConfig, + } + return { + config, + dependencies: agentDependencies, + modules: { askar: new AskarModule() }, + } as const +} diff --git a/packages/askar/tests/setup.ts b/packages/askar/tests/setup.ts new file mode 100644 index 0000000000..a09e05318c --- /dev/null +++ b/packages/askar/tests/setup.ts @@ -0,0 +1,11 @@ +import 'reflect-metadata' + +try { + // eslint-disable-next-line import/no-extraneous-dependencies + require('@hyperledger/aries-askar-nodejs') +} catch (error) { + throw new Error('Could not load aries-askar bindings') +} + +// FIXME: Remove when Askar JS Wrapper performance issues are solved +jest.setTimeout(180000) diff --git a/packages/askar/tsconfig.build.json b/packages/askar/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/askar/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/askar/tsconfig.json b/packages/askar/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/askar/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 2909c3536d..3785d00f4a 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -74,6 +74,9 @@ export class Agent extends BaseAge dependencyManager.registerInstance(InjectionSymbols.Stop$, new Subject()) dependencyManager.registerInstance(InjectionSymbols.FileSystem, new agentConfig.agentDependencies.FileSystem()) + // Register all modules. This will also include the default modules + dependencyManager.registerModules(modulesWithDefaultModules) + // Register possibly already defined services if (!dependencyManager.isRegistered(InjectionSymbols.Wallet)) { dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndyWallet) @@ -88,9 +91,6 @@ export class Agent extends BaseAge dependencyManager.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) } - // Register all modules. This will also include the default modules - dependencyManager.registerModules(modulesWithDefaultModules) - // TODO: contextCorrelationId for base wallet // Bind the default agent context to the container for use in modules etc. dependencyManager.registerInstance( diff --git a/packages/core/src/storage/FileSystem.ts b/packages/core/src/storage/FileSystem.ts index 6673bc333c..b724e68158 100644 --- a/packages/core/src/storage/FileSystem.ts +++ b/packages/core/src/storage/FileSystem.ts @@ -2,6 +2,7 @@ export interface FileSystem { readonly basePath: string exists(path: string): Promise + createDirectory(path: string): Promise write(path: string, data: string): Promise read(path: string): Promise downloadToFile(url: string, path: string): Promise diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index d2f5a21c8f..b454c7963e 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -13,14 +13,16 @@ export enum KeyDerivationMethod { Raw = 'RAW', } +export interface WalletStorageConfig { + type: string + [key: string]: unknown +} + export interface WalletConfig { id: string key: string keyDerivationMethod?: KeyDerivationMethod - storage?: { - type: string - [key: string]: unknown - } + storage?: WalletStorageConfig masterSecretId?: string } diff --git a/packages/core/src/utils/TypedArrayEncoder.ts b/packages/core/src/utils/TypedArrayEncoder.ts index 685eac485c..83ee5d89ca 100644 --- a/packages/core/src/utils/TypedArrayEncoder.ts +++ b/packages/core/src/utils/TypedArrayEncoder.ts @@ -17,7 +17,7 @@ export class TypedArrayEncoder { * * @param buffer the buffer to encode into base64url string */ - public static toBase64URL(buffer: Buffer) { + public static toBase64URL(buffer: Buffer | Uint8Array) { return base64ToBase64URL(TypedArrayEncoder.toBase64(buffer)) } diff --git a/packages/node/src/NodeFileSystem.ts b/packages/node/src/NodeFileSystem.ts index f739c40814..240440d64c 100644 --- a/packages/node/src/NodeFileSystem.ts +++ b/packages/node/src/NodeFileSystem.ts @@ -29,6 +29,10 @@ export class NodeFileSystem implements FileSystem { } } + public async createDirectory(path: string): Promise { + await promises.mkdir(dirname(path), { recursive: true }) + } + public async write(path: string, data: string): Promise { // Make sure parent directories exist await promises.mkdir(dirname(path), { recursive: true }) diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index 331fa11a54..0eaab55429 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -21,6 +21,10 @@ export class ReactNativeFileSystem implements FileSystem { return RNFS.exists(path) } + public async createDirectory(path: string): Promise { + await RNFS.mkdir(getDirFromFilePath(path)) + } + public async write(path: string, data: string): Promise { // Make sure parent directories exist await RNFS.mkdir(getDirFromFilePath(path)) diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts new file mode 100644 index 0000000000..b7d4233738 --- /dev/null +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -0,0 +1,135 @@ +import type { SubjectMessage } from './transport/SubjectInboundTransport' + +import { Subject } from 'rxjs' + +import { getAgentOptions, makeConnection, waitForBasicMessage } from '../packages/core/tests/helpers' + +import { AskarModule } from '@aries-framework/askar' +import { Agent, DependencyManager, InjectionSymbols } from '@aries-framework/core' +import { IndySdkModule, IndySdkStorageService, IndySdkWallet } from '@aries-framework/indy-sdk' + +import { SubjectInboundTransport } from './transport/SubjectInboundTransport' + +import { agentDependencies } from '@aries-framework/node' + +import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' + +// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved +describe.skip('E2E Askar-Indy SDK Wallet Subject tests', () => { + let recipientAgent: Agent + let senderAgent: Agent + + afterEach(async () => { + if (recipientAgent) { + await recipientAgent.shutdown() + await recipientAgent.wallet.delete() + } + + if (senderAgent) { + await senderAgent.shutdown() + await senderAgent.wallet.delete() + } + }) + + test('Wallet Subject flow - Indy Sender / Askar Receiver ', async () => { + // Sender is an Agent using Indy SDK Wallet + const senderDependencyManager = new DependencyManager() + senderDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + senderDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) + senderAgent = new Agent( + { + ...getAgentOptions('E2E Wallet Subject Sender Indy', { endpoints: ['rxjs:sender'] }), + modules: { indySdk: new IndySdkModule({ indySdk: agentDependencies.indy }) }, + }, + senderDependencyManager + ) + + // Recipient is an Agent using Askar Wallet + recipientAgent = new Agent({ + ...getAgentOptions('E2E Wallet Subject Recipient Askar', { endpoints: ['rxjs:recipient'] }), + modules: { askar: new AskarModule() }, + }) + + await e2eWalletTest(senderAgent, recipientAgent) + }) + + test('Wallet Subject flow - Askar Sender / Askar Recipient ', async () => { + // Sender is an Agent using Askar Wallet + senderAgent = new Agent({ + ...getAgentOptions('E2E Wallet Subject Sender Askar', { endpoints: ['rxjs:sender'] }), + modules: { askar: new AskarModule() }, + }) + + // Recipient is an Agent using Askar Wallet + recipientAgent = new Agent({ + ...getAgentOptions('E2E Wallet Subject Recipient Askar', { endpoints: ['rxjs:recipient'] }), + modules: { askar: new AskarModule() }, + }) + + await e2eWalletTest(senderAgent, recipientAgent) + }) + + test('Wallet Subject flow - Indy Sender / Indy Recipient ', async () => { + // Sender is an Agent using Indy SDK Wallet + const senderDependencyManager = new DependencyManager() + senderDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + senderDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) + senderAgent = new Agent( + { + ...getAgentOptions('E2E Wallet Subject Sender Indy', { endpoints: ['rxjs:sender'] }), + modules: { indySdk: new IndySdkModule({ indySdk: agentDependencies.indy }) }, + }, + senderDependencyManager + ) + + // Recipient is an Agent using Indy Wallet + const recipientDependencyManager = new DependencyManager() + recipientDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + recipientDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) + recipientAgent = new Agent( + { + ...getAgentOptions('E2E Wallet Subject Recipient Indy', { endpoints: ['rxjs:recipient'] }), + modules: { indySdk: new IndySdkModule({ indySdk: agentDependencies.indy }) }, + }, + recipientDependencyManager + ) + + await e2eWalletTest(senderAgent, recipientAgent) + }) +}) + +export async function e2eWalletTest(senderAgent: Agent, recipientAgent: Agent) { + const recipientMessages = new Subject() + const senderMessages = new Subject() + + const subjectMap = { + 'rxjs:recipient': recipientMessages, + 'rxjs:sender': senderMessages, + } + + // Recipient Setup + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) + await recipientAgent.initialize() + + // Sender Setup + senderAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) + await senderAgent.initialize() + + // Make connection between sender and recipient + const [recipientSenderConnection, senderRecipientConnection] = await makeConnection(recipientAgent, senderAgent) + expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) + + // Sender sends a basic message and Recipient waits for it + await senderAgent.basicMessages.sendMessage(senderRecipientConnection.id, 'Hello') + await waitForBasicMessage(recipientAgent, { + content: 'Hello', + }) + + // Recipient sends a basic message and Sender waits for it + await recipientAgent.basicMessages.sendMessage(recipientSenderConnection.id, 'How are you?') + await waitForBasicMessage(senderAgent, { + content: 'How are you?', + }) +} diff --git a/yarn.lock b/yarn.lock index 3fe3c222e0..4a8447c5fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -858,6 +858,26 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== +"@hyperledger/aries-askar-nodejs@^0.1.0-dev.1": + version "0.1.0-dev.1" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.1.tgz#b384d422de48f0ce5918e1612d2ca32ebd160520" + integrity sha512-XrRskQ0PaNAerItvfxKkS8YaVg+iuImguoqfyQ4ZSaePKZQnTqZpkxo6faKS+GlsaubRXz/6yz3YndVRIxPO+w== + dependencies: + "@hyperledger/aries-askar-shared" "0.1.0-dev.1" + "@mapbox/node-pre-gyp" "^1.0.10" + ffi-napi "^4.0.3" + node-cache "^5.1.2" + ref-array-di "^1.2.2" + ref-napi "^3.0.3" + ref-struct-di "^1.1.1" + +"@hyperledger/aries-askar-shared@0.1.0-dev.1", "@hyperledger/aries-askar-shared@^0.1.0-dev.1": + version "0.1.0-dev.1" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.1.tgz#4e4e494c3a44c7c82f7b95ad4f06149f2a3a9b6c" + integrity sha512-Pt525M6CvnE3N6jxMpSqLy7RpOsc4oqa2Q+hc2UdCHuSYwmM/aeqt6wiA5dpghvl8g/78lCi1Dz74pzp7Dmm3w== + dependencies: + fast-text-encoding "^1.0.3" + "@hyperledger/indy-vdr-nodejs@^0.1.0-dev.4": version "0.1.0-dev.4" resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.4.tgz#b5d2090b30c4a51e4e4f15a024054aada0d3550e" @@ -3917,6 +3937,11 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +clone@2.x: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== + clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -8392,6 +8417,13 @@ node-addon-api@^3.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== +node-cache@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" + integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== + dependencies: + clone "2.x" + node-dir@^0.1.17: version "0.1.17" resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" From d056316712b5ee5c42a159816b5dda0b05ad84a8 Mon Sep 17 00:00:00 2001 From: Victor Anene <62852943+Vickysomtee@users.noreply.github.com> Date: Fri, 10 Feb 2023 01:31:43 +0100 Subject: [PATCH 028/139] feat(indy-vdr): add IndyVdrAnonCredsRegistry (#1270) Signed-off-by: Timo Glastra --- packages/indy-sdk/tests/setup.ts | 2 +- packages/indy-vdr/package.json | 1 + .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 431 ++++++++++++++++++ .../utils/_tests_/identifier.test.ts | 52 +++ .../src/anoncreds/utils/identifiers.ts | 42 ++ .../indy-vdr-anoncreds-registry.e2e.test.ts | 190 ++++++++ .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 168 ++++--- 7 files changed, 800 insertions(+), 86 deletions(-) create mode 100644 packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts create mode 100644 packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts create mode 100644 packages/indy-vdr/src/anoncreds/utils/identifiers.ts create mode 100644 packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts diff --git a/packages/indy-sdk/tests/setup.ts b/packages/indy-sdk/tests/setup.ts index 719a473b6e..b60b932be5 100644 --- a/packages/indy-sdk/tests/setup.ts +++ b/packages/indy-sdk/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(10000) +jest.setTimeout(25000) diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index e12d0116de..e73cfd7a83 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -25,6 +25,7 @@ "test": "jest" }, "dependencies": { + "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", "@hyperledger/indy-vdr-shared": "^0.1.0-dev.4" }, diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts new file mode 100644 index 0000000000..1106b498cd --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -0,0 +1,431 @@ +import type { + AnonCredsRegistry, + GetCredentialDefinitionReturn, + GetSchemaReturn, + RegisterSchemaOptions, + RegisterCredentialDefinitionOptions, + RegisterSchemaReturn, + RegisterCredentialDefinitionReturn, + GetRevocationStatusListReturn, + GetRevocationRegistryDefinitionReturn, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' + +import { DidsApi, getKeyDidMappingByVerificationMethod } from '@aries-framework/core' +import { + GetSchemaRequest, + SchemaRequest, + GetCredentialDefinitionRequest, + CredentialDefinitionRequest, +} from '@hyperledger/indy-vdr-shared' + +import { IndyVdrPoolService } from '../pool' + +import { + didFromSchemaId, + didFromCredentialDefinitionId, + getLegacySchemaId, + getLegacyCredentialDefinitionId, + indyVdrAnonCredsRegistryIdentifierRegex, +} from './utils/identifiers' + +export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { + public readonly supportedIdentifier = indyVdrAnonCredsRegistryIdentifierRegex + + public async getSchema(agentContext: AgentContext, schemaId: string): Promise { + try { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const did = didFromSchemaId(schemaId) + + const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.indyNamespace}'`) + const request = new GetSchemaRequest({ submitterDid: did, schemaId }) + + agentContext.config.logger.trace( + `Submitting get schema request for schema '${schemaId}' to ledger '${pool.indyNamespace}'` + ) + const response = await pool.submitReadRequest(request) + + agentContext.config.logger.trace(`Got un-parsed schema '${schemaId}' from ledger '${pool.indyNamespace}'`, { + response, + }) + + const issuerId = didFromSchemaId(schemaId) + + if ('attr_names' in response.result.data) { + return { + schema: { + attrNames: response.result.data.attr_names, + name: response.result.data.name, + version: response.result.data.version, + issuerId, + }, + schemaId: schemaId, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: pool.indyNamespace, + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo: response.result.seqNo, + }, + } + } + + agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`) + + return { + schemaId, + resolutionMetadata: { + error: 'notFound', + message: `unable to find schema with id ${schemaId}`, + }, + schemaMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`, { + error, + schemaId, + }) + + return { + schemaId, + resolutionMetadata: { + error: 'notFound', + }, + schemaMetadata: {}, + } + } + } + + public async registerSchema( + agentContext: AgentContext, + options: IndyVdrRegisterSchemaOptions + ): Promise { + if (!options.options.didIndyNamespace) { + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy VDR', + schema: options.schema, + state: 'failed', + }, + } + } + + try { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const schemaRequest = new SchemaRequest({ + submitterDid: options.schema.issuerId, + schema: { + id: getLegacySchemaId(options.schema.issuerId, options.schema.name, options.schema.version), + name: options.schema.name, + ver: '1.0', + version: options.schema.version, + attrNames: options.schema.attrNames, + }, + }) + + const pool = indyVdrPoolService.getPoolForNamespace(options.options.didIndyNamespace) + + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(`did:sov:${options.schema.issuerId}`) + + if (!didResult.didDocument) { + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + schema: options.schema, + state: 'failed', + reason: `didNotFound: unable to resolve did did:sov:${options.schema.issuerId}: ${didResult.didResolutionMetadata.message}`, + }, + } + } + + const verificationMethod = didResult.didDocument.dereferenceKey(`did:sov:${options.schema.issuerId}#key-1`) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const key = getKeyFromVerificationMethod(verificationMethod) + + const response = await pool.submitWriteRequest(agentContext, schemaRequest, key) + + return { + schemaState: { + state: 'finished', + schema: { + attrNames: options.schema.attrNames, + issuerId: options.schema.issuerId, + name: options.schema.name, + version: options.schema.version, + }, + schemaId: getLegacySchemaId(options.schema.issuerId, options.schema.name, options.schema.version), + }, + registrationMetadata: {}, + schemaMetadata: { + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo: response.result.txnMetadata.seqNo, + didIndyNamespace: pool.indyNamespace, + }, + } + } catch (error) { + agentContext.config.logger.error(`Error registering schema for did '${options.schema.issuerId}'`, { + error, + did: options.schema.issuerId, + schema: options.schema, + }) + + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + state: 'failed', + schema: options.schema, + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async getCredentialDefinition( + agentContext: AgentContext, + credentialDefinitionId: string + ): Promise { + try { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const did = didFromCredentialDefinitionId(credentialDefinitionId) + + const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug( + `Getting credential definition '${credentialDefinitionId}' from ledger '${pool.indyNamespace}'` + ) + + const request = new GetCredentialDefinitionRequest({ + submitterDid: did, + credentialDefinitionId, + }) + + agentContext.config.logger.trace( + `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.indyNamespace}'` + ) + + const response = await pool.submitReadRequest(request) + + if (response.result.data) { + return { + credentialDefinitionId: credentialDefinitionId, + credentialDefinition: { + issuerId: didFromCredentialDefinitionId(credentialDefinitionId), + schemaId: response.result.ref.toString(), + tag: response.result.tag, + type: 'CL', + value: response.result.data, + }, + credentialDefinitionMetadata: { + didIndyNamespace: pool.indyNamespace, + }, + resolutionMetadata: {}, + } + } + + agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`) + + return { + credentialDefinitionId, + credentialDefinitionMetadata: {}, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition with id ${credentialDefinitionId}`, + }, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { + error, + credentialDefinitionId, + }) + + return { + credentialDefinitionId, + credentialDefinitionMetadata: {}, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition: ${error.message}`, + }, + } + } + } + + public async registerCredentialDefinition( + agentContext: AgentContext, + options: IndyVdrRegisterCredentialDefinitionOptions + ): Promise { + // Make sure didIndyNamespace is passed + if (!options.options.didIndyNamespace) { + return { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy SDK', + credentialDefinition: options.credentialDefinition, + state: 'failed', + }, + } + } + + try { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const pool = indyVdrPoolService.getPoolForNamespace(options.options.didIndyNamespace) + + const { schema, schemaMetadata, resolutionMetadata } = await this.getSchema( + agentContext, + options.credentialDefinition.schemaId + ) + + if (!schema || !schemaMetadata.indyLedgerSeqNo || typeof schemaMetadata.indyLedgerSeqNo !== 'number') { + return { + registrationMetadata: {}, + credentialDefinitionMetadata: { + didIndyNamespace: pool.indyNamespace, + }, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + state: 'failed', + reason: `error resolving schema with id ${options.credentialDefinition.schemaId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, + }, + } + } + + const credentialDefinitionId = getLegacyCredentialDefinitionId( + options.credentialDefinition.issuerId, + schemaMetadata.indyLedgerSeqNo, + options.credentialDefinition.tag + ) + + const credentialDefinitionRequest = new CredentialDefinitionRequest({ + submitterDid: options.credentialDefinition.issuerId, + credentialDefinition: { + ver: '1.0', + id: credentialDefinitionId, + schemaId: `${schemaMetadata.indyLedgerSeqNo}`, + type: 'CL', + tag: options.credentialDefinition.tag, + value: { + primary: options.credentialDefinition.value, + }, + }, + }) + + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(`did:sov:${options.credentialDefinition.issuerId}`) + + if (!didResult.didDocument) { + return { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + state: 'failed', + reason: `didNotFound: unable to resolve did did:sov${options.credentialDefinition.issuerId}: ${didResult.didResolutionMetadata.message}`, + }, + } + } + + const verificationMethod = didResult.didDocument.dereferenceKey( + `did:sov:${options.credentialDefinition.issuerId}#key-1` + ) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const key = getKeyFromVerificationMethod(verificationMethod) + + const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, key) + + agentContext.config.logger.debug( + `Registered credential definition '${credentialDefinitionId}' on ledger '${pool.indyNamespace}'`, + { + response, + credentialDefinition: options.credentialDefinition, + } + ) + + return { + credentialDefinitionMetadata: { + didIndyNamespace: pool.indyNamespace, + }, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + credentialDefinitionId, + state: 'finished', + }, + registrationMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error( + `Error registering credential definition for schema '${options.credentialDefinition.schemaId}'`, + { + error, + did: options.credentialDefinition.issuerId, + credentialDefinition: options.credentialDefinition, + } + ) + + return { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async getRevocationStatusList( + agentContext: AgentContext, + revocationRegistryId: string, + timestamp: number + ): Promise { + return { + resolutionMetadata: { + error: 'Not Implemented', + message: `Revocation list not yet implemented `, + }, + revocationStatusListMetadata: {}, + } + } + + public async getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise { + return { + resolutionMetadata: { + error: 'Not Implemented', + message: `Revocation registry definition not yet implemented`, + }, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } + } +} + +export interface IndyVdrRegisterSchemaOptions extends RegisterSchemaOptions { + options: { + didIndyNamespace: string + } +} + +export interface IndyVdrRegisterCredentialDefinitionOptions extends RegisterCredentialDefinitionOptions { + options: { + didIndyNamespace: string + } +} diff --git a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts new file mode 100644 index 0000000000..62528a0075 --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts @@ -0,0 +1,52 @@ +import { + getLegacySchemaId, + getLegacyCredentialDefinitionId, + didFromSchemaId, + didFromCredentialDefinitionId, + indyVdrAnonCredsRegistryIdentifierRegex, +} from '../identifiers' + +describe('identifiers', () => { + it('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { + const did = '7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' + const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' + const revocationRegistryId = + 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + + const anotherId = 'some:id' + + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + + it('getLegacySchemaId should return a valid schema Id', () => { + const did = '29347' + const name = 'starlinks' + const version = '321' + + expect(getLegacySchemaId(did, name, version)).toEqual(`29347:2:starlinks:321`) + }) + + it('getLegacyCredentialDefinition should return a valid Credential Id', () => { + const did = '15565' + const seqNo = 323 + const tag = 'indyTag' + expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('15565:3:CL:323:indyTag') + }) + + it('didFromSchemaId should return the valid did from the schema', () => { + const schemaId = '29347:2:starlinks:321' + + expect(didFromSchemaId(schemaId)).toEqual('29347') + }) + + it('didFromCredentialId should return the valid did from the schema', () => { + const credentialDefinitionId = '15565:3:CL:323:indyTag' + + expect(didFromCredentialDefinitionId(credentialDefinitionId)).toEqual('15565') + }) +}) diff --git a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts new file mode 100644 index 0000000000..d242ca3461 --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts @@ -0,0 +1,42 @@ +export const legacyIndyVdrIssuerIdRegex = /^[a-zA-Z0-9]{21,22}$/ +export const legacyIndyVdrSchemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ +export const legacyIndyVdrCredentialDefinitionIdRegex = + /^[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ +export const legacyIndyVdrRevocationRegistryIdRegex = + /^[a-zA-Z0-9]{21,22}:4:[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)/ + +export const indyVdrAnonCredsRegistryIdentifierRegex = new RegExp( + `${legacyIndyVdrIssuerIdRegex.source}|${legacyIndyVdrSchemaIdRegex.source}|${legacyIndyVdrCredentialDefinitionIdRegex.source}|${legacyIndyVdrRevocationRegistryIdRegex.source}` +) + +export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { + return `${unqualifiedDid}:2:${name}:${version}` +} + +export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: number, tag: string) { + return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` +} + +/** + * Extract did from schema id + */ +export function didFromSchemaId(schemaId: string) { + const [did] = schemaId.split(':') + + return did +} + +/** + * Extract did from credential definition id + */ +export function didFromCredentialDefinitionId(credentialDefinitionId: string) { + const [did] = credentialDefinitionId.split(':') + + return did +} + +export function didFromRevocationRegistryDefinitionId(revocationRegistryId: string) { + const [did] = revocationRegistryId.split(':') + + return did +} diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts new file mode 100644 index 0000000000..9d214ea43d --- /dev/null +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -0,0 +1,190 @@ +import { Agent } from '@aries-framework/core' + +import { agentDependencies, genesisTransactions, getAgentConfig } from '../../core/tests/helpers' +import { IndyVdrAnonCredsRegistry } from '../src/anoncreds/IndyVdrAnonCredsRegistry' +import { IndyVdrPoolService } from '../src/pool' + +const agentConfig = getAgentConfig('IndyVdrAnonCredsRegistry') + +// TODO: update to module once available +const indyVdrPoolService = new IndyVdrPoolService(agentConfig.logger) +indyVdrPoolService.setPools([ + { + genesisTransactions, + indyNamespace: 'local:test', + isProduction: false, + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, +]) + +const indyVdrAnonCredsRegistry = new IndyVdrAnonCredsRegistry() + +const agent = new Agent({ + config: agentConfig, + dependencies: agentDependencies, +}) + +agent.dependencyManager.registerInstance(IndyVdrPoolService, indyVdrPoolService) + +describe('IndyVdrAnonCredsRegistry', () => { + beforeAll(async () => { + await agent.initialize() + await indyVdrPoolService.connectToPools() + }) + + afterAll(async () => { + for (const pool of indyVdrPoolService.pools) { + pool.close() + } + + await agent.shutdown() + await agent.wallet.delete() + }) + + // One test as the credential definition depends on the schema + test('register and resolve a schema and credential definition', async () => { + const dynamicVersion = `1.${Math.random() * 100}` + + const schemaResult = await indyVdrAnonCredsRegistry.registerSchema(agent.context, { + options: { + didIndyNamespace: 'local:test', + }, + schema: { + attrNames: ['age'], + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + name: 'test', + version: dynamicVersion, + }, + }) + + expect(schemaResult).toMatchObject({ + schemaState: { + state: 'finished', + schema: { + attrNames: ['age'], + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + name: 'test', + version: dynamicVersion, + }, + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + }, + registrationMetadata: {}, + schemaMetadata: { + indyLedgerSeqNo: expect.any(Number), + didIndyNamespace: 'local:test', + }, + }) + + const schemaResponse = await indyVdrAnonCredsRegistry.getSchema( + agent.context, + schemaResult.schemaState.schemaId as string + ) + expect(schemaResponse).toMatchObject({ + schema: { + attrNames: ['age'], + name: 'test', + version: dynamicVersion, + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + }, + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: 'local:test', + indyLedgerSeqNo: expect.any(Number), + }, + }) + + const credentialDefinitionResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(agent.context, { + credentialDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + tag: 'TAG', + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + options: { + didIndyNamespace: 'local:test', + }, + }) + + expect(credentialDefinitionResult).toMatchObject({ + credentialDefinitionMetadata: { + didIndyNamespace: 'local:test', + }, + credentialDefinitionState: { + credentialDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + tag: 'TAG', + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + state: 'finished', + }, + registrationMetadata: {}, + }) + + const credentialDefinitionResponse = await indyVdrAnonCredsRegistry.getCredentialDefinition( + agent.context, + credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId as string + ) + + expect(credentialDefinitionResponse).toMatchObject({ + credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + credentialDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + // FIXME: this will change when https://github.com/hyperledger/aries-framework-javascript/issues/1259 is merged + schemaId: `${schemaResponse.schemaMetadata.indyLedgerSeqNo}`, + tag: 'TAG', + type: 'CL', + value: { + primary: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'local:test', + }, + resolutionMetadata: {}, + }) + }) +}) diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index 52bd467cd5..95a3882ff1 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -1,12 +1,13 @@ import type { Key } from '@aries-framework/core' -import { IndyWallet, KeyType, SigningProviderRegistry, TypedArrayEncoder } from '@aries-framework/core' +import { IndyWallet, KeyType, SigningProviderRegistry } from '@aries-framework/core' import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from '@hyperledger/indy-vdr-shared' import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' import { IndyVdrPool } from '../src/pool' import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' +import { indyDidFromPublicKeyBase58 } from '../src/utils/did' const indyVdrPoolService = new IndyVdrPoolService(testLogger) const wallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) @@ -88,8 +89,7 @@ describe('IndyVdrPoolService', () => { // prepare the DID we are going to write to the ledger const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) - const buffer = TypedArrayEncoder.fromBase58(key.publicKeyBase58) - const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + const did = indyDidFromPublicKeyBase58(key.publicKeyBase58) const request = new NymRequest({ dest: did, @@ -116,108 +116,106 @@ describe('IndyVdrPoolService', () => { }) }) - describe('Schemas & credential Definition', () => { - test('can write a schema using the pool', async () => { - const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') + test('can write a schema and credential definition using the pool', async () => { + const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') - const dynamicVersion = `1.${Math.random() * 100}` + const dynamicVersion = `1.${Math.random() * 100}` - const schemaRequest = new SchemaRequest({ - submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', - schema: { - id: 'test-schema-id', - name: 'test-schema', - ver: '1.0', - version: dynamicVersion, - attrNames: ['first_name', 'last_name', 'age'], - }, - }) + const schemaRequest = new SchemaRequest({ + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + schema: { + id: 'test-schema-id', + name: 'test-schema', + ver: '1.0', + version: dynamicVersion, + attrNames: ['first_name', 'last_name', 'age'], + }, + }) - const schemaResponse = await pool.submitWriteRequest(agentContext, schemaRequest, signerKey) + const schemaResponse = await pool.submitWriteRequest(agentContext, schemaRequest, signerKey) - expect(schemaResponse).toMatchObject({ - op: 'REPLY', - result: { - ver: '1', - txn: { - metadata: expect.any(Object), - type: '101', + expect(schemaResponse).toMatchObject({ + op: 'REPLY', + result: { + ver: '1', + txn: { + metadata: expect.any(Object), + type: '101', + data: { data: { - data: { - attr_names: expect.arrayContaining(['age', 'last_name', 'first_name']), - name: 'test-schema', - version: dynamicVersion, - }, + attr_names: expect.arrayContaining(['age', 'last_name', 'first_name']), + name: 'test-schema', + version: dynamicVersion, }, }, }, - }) + }, + }) - const credentialDefinitionRequest = new CredentialDefinitionRequest({ - submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', - credentialDefinition: { - ver: '1.0', - id: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.result.txnMetadata.seqNo}:TAG`, - // must be string version of the schema seqNo - schemaId: `${schemaResponse.result.txnMetadata.seqNo}`, - type: 'CL', - tag: 'TAG', - value: { - primary: { - n: '95671911213029889766246243339609567053285242961853979532076192834533577534909796042025401129640348836502648821408485216223269830089771714177855160978214805993386076928594836829216646288195127289421136294309746871614765411402917891972999085287429566166932354413679994469616357622976775651506242447852304853465380257226445481515631782793575184420720296120464167257703633829902427169144462981949944348928086406211627174233811365419264314148304536534528344413738913277713548403058098093453580992173145127632199215550027527631259565822872315784889212327945030315062879193999012349220118290071491899498795367403447663354833', - s: '1573939820553851804028472930351082111827449763317396231059458630252708273163050576299697385049087601314071156646675105028237105229428440185022593174121924731226634356276616495327358864865629675802738680754755949997611920669823449540027707876555408118172529688443208301403297680159171306000341239398135896274940688268460793682007115152428685521865921925309154307574955324973580144009271977076586453011938089159885164705002797196738438392179082905738155386545935208094240038135576042886730802817809757582039362798495805441520744154270346780731494125065136433163757697326955962282840631850597919384092584727207908978907', - r: { - master_secret: - '51468326064458249697956272807708948542001661888325200180968238787091473418947480867518174106588127385097619219536294589148765074804124925845579871788369264160902401097166484002617399484700234182426993061977152961670486891123188739266793651668791365808983166555735631354925174224786218771453042042304773095663181121735652667614424198057134974727791329623974680096491276337756445057223988781749506082654194307092164895251308088903000573135447235553684949564809677864522417041639512933806794232354223826262154508950271949764583849083972967642587778197779127063591201123312548182885603427440981731822883101260509710567731', - last_name: - '35864556460959997092903171610228165251001245539613587319116151716453114432309327039517115215674024166920383445379522674504803469517283236033110568676156285676664363558333716898161685255450536856645604857714925836474250821415182026707218622134953915013803750771185050002646661004119778318524426368842019753903741998256374803456282688037624993010626333853831264356355867746685055670790915539230702546586615988121383960277550317876816983602795121749533628953449405383896799464872758725899520173321672584180060465965090049734285011738428381648013150818429882628144544132356242262467090140003979917439514443707537952643217', - first_name: - '26405366527417391838431479783966663952336302347775179063968690502492620867161212873635806190080000833725932174641667734138216137047349915190546601368424742647800764149890590518336588437317392528514313749533980651547425554257026971104775208127915118918084350210726664749850578299247705298976657301433446491575776774836993110356033664644761593799921221474617858131678955318702706530853801195330271860527250931569815553226145458665481867408279941785848264018364216087471931232367137301987457054918438087686484522112532447779498424748261678616461026788516567300969886029412198319909977473167405879110243445062391837349387', - age: '19865805272519696320755573045337531955436490760876870776207490804137339344112305203631892390827288264857621916650098902064979838987400911652887344763586495880167030031364467726355103327059673023946234460960685398768709062405377107912774045508870580108596597470880834205563197111550140867466625683117333370595295321833757429488192170551320637065066368716366317421169802474954914904380304190861641082310805418122837214965865969459724848071006870574514215255412289237027267424055400593307112849859757094597401668252862525566316402695830217450073667487951799749275437192883439584518905943435472478496028380016245355151988', - }, - rctxt: - '17146114573198643698878017247599007910707723139165264508694101989891626297408755744139587708989465136799243292477223763665064840330721616213638280284119891715514951989022398510785960099708705561761504012512387129498731093386014964896897751536856287377064154297370092339714578039195258061017640952790913108285519632654466006255438773382930416822756630391947263044087385305540191237328903426888518439803354213792647775798033294505898635058814132665832000734168261793545453678083703704122695006541391598116359796491845268631009298069826949515604008666680160398698425061157356267086946953480945396595351944425658076127674', - z: '57056568014385132434061065334124327103768023932445648883765905576432733866307137325457775876741578717650388638737098805750938053855430851133826479968450532729423746605371536096355616166421996729493639634413002114547787617999178137950004782677177313856876420539744625174205603354705595789330008560775613287118432593300023801651460885523314713996258581986238928077688246511704050386525431448517516821261983193275502089060128363906909778842476516981025598807378338053788433033754999771876361716562378445777250912525673660842724168260417083076824975992327559199634032439358787956784395443246565622469187082767614421691234', + const credentialDefinitionRequest = new CredentialDefinitionRequest({ + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + credentialDefinition: { + ver: '1.0', + id: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.result.txnMetadata.seqNo}:TAG`, + // must be string version of the schema seqNo + schemaId: `${schemaResponse.result.txnMetadata.seqNo}`, + type: 'CL', + tag: 'TAG', + value: { + primary: { + n: '95671911213029889766246243339609567053285242961853979532076192834533577534909796042025401129640348836502648821408485216223269830089771714177855160978214805993386076928594836829216646288195127289421136294309746871614765411402917891972999085287429566166932354413679994469616357622976775651506242447852304853465380257226445481515631782793575184420720296120464167257703633829902427169144462981949944348928086406211627174233811365419264314148304536534528344413738913277713548403058098093453580992173145127632199215550027527631259565822872315784889212327945030315062879193999012349220118290071491899498795367403447663354833', + s: '1573939820553851804028472930351082111827449763317396231059458630252708273163050576299697385049087601314071156646675105028237105229428440185022593174121924731226634356276616495327358864865629675802738680754755949997611920669823449540027707876555408118172529688443208301403297680159171306000341239398135896274940688268460793682007115152428685521865921925309154307574955324973580144009271977076586453011938089159885164705002797196738438392179082905738155386545935208094240038135576042886730802817809757582039362798495805441520744154270346780731494125065136433163757697326955962282840631850597919384092584727207908978907', + r: { + master_secret: + '51468326064458249697956272807708948542001661888325200180968238787091473418947480867518174106588127385097619219536294589148765074804124925845579871788369264160902401097166484002617399484700234182426993061977152961670486891123188739266793651668791365808983166555735631354925174224786218771453042042304773095663181121735652667614424198057134974727791329623974680096491276337756445057223988781749506082654194307092164895251308088903000573135447235553684949564809677864522417041639512933806794232354223826262154508950271949764583849083972967642587778197779127063591201123312548182885603427440981731822883101260509710567731', + last_name: + '35864556460959997092903171610228165251001245539613587319116151716453114432309327039517115215674024166920383445379522674504803469517283236033110568676156285676664363558333716898161685255450536856645604857714925836474250821415182026707218622134953915013803750771185050002646661004119778318524426368842019753903741998256374803456282688037624993010626333853831264356355867746685055670790915539230702546586615988121383960277550317876816983602795121749533628953449405383896799464872758725899520173321672584180060465965090049734285011738428381648013150818429882628144544132356242262467090140003979917439514443707537952643217', + first_name: + '26405366527417391838431479783966663952336302347775179063968690502492620867161212873635806190080000833725932174641667734138216137047349915190546601368424742647800764149890590518336588437317392528514313749533980651547425554257026971104775208127915118918084350210726664749850578299247705298976657301433446491575776774836993110356033664644761593799921221474617858131678955318702706530853801195330271860527250931569815553226145458665481867408279941785848264018364216087471931232367137301987457054918438087686484522112532447779498424748261678616461026788516567300969886029412198319909977473167405879110243445062391837349387', + age: '19865805272519696320755573045337531955436490760876870776207490804137339344112305203631892390827288264857621916650098902064979838987400911652887344763586495880167030031364467726355103327059673023946234460960685398768709062405377107912774045508870580108596597470880834205563197111550140867466625683117333370595295321833757429488192170551320637065066368716366317421169802474954914904380304190861641082310805418122837214965865969459724848071006870574514215255412289237027267424055400593307112849859757094597401668252862525566316402695830217450073667487951799749275437192883439584518905943435472478496028380016245355151988', }, + rctxt: + '17146114573198643698878017247599007910707723139165264508694101989891626297408755744139587708989465136799243292477223763665064840330721616213638280284119891715514951989022398510785960099708705561761504012512387129498731093386014964896897751536856287377064154297370092339714578039195258061017640952790913108285519632654466006255438773382930416822756630391947263044087385305540191237328903426888518439803354213792647775798033294505898635058814132665832000734168261793545453678083703704122695006541391598116359796491845268631009298069826949515604008666680160398698425061157356267086946953480945396595351944425658076127674', + z: '57056568014385132434061065334124327103768023932445648883765905576432733866307137325457775876741578717650388638737098805750938053855430851133826479968450532729423746605371536096355616166421996729493639634413002114547787617999178137950004782677177313856876420539744625174205603354705595789330008560775613287118432593300023801651460885523314713996258581986238928077688246511704050386525431448517516821261983193275502089060128363906909778842476516981025598807378338053788433033754999771876361716562378445777250912525673660842724168260417083076824975992327559199634032439358787956784395443246565622469187082767614421691234', }, }, - }) + }, + }) - const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, signerKey) + const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, signerKey) - expect(response).toMatchObject({ - op: 'REPLY', - result: { - ver: '1', - txn: { - metadata: expect.any(Object), - type: '102', + expect(response).toMatchObject({ + op: 'REPLY', + result: { + ver: '1', + txn: { + metadata: expect.any(Object), + type: '102', + data: { data: { - data: { - primary: { - r: { - last_name: - '35864556460959997092903171610228165251001245539613587319116151716453114432309327039517115215674024166920383445379522674504803469517283236033110568676156285676664363558333716898161685255450536856645604857714925836474250821415182026707218622134953915013803750771185050002646661004119778318524426368842019753903741998256374803456282688037624993010626333853831264356355867746685055670790915539230702546586615988121383960277550317876816983602795121749533628953449405383896799464872758725899520173321672584180060465965090049734285011738428381648013150818429882628144544132356242262467090140003979917439514443707537952643217', - first_name: - '26405366527417391838431479783966663952336302347775179063968690502492620867161212873635806190080000833725932174641667734138216137047349915190546601368424742647800764149890590518336588437317392528514313749533980651547425554257026971104775208127915118918084350210726664749850578299247705298976657301433446491575776774836993110356033664644761593799921221474617858131678955318702706530853801195330271860527250931569815553226145458665481867408279941785848264018364216087471931232367137301987457054918438087686484522112532447779498424748261678616461026788516567300969886029412198319909977473167405879110243445062391837349387', - age: '19865805272519696320755573045337531955436490760876870776207490804137339344112305203631892390827288264857621916650098902064979838987400911652887344763586495880167030031364467726355103327059673023946234460960685398768709062405377107912774045508870580108596597470880834205563197111550140867466625683117333370595295321833757429488192170551320637065066368716366317421169802474954914904380304190861641082310805418122837214965865969459724848071006870574514215255412289237027267424055400593307112849859757094597401668252862525566316402695830217450073667487951799749275437192883439584518905943435472478496028380016245355151988', - master_secret: - '51468326064458249697956272807708948542001661888325200180968238787091473418947480867518174106588127385097619219536294589148765074804124925845579871788369264160902401097166484002617399484700234182426993061977152961670486891123188739266793651668791365808983166555735631354925174224786218771453042042304773095663181121735652667614424198057134974727791329623974680096491276337756445057223988781749506082654194307092164895251308088903000573135447235553684949564809677864522417041639512933806794232354223826262154508950271949764583849083972967642587778197779127063591201123312548182885603427440981731822883101260509710567731', - }, - z: '57056568014385132434061065334124327103768023932445648883765905576432733866307137325457775876741578717650388638737098805750938053855430851133826479968450532729423746605371536096355616166421996729493639634413002114547787617999178137950004782677177313856876420539744625174205603354705595789330008560775613287118432593300023801651460885523314713996258581986238928077688246511704050386525431448517516821261983193275502089060128363906909778842476516981025598807378338053788433033754999771876361716562378445777250912525673660842724168260417083076824975992327559199634032439358787956784395443246565622469187082767614421691234', - rctxt: - '17146114573198643698878017247599007910707723139165264508694101989891626297408755744139587708989465136799243292477223763665064840330721616213638280284119891715514951989022398510785960099708705561761504012512387129498731093386014964896897751536856287377064154297370092339714578039195258061017640952790913108285519632654466006255438773382930416822756630391947263044087385305540191237328903426888518439803354213792647775798033294505898635058814132665832000734168261793545453678083703704122695006541391598116359796491845268631009298069826949515604008666680160398698425061157356267086946953480945396595351944425658076127674', - n: '95671911213029889766246243339609567053285242961853979532076192834533577534909796042025401129640348836502648821408485216223269830089771714177855160978214805993386076928594836829216646288195127289421136294309746871614765411402917891972999085287429566166932354413679994469616357622976775651506242447852304853465380257226445481515631782793575184420720296120464167257703633829902427169144462981949944348928086406211627174233811365419264314148304536534528344413738913277713548403058098093453580992173145127632199215550027527631259565822872315784889212327945030315062879193999012349220118290071491899498795367403447663354833', - s: '1573939820553851804028472930351082111827449763317396231059458630252708273163050576299697385049087601314071156646675105028237105229428440185022593174121924731226634356276616495327358864865629675802738680754755949997611920669823449540027707876555408118172529688443208301403297680159171306000341239398135896274940688268460793682007115152428685521865921925309154307574955324973580144009271977076586453011938089159885164705002797196738438392179082905738155386545935208094240038135576042886730802817809757582039362798495805441520744154270346780731494125065136433163757697326955962282840631850597919384092584727207908978907', + primary: { + r: { + last_name: + '35864556460959997092903171610228165251001245539613587319116151716453114432309327039517115215674024166920383445379522674504803469517283236033110568676156285676664363558333716898161685255450536856645604857714925836474250821415182026707218622134953915013803750771185050002646661004119778318524426368842019753903741998256374803456282688037624993010626333853831264356355867746685055670790915539230702546586615988121383960277550317876816983602795121749533628953449405383896799464872758725899520173321672584180060465965090049734285011738428381648013150818429882628144544132356242262467090140003979917439514443707537952643217', + first_name: + '26405366527417391838431479783966663952336302347775179063968690502492620867161212873635806190080000833725932174641667734138216137047349915190546601368424742647800764149890590518336588437317392528514313749533980651547425554257026971104775208127915118918084350210726664749850578299247705298976657301433446491575776774836993110356033664644761593799921221474617858131678955318702706530853801195330271860527250931569815553226145458665481867408279941785848264018364216087471931232367137301987457054918438087686484522112532447779498424748261678616461026788516567300969886029412198319909977473167405879110243445062391837349387', + age: '19865805272519696320755573045337531955436490760876870776207490804137339344112305203631892390827288264857621916650098902064979838987400911652887344763586495880167030031364467726355103327059673023946234460960685398768709062405377107912774045508870580108596597470880834205563197111550140867466625683117333370595295321833757429488192170551320637065066368716366317421169802474954914904380304190861641082310805418122837214965865969459724848071006870574514215255412289237027267424055400593307112849859757094597401668252862525566316402695830217450073667487951799749275437192883439584518905943435472478496028380016245355151988', + master_secret: + '51468326064458249697956272807708948542001661888325200180968238787091473418947480867518174106588127385097619219536294589148765074804124925845579871788369264160902401097166484002617399484700234182426993061977152961670486891123188739266793651668791365808983166555735631354925174224786218771453042042304773095663181121735652667614424198057134974727791329623974680096491276337756445057223988781749506082654194307092164895251308088903000573135447235553684949564809677864522417041639512933806794232354223826262154508950271949764583849083972967642587778197779127063591201123312548182885603427440981731822883101260509710567731', }, + z: '57056568014385132434061065334124327103768023932445648883765905576432733866307137325457775876741578717650388638737098805750938053855430851133826479968450532729423746605371536096355616166421996729493639634413002114547787617999178137950004782677177313856876420539744625174205603354705595789330008560775613287118432593300023801651460885523314713996258581986238928077688246511704050386525431448517516821261983193275502089060128363906909778842476516981025598807378338053788433033754999771876361716562378445777250912525673660842724168260417083076824975992327559199634032439358787956784395443246565622469187082767614421691234', + rctxt: + '17146114573198643698878017247599007910707723139165264508694101989891626297408755744139587708989465136799243292477223763665064840330721616213638280284119891715514951989022398510785960099708705561761504012512387129498731093386014964896897751536856287377064154297370092339714578039195258061017640952790913108285519632654466006255438773382930416822756630391947263044087385305540191237328903426888518439803354213792647775798033294505898635058814132665832000734168261793545453678083703704122695006541391598116359796491845268631009298069826949515604008666680160398698425061157356267086946953480945396595351944425658076127674', + n: '95671911213029889766246243339609567053285242961853979532076192834533577534909796042025401129640348836502648821408485216223269830089771714177855160978214805993386076928594836829216646288195127289421136294309746871614765411402917891972999085287429566166932354413679994469616357622976775651506242447852304853465380257226445481515631782793575184420720296120464167257703633829902427169144462981949944348928086406211627174233811365419264314148304536534528344413738913277713548403058098093453580992173145127632199215550027527631259565822872315784889212327945030315062879193999012349220118290071491899498795367403447663354833', + s: '1573939820553851804028472930351082111827449763317396231059458630252708273163050576299697385049087601314071156646675105028237105229428440185022593174121924731226634356276616495327358864865629675802738680754755949997611920669823449540027707876555408118172529688443208301403297680159171306000341239398135896274940688268460793682007115152428685521865921925309154307574955324973580144009271977076586453011938089159885164705002797196738438392179082905738155386545935208094240038135576042886730802817809757582039362798495805441520744154270346780731494125065136433163757697326955962282840631850597919384092584727207908978907', }, - signature_type: 'CL', - ref: schemaResponse.result.txnMetadata.seqNo, - tag: 'TAG', }, + signature_type: 'CL', + ref: schemaResponse.result.txnMetadata.seqNo, + tag: 'TAG', }, }, - }) + }, }) }) }) From 86cb9d088693182a2a08f26645b00204bd7d2adc Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 10 Feb 2023 11:13:44 -0300 Subject: [PATCH 029/139] ci: increase maximum heap memory for node (#1280) Signed-off-by: Ariel Gentile --- .github/workflows/continuous-integration.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 6890536c12..44820700fe 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -12,6 +12,7 @@ env: TEST_AGENT_PUBLIC_DID_SEED: 000000000000000000000000Trustee9 GENESIS_TXN_PATH: network/genesis/local-genesis.txn LIB_INDY_STRG_POSTGRES: /home/runner/work/aries-framework-javascript/indy-sdk/experimental/plugins/postgres_storage/target/release # for Linux + NODE_OPTIONS: --max_old_space_size=4096 # Make sure we're not running multiple release steps at the same time as this can give issues with determining the next npm version to release. # Ideally we only add this to the 'release' job so it doesn't limit PR runs, but github can't guarantee the job order in that case: From 1d487b1a7e11b3f18b5229ba580bd035a7f564a0 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Fri, 10 Feb 2023 20:21:20 +0100 Subject: [PATCH 030/139] feat: added endpoint setter to agent InitConfig (#1278) Signed-off-by: Jim Ezesinachi --- packages/core/src/agent/AgentConfig.ts | 10 ++++++++-- .../core/src/agent/__tests__/AgentConfig.test.ts | 12 ++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 28ad67488a..a2df97a94c 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -11,12 +11,14 @@ import { DidCommMimeType } from '../types' export class AgentConfig { private initConfig: InitConfig + private _endpoints: string[] | undefined public label: string public logger: Logger public readonly agentDependencies: AgentDependencies public constructor(initConfig: InitConfig, agentDependencies: AgentDependencies) { this.initConfig = initConfig + this._endpoints = initConfig.endpoints this.label = initConfig.label this.logger = initConfig.logger ?? new ConsoleLogger(LogLevel.off) this.agentDependencies = agentDependencies @@ -134,11 +136,15 @@ export class AgentConfig { public get endpoints(): [string, ...string[]] { // if endpoints is not set, return queue endpoint // https://github.com/hyperledger/aries-rfcs/issues/405#issuecomment-582612875 - if (!this.initConfig.endpoints || this.initConfig.endpoints.length === 0) { + if (!this._endpoints || this._endpoints.length === 0) { return [DID_COMM_TRANSPORT_QUEUE] } - return this.initConfig.endpoints as [string, ...string[]] + return this._endpoints as [string, ...string[]] + } + + public set endpoints(endpoints: string[]) { + this._endpoints = endpoints } /** diff --git a/packages/core/src/agent/__tests__/AgentConfig.test.ts b/packages/core/src/agent/__tests__/AgentConfig.test.ts index 559a9880a3..43549b1e87 100644 --- a/packages/core/src/agent/__tests__/AgentConfig.test.ts +++ b/packages/core/src/agent/__tests__/AgentConfig.test.ts @@ -18,6 +18,18 @@ describe('AgentConfig', () => { expect(agentConfig.endpoints).toStrictEqual(['didcomm:transport/queue']) }) + + it('should return the new config endpoint after setter is called', () => { + const endpoint = 'https://local-url.com' + const newEndpoint = 'https://new-local-url.com' + + const agentConfig = getAgentConfig('AgentConfig Test', { + endpoints: [endpoint], + }) + + agentConfig.endpoints = [newEndpoint] + expect(agentConfig.endpoints).toEqual([newEndpoint]) + }) }) describe('label', () => { From 2669d7dd3d7c0ddfd1108dfd65e6115dd3418500 Mon Sep 17 00:00:00 2001 From: KolbyRKunz Date: Fri, 10 Feb 2023 14:14:59 -0700 Subject: [PATCH 031/139] fix: set updateAt on records when updating a record (#1272) Signed-off-by: KolbyRKunz --- .../askar/src/storage/AskarStorageService.ts | 4 + .../__tests__/AskarStorageService.test.ts | 12 +++ packages/core/jest.config.ts | 2 + .../core/src/storage/IndyStorageService.ts | 4 + .../__tests__/IndyStorageService.test.ts | 21 +++++ .../__tests__/__snapshots__/0.1.test.ts.snap | 82 +++++++++++++++++++ .../__tests__/__snapshots__/0.2.test.ts.snap | 11 +++ .../__tests__/__snapshots__/0.3.test.ts.snap | 9 ++ .../__snapshots__/backup.test.ts.snap | 4 + tests/InMemoryStorageService.ts | 2 + 10 files changed, 151 insertions(+) diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index e7c96399c2..cdf537745d 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -21,6 +21,8 @@ export class AskarStorageService implements StorageService assertAskarWallet(agentContext.wallet) const session = agentContext.wallet.session + record.updatedAt = new Date() + const value = JsonTransformer.serialize(record) const tags = transformFromRecordTagValues(record.getTags()) as Record @@ -40,6 +42,8 @@ export class AskarStorageService implements StorageService assertAskarWallet(agentContext.wallet) const session = agentContext.wallet.session + record.updatedAt = new Date() + const value = JsonTransformer.serialize(record) const tags = transformFromRecordTagValues(record.getTags()) as Record diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index 1ba1bf329f..2208cde944 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -14,6 +14,8 @@ import { AskarWallet } from '../../wallet/AskarWallet' import { AskarStorageService } from '../AskarStorageService' import { askarQueryFromSearchQuery } from '../utils' +const startDate = Date.now() + describe('AskarStorageService', () => { let wallet: AskarWallet let storageService: AskarStorageService @@ -127,6 +129,11 @@ describe('AskarStorageService', () => { expect(record).toEqual(found) }) + + it('updatedAt should have a new value after a save', async () => { + const record = await insertRecord({ id: 'test-id' }) + expect(record.updatedAt?.getTime()).toBeGreaterThan(startDate) + }) }) describe('getById()', () => { @@ -165,6 +172,11 @@ describe('AskarStorageService', () => { const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) expect(retrievedRecord).toEqual(record) }) + + it('updatedAt should have a new value after an update', async () => { + const record = await insertRecord({ id: 'test-id' }) + expect(record.updatedAt?.getTime()).toBeGreaterThan(startDate) + }) }) describe('delete()', () => { diff --git a/packages/core/jest.config.ts b/packages/core/jest.config.ts index 55c67d70a6..22e2708f18 100644 --- a/packages/core/jest.config.ts +++ b/packages/core/jest.config.ts @@ -4,6 +4,8 @@ import base from '../../jest.config.base' import packageJson from './package.json' +process.env.TZ = 'GMT' + const config: Config.InitialOptions = { ...base, name: packageJson.name, diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts index 452ef555c1..bd71d1701f 100644 --- a/packages/core/src/storage/IndyStorageService.ts +++ b/packages/core/src/storage/IndyStorageService.ts @@ -138,6 +138,8 @@ export class IndyStorageService> implements public async save(agentContext: AgentContext, record: T) { assertIndyWallet(agentContext.wallet) + record.updatedAt = new Date() + const value = JsonTransformer.serialize(record) const tags = this.transformFromRecordTagValues(record.getTags()) as Record @@ -157,6 +159,8 @@ export class IndyStorageService> implements public async update(agentContext: AgentContext, record: T): Promise { assertIndyWallet(agentContext.wallet) + record.updatedAt = new Date() + const value = JsonTransformer.serialize(record) const tags = this.transformFromRecordTagValues(record.getTags()) as Record diff --git a/packages/core/src/storage/__tests__/IndyStorageService.test.ts b/packages/core/src/storage/__tests__/IndyStorageService.test.ts index bd61553b08..b517641408 100644 --- a/packages/core/src/storage/__tests__/IndyStorageService.test.ts +++ b/packages/core/src/storage/__tests__/IndyStorageService.test.ts @@ -5,11 +5,14 @@ import type * as Indy from 'indy-sdk' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../tests/helpers' import { SigningProviderRegistry } from '../../crypto/signing-provider' import { RecordDuplicateError, RecordNotFoundError } from '../../error' +import { sleep } from '../../utils/sleep' import { IndyWallet } from '../../wallet/IndyWallet' import { IndyStorageService } from '../IndyStorageService' import { TestRecord } from './TestRecord' +const startDate = Date.now() + describe('IndyStorageService', () => { let wallet: IndyWallet let indy: typeof Indy @@ -113,6 +116,12 @@ describe('IndyStorageService', () => { expect(record).toEqual(found) }) + + it('After a save the record should have update the updatedAt property', async () => { + const time = startDate + const record = await insertRecord({ id: 'test-updatedAt' }) + expect(record.updatedAt?.getTime()).toBeGreaterThan(time) + }) }) describe('getById()', () => { @@ -151,6 +160,18 @@ describe('IndyStorageService', () => { const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) expect(retrievedRecord).toEqual(record) }) + + it('After a record has been updated it should have updated the updatedAT property', async () => { + const time = startDate + const record = await insertRecord({ id: 'test-id' }) + + record.replaceTags({ ...record.getTags(), foo: 'bar' }) + record.foo = 'foobaz' + await storageService.update(agentContext, record) + + const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) + expect(retrievedRecord.createdAt.getTime()).toBeGreaterThan(time) + }) }) describe('delete()', () => { diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 241611490a..8fe31caafb 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -56,6 +56,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "10-4e4f-41d9-94c4-f49351b811f1": Object { @@ -112,6 +113,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "11-4e4f-41d9-94c4-f49351b811f1": Object { @@ -151,6 +153,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "12-4e4f-41d9-94c4-f49351b811f1": Object { @@ -191,6 +194,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "2-4e4f-41d9-94c4-f49351b811f1": Object { @@ -230,6 +234,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "3-4e4f-41d9-94c4-f49351b811f1": Object { @@ -270,6 +275,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "4-4e4f-41d9-94c4-f49351b811f1": Object { @@ -326,6 +332,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5-4e4f-41d9-94c4-f49351b811f1": Object { @@ -365,6 +372,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { @@ -410,6 +418,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": Object { @@ -471,6 +480,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "6-4e4f-41d9-94c4-f49351b811f1": Object { @@ -511,6 +521,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7-4e4f-41d9-94c4-f49351b811f1": Object { @@ -567,6 +578,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "8-4e4f-41d9-94c4-f49351b811f1": Object { @@ -606,6 +618,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "9-4e4f-41d9-94c4-f49351b811f1": Object { @@ -646,6 +659,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -657,6 +671,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": Object { @@ -702,6 +717,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": Object { @@ -763,6 +779,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -832,6 +849,7 @@ Object { "reuseConnectionId": undefined, "role": "receiver", "state": "done", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "2-4e4f-41d9-94c4-f49351b811f1": Object { @@ -896,6 +914,7 @@ Object { "reuseConnectionId": undefined, "role": "sender", "state": "done", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "3-4e4f-41d9-94c4-f49351b811f1": Object { @@ -960,6 +979,7 @@ Object { "reuseConnectionId": undefined, "role": "sender", "state": "done", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "4-4e4f-41d9-94c4-f49351b811f1": Object { @@ -1024,6 +1044,7 @@ Object { "reuseConnectionId": undefined, "role": "receiver", "state": "done", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5-4e4f-41d9-94c4-f49351b811f1": Object { @@ -1088,6 +1109,7 @@ Object { "reuseConnectionId": undefined, "role": "receiver", "state": "done", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "6-4e4f-41d9-94c4-f49351b811f1": Object { @@ -1147,6 +1169,7 @@ Object { "reuseConnectionId": undefined, "role": "sender", "state": "await-response", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7-4e4f-41d9-94c4-f49351b811f1": Object { @@ -1211,6 +1234,7 @@ Object { "reuseConnectionId": undefined, "role": "sender", "state": "await-response", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7781341d-be29-441b-9b79-4a957d8c6d37": Object { @@ -1244,6 +1268,7 @@ Object { "theirDid": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", "theirLabel": "Agent: PopulateWallet2", "threadId": "a0c0e4d2-1501-42a2-a09b-7d5adc90b353", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "8f4908ee-15ad-4058-9106-eda26eae735c": Object { @@ -1277,6 +1302,7 @@ Object { "theirDid": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", "theirLabel": "Agent: PopulateWallet2", "threadId": "fe287ec6-711b-4582-bb2b-d155aee86e61", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "9383d8e5-c002-4aae-8300-4a21384c919e": Object { @@ -1309,6 +1335,7 @@ Object { "theirDid": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", "theirLabel": "Agent: PopulateWallet2", "threadId": "0b2f1133-ced9-49f1-83a1-eb6ba1c24cdf", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -1320,6 +1347,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "b65c2ccd-277c-4140-9d87-c8dd30e7a98c": Object { @@ -1349,6 +1377,7 @@ Object { "outOfBandId": "7-4e4f-41d9-94c4-f49351b811f1", "role": "responder", "state": "invitation-sent", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "da518433-0e55-4b74-a05b-aa75c1095a99": Object { @@ -1382,6 +1411,7 @@ Object { "theirDid": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", "theirLabel": "Agent: PopulateWallet2", "threadId": "6eeb6a80-cd75-491d-b2e0-7bae65ced1c3", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT": Object { @@ -1455,6 +1485,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56": Object { @@ -1528,6 +1559,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ": Object { @@ -1601,6 +1633,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb": Object { @@ -1674,6 +1707,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU": Object { @@ -1747,6 +1781,7 @@ Object { }, }, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy": Object { @@ -1820,6 +1855,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf": Object { @@ -1893,6 +1929,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga": Object { @@ -1966,6 +2003,7 @@ Object { }, }, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ": Object { @@ -2039,6 +2077,7 @@ Object { }, }, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui": Object { @@ -2112,6 +2151,7 @@ Object { }, }, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw": Object { @@ -2185,6 +2225,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX": Object { @@ -2258,6 +2299,7 @@ Object { }, }, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv": Object { @@ -2331,6 +2373,7 @@ Object { }, }, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199": Object { @@ -2364,6 +2407,7 @@ Object { "theirDid": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", "theirLabel": "Agent: PopulateWallet2", "threadId": "daf3372c-1ee2-4246-a1f4-f62f54f7d68b", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "ee88e2e1-e27e-46a6-a910-f87690109e32": Object { @@ -2393,6 +2437,7 @@ Object { "role": "requester", "state": "request-sent", "theirLabel": "Agent: PopulateWallet2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -2454,6 +2499,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "10-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2510,6 +2556,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "11-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2549,6 +2596,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "12-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2589,6 +2637,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "2-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2628,6 +2677,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "3-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2668,6 +2718,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "4-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2724,6 +2775,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2763,6 +2815,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { @@ -2808,6 +2861,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": Object { @@ -2869,6 +2923,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "6-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2909,6 +2964,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7-4e4f-41d9-94c4-f49351b811f1": Object { @@ -2965,6 +3021,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "8-4e4f-41d9-94c4-f49351b811f1": Object { @@ -3004,6 +3061,7 @@ Object { }, "metadata": Object {}, "role": "sender", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "9-4e4f-41d9-94c4-f49351b811f1": Object { @@ -3044,6 +3102,7 @@ Object { }, "metadata": Object {}, "role": "receiver", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -3055,6 +3114,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": Object { @@ -3100,6 +3160,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": Object { @@ -3161,6 +3222,7 @@ Object { "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -3191,6 +3253,7 @@ Object { ], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { @@ -3216,6 +3279,7 @@ Object { ], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "802ef124-36b7-490f-b152-e9d090ddf073": Object { @@ -3238,6 +3302,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -3249,6 +3314,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { @@ -3271,6 +3337,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -3301,6 +3368,7 @@ Object { ], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { @@ -3326,6 +3394,7 @@ Object { ], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "802ef124-36b7-490f-b152-e9d090ddf073": Object { @@ -3348,6 +3417,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -3359,6 +3429,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { @@ -3381,6 +3452,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -3411,6 +3483,7 @@ Object { ], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { @@ -3436,6 +3509,7 @@ Object { ], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "802ef124-36b7-490f-b152-e9d090ddf073": Object { @@ -3458,6 +3532,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -3469,6 +3544,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { @@ -3491,6 +3567,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -3521,6 +3598,7 @@ Object { ], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { @@ -3546,6 +3624,7 @@ Object { ], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "802ef124-36b7-490f-b152-e9d090ddf073": Object { @@ -3568,6 +3647,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -3579,6 +3659,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.2", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { @@ -3601,6 +3682,7 @@ Object { "routingKeys": Array [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap index a56e8065c1..8d76122ef4 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap @@ -122,6 +122,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.3.1", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "ea840186-3c77-45f4-a2e6-349811ad8994": Object { @@ -302,6 +303,7 @@ Object { "id": "1-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "2-4e4f-41d9-94c4-f49351b811f1": Object { @@ -364,6 +366,7 @@ Object { "id": "2-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "3-4e4f-41d9-94c4-f49351b811f1": Object { @@ -426,6 +429,7 @@ Object { "id": "3-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "4-4e4f-41d9-94c4-f49351b811f1": Object { @@ -490,6 +494,7 @@ Object { "id": "4-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5-4e4f-41d9-94c4-f49351b811f1": Object { @@ -552,6 +557,7 @@ Object { "id": "5-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "6-4e4f-41d9-94c4-f49351b811f1": Object { @@ -614,6 +620,7 @@ Object { "id": "6-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7-4e4f-41d9-94c4-f49351b811f1": Object { @@ -676,6 +683,7 @@ Object { "id": "7-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "8-4e4f-41d9-94c4-f49351b811f1": Object { @@ -738,6 +746,7 @@ Object { "id": "8-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -749,6 +758,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.3.1", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } @@ -876,6 +886,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.3.1", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "ea840186-3c77-45f4-a2e6-349811ad8994": Object { diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap index 8169373e57..d75c5d4c22 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap @@ -64,6 +64,7 @@ Object { "id": "1-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "2-4e4f-41d9-94c4-f49351b811f1": Object { @@ -126,6 +127,7 @@ Object { "id": "2-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "3-4e4f-41d9-94c4-f49351b811f1": Object { @@ -188,6 +190,7 @@ Object { "id": "3-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "4-4e4f-41d9-94c4-f49351b811f1": Object { @@ -252,6 +255,7 @@ Object { "id": "4-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "5-4e4f-41d9-94c4-f49351b811f1": Object { @@ -314,6 +318,7 @@ Object { "id": "5-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "6-4e4f-41d9-94c4-f49351b811f1": Object { @@ -376,6 +381,7 @@ Object { "id": "6-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "7-4e4f-41d9-94c4-f49351b811f1": Object { @@ -438,6 +444,7 @@ Object { "id": "7-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "received", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "8-4e4f-41d9-94c4-f49351b811f1": Object { @@ -500,6 +507,7 @@ Object { "id": "8-4e4f-41d9-94c4-f49351b811f1", "metadata": Object {}, "role": "created", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -511,6 +519,7 @@ Object { "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, "storageVersion": "0.3.1", + "updatedAt": "2022-01-21T22:50:20.522Z", }, }, } diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap index 04765793f5..676480ae59 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap @@ -39,6 +39,7 @@ Array [ "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-03-21T22:50:20.522Z", }, Object { "_tags": Object { @@ -94,6 +95,7 @@ Array [ "protocolVersion": "v1", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-03-21T22:50:20.522Z", }, Object { "_tags": Object { @@ -132,6 +134,7 @@ Array [ "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-03-21T22:50:20.522Z", }, Object { "_tags": Object { @@ -187,6 +190,7 @@ Array [ "protocolVersion": "v1", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-03-21T22:50:20.522Z", }, ] `; diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index cd4415a2e5..0b2a73ebb4 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -35,6 +35,7 @@ export class InMemoryStorageService implement /** @inheritDoc */ public async save(agentContext: AgentContext, record: T) { + record.updatedAt = new Date() const value = JsonTransformer.toJSON(record) if (this.records[record.id]) { @@ -51,6 +52,7 @@ export class InMemoryStorageService implement /** @inheritDoc */ public async update(agentContext: AgentContext, record: T): Promise { + record.updatedAt = new Date() const value = JsonTransformer.toJSON(record) delete value._tags From efe0271198f21f1307df0f934c380f7a5c720b06 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 10 Feb 2023 19:15:36 -0300 Subject: [PATCH 032/139] feat: add anoncreds-rs package (#1275) Signed-off-by: Ariel Gentile --- .github/actions/setup-libssl/action.yml | 22 + .../setup-postgres-wallet-plugin/action.yml | 2 +- .github/workflows/continuous-integration.yml | 17 +- Dockerfile | 12 +- packages/anoncreds-rs/README.md | 31 ++ packages/anoncreds-rs/jest.config.ts | 14 + packages/anoncreds-rs/package.json | 41 ++ .../anoncreds-rs/src/AnonCredsRsModule.ts | 29 + .../src/errors/AnonCredsRsError.ts | 7 + packages/anoncreds-rs/src/index.ts | 5 + .../src/services/AnonCredsRsHolderService.ts | 374 +++++++++++++ .../src/services/AnonCredsRsIssuerService.ts | 158 ++++++ .../services/AnonCredsRsVerifierService.ts | 66 +++ .../AnonCredsRsHolderService.test.ts | 501 ++++++++++++++++++ .../__tests__/AnonCredsRsServices.test.ts | 224 ++++++++ .../src/services/__tests__/helpers.ts | 173 ++++++ packages/anoncreds-rs/src/services/index.ts | 3 + packages/anoncreds-rs/src/types.ts | 4 + packages/anoncreds-rs/tests/indy-flow.test.ts | 277 ++++++++++ packages/anoncreds-rs/tests/setup.ts | 3 + packages/anoncreds-rs/tsconfig.build.json | 7 + packages/anoncreds-rs/tsconfig.json | 6 + .../src/formats/AnonCredsCredentialFormat.ts | 4 +- .../LegacyIndyCredentialFormatService.ts | 18 +- packages/anoncreds/src/models/registry.ts | 16 +- .../repository/AnonCredsCredentialRecord.ts | 76 +++ .../AnonCredsCredentialRepository.ts | 31 ++ packages/anoncreds/src/repository/index.ts | 2 + .../services/AnonCredsHolderServiceOptions.ts | 1 + packages/anoncreds/tests/anoncreds.test.ts | 16 +- packages/core/src/index.ts | 2 +- .../indy-sdk/src/anoncreds/utils/transform.ts | 24 +- tests/InMemoryStorageService.ts | 1 + yarn.lock | 28 +- 34 files changed, 2155 insertions(+), 40 deletions(-) create mode 100644 .github/actions/setup-libssl/action.yml create mode 100644 packages/anoncreds-rs/README.md create mode 100644 packages/anoncreds-rs/jest.config.ts create mode 100644 packages/anoncreds-rs/package.json create mode 100644 packages/anoncreds-rs/src/AnonCredsRsModule.ts create mode 100644 packages/anoncreds-rs/src/errors/AnonCredsRsError.ts create mode 100644 packages/anoncreds-rs/src/index.ts create mode 100644 packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts create mode 100644 packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts create mode 100644 packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts create mode 100644 packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts create mode 100644 packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts create mode 100644 packages/anoncreds-rs/src/services/__tests__/helpers.ts create mode 100644 packages/anoncreds-rs/src/services/index.ts create mode 100644 packages/anoncreds-rs/src/types.ts create mode 100644 packages/anoncreds-rs/tests/indy-flow.test.ts create mode 100644 packages/anoncreds-rs/tests/setup.ts create mode 100644 packages/anoncreds-rs/tsconfig.build.json create mode 100644 packages/anoncreds-rs/tsconfig.json create mode 100644 packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts create mode 100644 packages/anoncreds/src/repository/AnonCredsCredentialRepository.ts diff --git a/.github/actions/setup-libssl/action.yml b/.github/actions/setup-libssl/action.yml new file mode 100644 index 0000000000..9710ea6e88 --- /dev/null +++ b/.github/actions/setup-libssl/action.yml @@ -0,0 +1,22 @@ +name: Setup libSSL +description: Install libssl and libssl-dev 1.1 +author: 'gentilester@gmail.com' + +runs: + using: composite + steps: + - name: Install libssl1.1 + run: | + curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl1.1.deb + sudo dpkg -i libssl1.1.deb + shell: bash + + - name: Instal libssl-dev.1.1 + run: | + curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl-dev1.1.deb + sudo dpkg -i libssl-dev1.1.deb + shell: bash + +branding: + icon: scissors + color: purple diff --git a/.github/actions/setup-postgres-wallet-plugin/action.yml b/.github/actions/setup-postgres-wallet-plugin/action.yml index a03b2f3fde..81f41d3578 100644 --- a/.github/actions/setup-postgres-wallet-plugin/action.yml +++ b/.github/actions/setup-postgres-wallet-plugin/action.yml @@ -10,7 +10,7 @@ runs: # so pointing rust version to 1.63.0 - name: Setup Postgres wallet plugin run: | - sudo apt-get install -y libzmq3-dev libsodium-dev pkg-config libssl-dev + sudo apt-get install -y libzmq3-dev libsodium-dev pkg-config curl https://sh.rustup.rs -sSf | bash -s -- -y export PATH="/root/.cargo/bin:${PATH}" rustup default 1.63.0 diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 44820700fe..0ad780e636 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -26,7 +26,7 @@ jobs: # validation scripts. To still be able to run the CI we can manually trigger it by adding the 'ci-test' # label to the pull request ci-trigger: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 outputs: triggered: ${{ steps.check.outputs.triggered }} steps: @@ -45,13 +45,16 @@ jobs: echo "::set-output name=triggered::${SHOULD_RUN}" validate: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 name: Validate steps: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 # setup dependencies + - name: Setup Libssl + uses: ./.github/actions/setup-libssl + - name: Setup Libindy uses: ./.github/actions/setup-libindy @@ -76,7 +79,7 @@ jobs: run: yarn build integration-test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 name: Integration Tests strategy: @@ -88,6 +91,9 @@ jobs: uses: actions/checkout@v2 # setup dependencies + - name: Setup Libssl + uses: ./.github/actions/setup-libssl + - name: Setup Libindy uses: ./.github/actions/setup-libindy - name: Setup Indy Pool @@ -115,7 +121,7 @@ jobs: if: always() version-stable: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 name: Release stable needs: [integration-test, validate] if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' @@ -127,6 +133,9 @@ jobs: fetch-depth: 0 # setup dependencies + - name: Setup Libssl + uses: ./.github/actions/setup-libssl + - name: Setup Libindy uses: ./.github/actions/setup-libindy diff --git a/Dockerfile b/Dockerfile index 91ccda0363..7f55d81dfe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:18.04 as base +FROM ubuntu:22.04 as base ENV DEBIAN_FRONTEND noninteractive @@ -9,7 +9,15 @@ RUN apt-get update -y && apt-get install -y \ # Only needed to build indy-sdk build-essential \ git \ - libzmq3-dev libsodium-dev pkg-config libssl-dev + libzmq3-dev libsodium-dev pkg-config + +# libssl1.1 (required by libindy) +RUN curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl1.1.deb +RUN dpkg -i libssl1.1.deb + +# libssl-dev1.1 (required to compile libindy with posgres plugin) +RUN curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl-dev1.1.deb +RUN dpkg -i libssl-dev1.1.deb # libindy RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 diff --git a/packages/anoncreds-rs/README.md b/packages/anoncreds-rs/README.md new file mode 100644 index 0000000000..87f28670e7 --- /dev/null +++ b/packages/anoncreds-rs/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript AnonCreds RS Module

+

+ License + typescript + @aries-framework/anoncreds-rs version + +

+
+ +AnonCreds RS module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). diff --git a/packages/anoncreds-rs/jest.config.ts b/packages/anoncreds-rs/jest.config.ts new file mode 100644 index 0000000000..55c67d70a6 --- /dev/null +++ b/packages/anoncreds-rs/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json new file mode 100644 index 0000000000..d60aa4f4ca --- /dev/null +++ b/packages/anoncreds-rs/package.json @@ -0,0 +1,41 @@ +{ + "name": "@aries-framework/anoncreds-rs", + "main": "build/index", + "types": "build/index", + "version": "0.3.3", + "private": true, + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/anoncreds-rs", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/anoncreds-rs" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "0.3.3", + "@aries-framework/anoncreds": "0.3.3", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.5", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "rxjs": "^7.2.0", + "tsyringe": "^4.7.0" + }, + "devDependencies": { + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.5", + "rimraf": "^4.0.7", + "typescript": "~4.9.4" + } +} diff --git a/packages/anoncreds-rs/src/AnonCredsRsModule.ts b/packages/anoncreds-rs/src/AnonCredsRsModule.ts new file mode 100644 index 0000000000..4ceb7b8304 --- /dev/null +++ b/packages/anoncreds-rs/src/AnonCredsRsModule.ts @@ -0,0 +1,29 @@ +import type { DependencyManager, Module } from '@aries-framework/core' + +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, +} from '@aries-framework/anoncreds' + +import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from './services' + +export class AnonCredsRsModule implements Module { + public register(dependencyManager: DependencyManager) { + try { + // eslint-disable-next-line import/no-extraneous-dependencies + require('@hyperledger/anoncreds-nodejs') + } catch (error) { + try { + require('@hyperledger/anoncreds-react-native') + } catch (error) { + throw new Error('Could not load anoncreds bindings') + } + } + + // Register services + dependencyManager.registerSingleton(AnonCredsHolderServiceSymbol, AnonCredsRsHolderService) + dependencyManager.registerSingleton(AnonCredsIssuerServiceSymbol, AnonCredsRsIssuerService) + dependencyManager.registerSingleton(AnonCredsVerifierServiceSymbol, AnonCredsRsVerifierService) + } +} diff --git a/packages/anoncreds-rs/src/errors/AnonCredsRsError.ts b/packages/anoncreds-rs/src/errors/AnonCredsRsError.ts new file mode 100644 index 0000000000..e8cdf3023d --- /dev/null +++ b/packages/anoncreds-rs/src/errors/AnonCredsRsError.ts @@ -0,0 +1,7 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +export class AnonCredsRsError extends AriesFrameworkError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/anoncreds-rs/src/index.ts b/packages/anoncreds-rs/src/index.ts new file mode 100644 index 0000000000..5fdd9486c7 --- /dev/null +++ b/packages/anoncreds-rs/src/index.ts @@ -0,0 +1,5 @@ +// Services +export * from './services' + +// Module +export { AnonCredsRsModule } from './AnonCredsRsModule' diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts new file mode 100644 index 0000000000..e0c84fd7b1 --- /dev/null +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -0,0 +1,374 @@ +import type { + AnonCredsHolderService, + AnonCredsProof, + CreateCredentialRequestOptions, + CreateCredentialRequestReturn, + CreateProofOptions, + GetCredentialOptions, + StoreCredentialOptions, + GetCredentialsForProofRequestOptions, + GetCredentialsForProofRequestReturn, + AnonCredsCredentialInfo, + CreateLinkSecretOptions, + CreateLinkSecretReturn, + AnonCredsProofRequestRestriction, + AnonCredsRequestedAttribute, + AnonCredsRequestedPredicate, + AnonCredsCredential, +} from '@aries-framework/anoncreds' +import type { AgentContext, Query, SimpleQuery } from '@aries-framework/core' +import type { CredentialEntry, CredentialProve } from '@hyperledger/anoncreds-shared' + +import { + AnonCredsCredentialRecord, + AnonCredsLinkSecretRepository, + AnonCredsCredentialRepository, +} from '@aries-framework/anoncreds' +import { injectable } from '@aries-framework/core' +import { + CredentialRequestMetadata, + Credential, + CredentialDefinition, + CredentialOffer, + CredentialRequest, + CredentialRevocationState, + MasterSecret, + Presentation, + PresentationRequest, + RevocationRegistryDefinition, + RevocationStatusList, + Schema, +} from '@hyperledger/anoncreds-shared' + +import { uuid } from '../../../core/src/utils/uuid' +import { AnonCredsRsError } from '../errors/AnonCredsRsError' + +@injectable() +export class AnonCredsRsHolderService implements AnonCredsHolderService { + public async createLinkSecret( + agentContext: AgentContext, + options?: CreateLinkSecretOptions + ): Promise { + try { + return { + linkSecretId: options?.linkSecretId ?? uuid(), + linkSecretValue: JSON.parse(MasterSecret.create().toJson()).value.ms, + } + } catch (error) { + agentContext.config.logger.error(`Error creating Link Secret`, { + error, + }) + throw new AnonCredsRsError('Error creating Link Secret', { cause: error }) + } + } + + public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise { + const { credentialDefinitions, proofRequest, requestedCredentials, schemas } = options + + try { + const rsCredentialDefinitions: Record = {} + for (const credDefId in credentialDefinitions) { + rsCredentialDefinitions[credDefId] = CredentialDefinition.load(JSON.stringify(credentialDefinitions[credDefId])) + } + + const rsSchemas: Record = {} + for (const schemaId in schemas) { + rsSchemas[schemaId] = Schema.load(JSON.stringify(schemas[schemaId])) + } + + const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) + + // Cache retrieved credentials in order to minimize storage calls + const retrievedCredentials = new Map() + + const credentialEntryFromAttribute = async ( + attribute: AnonCredsRequestedAttribute | AnonCredsRequestedPredicate + ): Promise<{ linkSecretId: string; credentialEntry: CredentialEntry }> => { + let credentialRecord = retrievedCredentials.get(attribute.credentialId) + if (!credentialRecord) { + credentialRecord = await credentialRepository.getByCredentialId(agentContext, attribute.credentialId) + retrievedCredentials.set(attribute.credentialId, credentialRecord) + } + + const credential = Credential.load(JSON.stringify(credentialRecord.credential)) + + const revocationRegistryDefinitionId = credential.revocationRegistryId + const revocationRegistryIndex = credential.revocationRegistryIndex + + // TODO: Check if credential has a revocation registry id (check response from anoncreds-rs API, as it is + // sending back a mandatory string in Credential.revocationRegistryId) + const timestamp = attribute.timestamp + + let revocationState + if (timestamp) { + if (revocationRegistryIndex) { + if (!options.revocationRegistries[revocationRegistryDefinitionId]) { + throw new AnonCredsRsError(`Revocation Registry ${revocationRegistryDefinitionId} not found`) + } + + const { definition, tailsFilePath } = options.revocationRegistries[revocationRegistryDefinitionId] + + const revocationRegistryDefinition = RevocationRegistryDefinition.load(JSON.stringify(definition)) + revocationState = CredentialRevocationState.create({ + revocationRegistryIndex, + revocationRegistryDefinition, + tailsPath: tailsFilePath, + revocationStatusList: RevocationStatusList.create({ + issuanceByDefault: true, + revocationRegistryDefinition, + revocationRegistryDefinitionId, + timestamp, + }), + }) + } + } + return { + linkSecretId: credentialRecord.linkSecretId, + credentialEntry: { + credential, + revocationState, + timestamp, + }, + } + } + + const credentialsProve: CredentialProve[] = [] + const credentials: { linkSecretId: string; credentialEntry: CredentialEntry }[] = [] + + let entryIndex = 0 + for (const referent in requestedCredentials.requestedAttributes) { + const attribute = requestedCredentials.requestedAttributes[referent] + credentials.push(await credentialEntryFromAttribute(attribute)) + credentialsProve.push({ entryIndex, isPredicate: false, referent, reveal: attribute.revealed }) + entryIndex = entryIndex + 1 + } + + for (const referent in requestedCredentials.requestedPredicates) { + const predicate = requestedCredentials.requestedPredicates[referent] + credentials.push(await credentialEntryFromAttribute(predicate)) + credentialsProve.push({ entryIndex, isPredicate: true, referent, reveal: true }) + entryIndex = entryIndex + 1 + } + + // Get all requested credentials and take linkSecret. If it's not the same for every credential, throw error + const linkSecretsMatch = credentials.every((item) => item.linkSecretId === credentials[0].linkSecretId) + if (!linkSecretsMatch) { + throw new AnonCredsRsError('All credentials in a Proof should have been issued using the same Link Secret') + } + + const linkSecretRecord = await agentContext.dependencyManager + .resolve(AnonCredsLinkSecretRepository) + .getByLinkSecretId(agentContext, credentials[0].linkSecretId) + + if (!linkSecretRecord.value) { + throw new AnonCredsRsError('Link Secret value not stored') + } + + const presentation = Presentation.create({ + credentialDefinitions: rsCredentialDefinitions, + schemas: rsSchemas, + presentationRequest: PresentationRequest.load(JSON.stringify(proofRequest)), + credentials: credentials.map((entry) => entry.credentialEntry), + credentialsProve, + selfAttest: requestedCredentials.selfAttestedAttributes, + masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), + }) + + return JSON.parse(presentation.toJson()) + } catch (error) { + agentContext.config.logger.error(`Error creating AnonCreds Proof`, { + error, + proofRequest, + requestedCredentials, + }) + throw new AnonCredsRsError(`Error creating proof: ${error}`, { cause: error }) + } + } + + public async createCredentialRequest( + agentContext: AgentContext, + options: CreateCredentialRequestOptions + ): Promise { + const { credentialDefinition, credentialOffer } = options + try { + const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) + + // If a link secret is specified, use it. Otherwise, attempt to use default link secret + const linkSecretRecord = options.linkSecretId + ? await linkSecretRepository.getByLinkSecretId(agentContext, options.linkSecretId) + : await linkSecretRepository.findDefault(agentContext) + + if (!linkSecretRecord) { + // No default link secret + throw new AnonCredsRsError('No default link secret has been found') + } + + const { credentialRequest, credentialRequestMetadata } = CredentialRequest.create({ + credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), + credentialOffer: CredentialOffer.load(JSON.stringify(credentialOffer)), + masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), + masterSecretId: linkSecretRecord.linkSecretId, + }) + + return { + credentialRequest: JSON.parse(credentialRequest.toJson()), + credentialRequestMetadata: JSON.parse(credentialRequestMetadata.toJson()), + } + } catch (error) { + throw new AnonCredsRsError(`Error creating credential request: ${error}`, { cause: error }) + } + } + + public async storeCredential(agentContext: AgentContext, options: StoreCredentialOptions): Promise { + const { credential, credentialDefinition, credentialRequestMetadata, revocationRegistry, schema } = options + + const linkSecretRecord = await agentContext.dependencyManager + .resolve(AnonCredsLinkSecretRepository) + .getByLinkSecretId(agentContext, credentialRequestMetadata.master_secret_name) + + const revocationRegistryDefinition = revocationRegistry?.definition + ? RevocationRegistryDefinition.load(JSON.stringify(revocationRegistry.definition)) + : undefined + + const credentialId = options.credentialId ?? uuid() + const processedCredential = Credential.load(JSON.stringify(credential)).process({ + credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), + credentialRequestMetadata: CredentialRequestMetadata.load(JSON.stringify(credentialRequestMetadata)), + masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), + revocationRegistryDefinition, + }) + + const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) + + await credentialRepository.save( + agentContext, + new AnonCredsCredentialRecord({ + credential: JSON.parse(processedCredential.toJson()) as AnonCredsCredential, + credentialId, + linkSecretId: linkSecretRecord.linkSecretId, + issuerId: options.credentialDefinition.issuerId, + schemaName: schema.name, + schemaIssuerId: schema.issuerId, + schemaVersion: schema.version, + }) + ) + + return credentialId + } + + public async getCredential( + agentContext: AgentContext, + options: GetCredentialOptions + ): Promise { + const credentialRecord = await agentContext.dependencyManager + .resolve(AnonCredsCredentialRepository) + .getByCredentialId(agentContext, options.credentialId) + + const attributes: { [key: string]: string } = {} + for (const attribute in credentialRecord.credential.values) { + attributes[attribute] = credentialRecord.credential.values[attribute].raw + } + return { + attributes, + credentialDefinitionId: credentialRecord.credential.cred_def_id, + credentialId: credentialRecord.credentialId, + schemaId: credentialRecord.credential.schema_id, + credentialRevocationId: credentialRecord.credentialRevocationId, + revocationRegistryId: credentialRecord.credential.rev_reg_id, + } + } + + public async deleteCredential(agentContext: AgentContext, credentialId: string): Promise { + const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) + const credentialRecord = await credentialRepository.getByCredentialId(agentContext, credentialId) + await credentialRepository.delete(agentContext, credentialRecord) + } + + public async getCredentialsForProofRequest( + agentContext: AgentContext, + options: GetCredentialsForProofRequestOptions + ): Promise { + const proofRequest = options.proofRequest + const referent = options.attributeReferent + + const requestedAttribute = + proofRequest.requested_attributes[referent] ?? proofRequest.requested_predicates[referent] + + if (!requestedAttribute) { + throw new AnonCredsRsError(`Referent not found in proof request`) + } + const attributes = requestedAttribute.name ? [requestedAttribute.name] : requestedAttribute.names + + const restrictionQuery = requestedAttribute.restrictions + ? this.queryFromRestrictions(requestedAttribute.restrictions) + : undefined + + const query: Query = { + attributes, + ...restrictionQuery, + ...options.extraQuery, + } + + const credentials = await agentContext.dependencyManager + .resolve(AnonCredsCredentialRepository) + .findByQuery(agentContext, query) + + return credentials.map((credentialRecord) => { + const attributes: { [key: string]: string } = {} + for (const attribute in credentialRecord.credential.values) { + attributes[attribute] = credentialRecord.credential.values[attribute].raw + } + return { + credentialInfo: { + attributes, + credentialDefinitionId: credentialRecord.credential.cred_def_id, + credentialId: credentialRecord.credentialId, + schemaId: credentialRecord.credential.schema_id, + credentialRevocationId: credentialRecord.credentialRevocationId, + revocationRegistryId: credentialRecord.credential.rev_reg_id, + }, + interval: proofRequest.non_revoked, + } + }) + } + + private queryFromRestrictions(restrictions: AnonCredsProofRequestRestriction[]) { + const query: Query[] = [] + + for (const restriction of restrictions) { + const queryElements: SimpleQuery = {} + + if (restriction.cred_def_id) { + queryElements.credentialDefinitionId = restriction.cred_def_id + } + + if (restriction.issuer_id || restriction.issuer_did) { + queryElements.issuerId = restriction.issuer_id ?? restriction.issuer_did + } + + if (restriction.rev_reg_id) { + queryElements.revocationRegistryId = restriction.rev_reg_id + } + + if (restriction.schema_id) { + queryElements.schemaId = restriction.schema_id + } + + if (restriction.schema_issuer_id || restriction.schema_issuer_did) { + queryElements.schemaIssuerId = restriction.schema_issuer_id ?? restriction.schema_issuer_did + } + + if (restriction.schema_name) { + queryElements.schemaName = restriction.schema_name + } + + if (restriction.schema_version) { + queryElements.schemaVersion = restriction.schema_version + } + + query.push(queryElements) + } + + return query.length === 1 ? query[0] : { $or: query } + } +} diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts new file mode 100644 index 0000000000..17b3c91d91 --- /dev/null +++ b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts @@ -0,0 +1,158 @@ +import type { + AnonCredsIssuerService, + CreateCredentialDefinitionOptions, + CreateCredentialOfferOptions, + CreateCredentialOptions, + CreateCredentialReturn, + CreateSchemaOptions, + AnonCredsCredentialOffer, + AnonCredsSchema, + AnonCredsCredentialDefinition, + CreateCredentialDefinitionReturn, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' + +import { + AnonCredsKeyCorrectnessProofRepository, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsCredentialDefinitionRepository, +} from '@aries-framework/anoncreds' +import { injectable, AriesFrameworkError } from '@aries-framework/core' +import { + Credential, + CredentialDefinition, + CredentialDefinitionPrivate, + CredentialRequest, + CredentialOffer, + KeyCorrectnessProof, + Schema, +} from '@hyperledger/anoncreds-shared' + +import { AnonCredsRsError } from '../errors/AnonCredsRsError' + +@injectable() +export class AnonCredsRsIssuerService implements AnonCredsIssuerService { + public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { + const { issuerId, name, version, attrNames: attributeNames } = options + + try { + const schema = Schema.create({ + issuerId, + name, + version, + attributeNames, + }) + + return JSON.parse(schema.toJson()) as AnonCredsSchema + } catch (error) { + throw new AnonCredsRsError('Error creating schema', { cause: error }) + } + } + + public async createCredentialDefinition( + agentContext: AgentContext, + options: CreateCredentialDefinitionOptions + ): Promise { + const { tag, supportRevocation, schema, issuerId, schemaId } = options + + try { + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = CredentialDefinition.create({ + schema: Schema.load(JSON.stringify(schema)), + issuerId, + schemaId, + tag, + supportRevocation, + signatureType: 'CL', + }) + + return { + credentialDefinition: JSON.parse(credentialDefinition.toJson()) as AnonCredsCredentialDefinition, + credentialDefinitionPrivate: JSON.parse(credentialDefinitionPrivate.toJson()), + keyCorrectnessProof: JSON.parse(keyCorrectnessProof.toJson()), + } + } catch (error) { + throw new AnonCredsRsError('Error creating credential definition', { cause: error }) + } + } + + public async createCredentialOffer( + agentContext: AgentContext, + options: CreateCredentialOfferOptions + ): Promise { + const { credentialDefinitionId } = options + + try { + const credentialDefinitionRecord = await agentContext.dependencyManager + .resolve(AnonCredsCredentialDefinitionRepository) + .getByCredentialDefinitionId(agentContext, options.credentialDefinitionId) + + const keyCorrectnessProofRecord = await agentContext.dependencyManager + .resolve(AnonCredsKeyCorrectnessProofRepository) + .getByCredentialDefinitionId(agentContext, options.credentialDefinitionId) + + if (!credentialDefinitionRecord) { + throw new AnonCredsRsError(`Credential Definition ${credentialDefinitionId} not found`) + } + + const credentialOffer = CredentialOffer.create({ + credentialDefinitionId, + keyCorrectnessProof: KeyCorrectnessProof.load(JSON.stringify(keyCorrectnessProofRecord?.value)), + schemaId: credentialDefinitionRecord.credentialDefinition.schemaId, + }) + + return JSON.parse(credentialOffer.toJson()) as AnonCredsCredentialOffer + } catch (error) { + throw new AnonCredsRsError(`Error creating credential offer: ${error}`, { cause: error }) + } + } + + public async createCredential( + agentContext: AgentContext, + options: CreateCredentialOptions + ): Promise { + const { tailsFilePath, credentialOffer, credentialRequest, credentialValues, revocationRegistryId } = options + + try { + if (revocationRegistryId || tailsFilePath) { + throw new AriesFrameworkError('Revocation not supported yet') + } + + const attributeRawValues: Record = {} + const attributeEncodedValues: Record = {} + + Object.keys(credentialValues).forEach((key) => { + attributeRawValues[key] = credentialValues[key].raw + attributeEncodedValues[key] = credentialValues[key].encoded + }) + + const credentialDefinitionRecord = await agentContext.dependencyManager + .resolve(AnonCredsCredentialDefinitionRepository) + .getByCredentialDefinitionId(agentContext, options.credentialRequest.cred_def_id) + + const credentialDefinitionPrivateRecord = await agentContext.dependencyManager + .resolve(AnonCredsCredentialDefinitionPrivateRepository) + .getByCredentialDefinitionId(agentContext, options.credentialRequest.cred_def_id) + + const credential = Credential.create({ + credentialDefinition: CredentialDefinition.load( + JSON.stringify(credentialDefinitionRecord.credentialDefinition) + ), + credentialOffer: CredentialOffer.load(JSON.stringify(credentialOffer)), + credentialRequest: CredentialRequest.load(JSON.stringify(credentialRequest)), + revocationRegistryId, + attributeEncodedValues, + attributeRawValues, + credentialDefinitionPrivate: CredentialDefinitionPrivate.load( + JSON.stringify(credentialDefinitionPrivateRecord.value) + ), + }) + + return { + credential: JSON.parse(credential.toJson()), + credentialRevocationId: credential.revocationRegistryIndex?.toString(), + } + } catch (error) { + throw new AnonCredsRsError(`Error creating credential: ${error}`, { cause: error }) + } + } +} diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts new file mode 100644 index 0000000000..96030d44ba --- /dev/null +++ b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts @@ -0,0 +1,66 @@ +import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' + +import { injectable } from '@aries-framework/core' +import { + CredentialDefinition, + Presentation, + PresentationRequest, + RevocationRegistryDefinition, + RevocationStatusList, + Schema, +} from '@hyperledger/anoncreds-shared' + +import { AnonCredsRsError } from '../errors/AnonCredsRsError' + +@injectable() +export class AnonCredsRsVerifierService implements AnonCredsVerifierService { + public async verifyProof(options: VerifyProofOptions): Promise { + const { credentialDefinitions, proof, proofRequest, revocationStates, schemas } = options + + try { + const presentation = Presentation.load(JSON.stringify(proof)) + + const rsCredentialDefinitions: Record = {} + for (const credDefId in credentialDefinitions) { + rsCredentialDefinitions[credDefId] = CredentialDefinition.load(JSON.stringify(credentialDefinitions[credDefId])) + } + + const rsSchemas: Record = {} + for (const schemaId in schemas) { + rsSchemas[schemaId] = Schema.load(JSON.stringify(schemas[schemaId])) + } + + const revocationRegistryDefinitions: Record = {} + const lists = [] + + for (const revocationRegistryDefinitionId in revocationStates) { + const { definition, revocationStatusLists } = options.revocationStates[revocationRegistryDefinitionId] + + revocationRegistryDefinitions[revocationRegistryDefinitionId] = RevocationRegistryDefinition.load( + JSON.stringify(definition) + ) + + for (const timestamp in revocationStatusLists) { + lists.push( + RevocationStatusList.create({ + issuanceByDefault: true, + revocationRegistryDefinition: revocationRegistryDefinitions[revocationRegistryDefinitionId], + revocationRegistryDefinitionId, + timestamp: Number(timestamp), + }) + ) + } + } + + return presentation.verify({ + presentationRequest: PresentationRequest.load(JSON.stringify(proofRequest)), + credentialDefinitions: rsCredentialDefinitions, + schemas: rsSchemas, + revocationRegistryDefinitions, + revocationStatusLists: lists, + }) + } catch (error) { + throw new AnonCredsRsError('Error verifying proof', { cause: error }) + } + } +} diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts new file mode 100644 index 0000000000..f0585f6ffb --- /dev/null +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -0,0 +1,501 @@ +import type { + AnonCredsCredentialDefinition, + AnonCredsProofRequest, + AnonCredsRequestedCredentials, + AnonCredsRevocationStatusList, + AnonCredsCredential, + AnonCredsSchema, +} from '@aries-framework/anoncreds' + +import { + AnonCredsHolderServiceSymbol, + AnonCredsLinkSecretRecord, + AnonCredsCredentialRecord, +} from '@aries-framework/anoncreds' +import { anoncreds, RevocationRegistryDefinition } from '@hyperledger/anoncreds-nodejs' + +import { AnonCredsCredentialDefinitionRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialDefinitionRepository' +import { AnonCredsCredentialRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialRepository' +import { AnonCredsLinkSecretRepository } from '../../../../anoncreds/src/repository/AnonCredsLinkSecretRepository' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' +import { AnonCredsRsHolderService } from '../AnonCredsRsHolderService' + +import { + createCredentialDefinition, + createCredentialForHolder, + createCredentialOffer, + createLinkSecret, +} from './helpers' + +const agentConfig = getAgentConfig('AnonCredsRsHolderServiceTest') +const anonCredsHolderService = new AnonCredsRsHolderService() + +jest.mock('../../../../anoncreds/src/repository/AnonCredsCredentialDefinitionRepository') +const CredentialDefinitionRepositoryMock = + AnonCredsCredentialDefinitionRepository as jest.Mock +const credentialDefinitionRepositoryMock = new CredentialDefinitionRepositoryMock() + +jest.mock('../../../../anoncreds/src/repository/AnonCredsLinkSecretRepository') +const AnonCredsLinkSecretRepositoryMock = AnonCredsLinkSecretRepository as jest.Mock +const anoncredsLinkSecretRepositoryMock = new AnonCredsLinkSecretRepositoryMock() + +jest.mock('../../../../anoncreds/src/repository/AnonCredsCredentialRepository') +const AnonCredsCredentialRepositoryMock = AnonCredsCredentialRepository as jest.Mock +const anoncredsCredentialRepositoryMock = new AnonCredsCredentialRepositoryMock() + +const agentContext = getAgentContext({ + registerInstances: [ + [AnonCredsCredentialDefinitionRepository, credentialDefinitionRepositoryMock], + [AnonCredsLinkSecretRepository, anoncredsLinkSecretRepositoryMock], + [AnonCredsCredentialRepository, anoncredsCredentialRepositoryMock], + [AnonCredsHolderServiceSymbol, anonCredsHolderService], + ], + agentConfig, +}) + +describe('AnonCredsRsHolderService', () => { + const getByCredentialIdMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'getByCredentialId') + + afterEach(() => { + getByCredentialIdMock.mockClear() + }) + + test('createCredentialRequest', async () => { + mockFunction(anoncredsLinkSecretRepositoryMock.getByLinkSecretId).mockResolvedValue( + new AnonCredsLinkSecretRecord({ linkSecretId: 'linkSecretId', value: createLinkSecret() }) + ) + + const { credentialDefinition, keyCorrectnessProof } = createCredentialDefinition({ + attributeNames: ['phoneNumber'], + issuerId: 'issuer:uri', + }) + const credentialOffer = createCredentialOffer(keyCorrectnessProof) + + const { credentialRequest } = await anonCredsHolderService.createCredentialRequest(agentContext, { + credentialDefinition, + credentialOffer, + linkSecretId: 'linkSecretId', + }) + + expect(credentialRequest.cred_def_id).toBe('creddef:uri') + expect(credentialRequest.prover_did).toBeUndefined() + }) + + test('createLinkSecret', async () => { + let linkSecret = await anonCredsHolderService.createLinkSecret(agentContext, { + linkSecretId: 'linkSecretId', + }) + + expect(linkSecret.linkSecretId).toBe('linkSecretId') + expect(linkSecret.linkSecretValue).toBeDefined() + + linkSecret = await anonCredsHolderService.createLinkSecret(agentContext) + + expect(linkSecret.linkSecretId).toBeDefined() + expect(linkSecret.linkSecretValue).toBeDefined() + }) + + test('createProof', async () => { + const proofRequest: AnonCredsProofRequest = { + nonce: anoncreds.generateNonce(), + name: 'pres_req_1', + version: '0.1', + requested_attributes: { + attr1_referent: { + name: 'name', + restrictions: [{ issuer_did: 'issuer:uri' }], + }, + attr2_referent: { + name: 'phoneNumber', + }, + attr3_referent: { + name: 'age', + }, + attr4_referent: { + names: ['name', 'height'], + }, + attr5_referent: { + name: 'favouriteSport', + }, + }, + requested_predicates: { + predicate1_referent: { name: 'age', p_type: '>=' as const, p_value: 18 }, + }, + //non_revoked: { from: 10, to: 200 }, + } + + const { + credentialDefinition: personCredentialDefinition, + credentialDefinitionPrivate: personCredentialDefinitionPrivate, + keyCorrectnessProof: personKeyCorrectnessProof, + } = createCredentialDefinition({ + attributeNames: ['name', 'age', 'sex', 'height'], + issuerId: 'issuer:uri', + }) + + const { + credentialDefinition: phoneCredentialDefinition, + credentialDefinitionPrivate: phoneCredentialDefinitionPrivate, + keyCorrectnessProof: phoneKeyCorrectnessProof, + } = createCredentialDefinition({ + attributeNames: ['phoneNumber'], + issuerId: 'issuer:uri', + }) + + const linkSecret = createLinkSecret() + + mockFunction(anoncredsLinkSecretRepositoryMock.getByLinkSecretId).mockResolvedValue( + new AnonCredsLinkSecretRecord({ linkSecretId: 'linkSecretId', value: linkSecret }) + ) + + const { + credential: personCredential, + credentialInfo: personCredentialInfo, + revocationRegistryDefinition: personRevRegDef, + tailsPath: personTailsPath, + } = createCredentialForHolder({ + attributes: { + name: 'John', + sex: 'M', + height: '179', + age: '19', + }, + credentialDefinition: personCredentialDefinition, + schemaId: 'personschema:uri', + credentialDefinitionId: 'personcreddef:uri', + credentialDefinitionPrivate: personCredentialDefinitionPrivate, + keyCorrectnessProof: personKeyCorrectnessProof, + linkSecret, + linkSecretId: 'linkSecretId', + credentialId: 'personCredId', + revocationRegistryDefinitionId: 'personrevregid:uri', + }) + + const { + credential: phoneCredential, + credentialInfo: phoneCredentialInfo, + revocationRegistryDefinition: phoneRevRegDef, + tailsPath: phoneTailsPath, + } = createCredentialForHolder({ + attributes: { + phoneNumber: 'linkSecretId56', + }, + credentialDefinition: phoneCredentialDefinition, + schemaId: 'phoneschema:uri', + credentialDefinitionId: 'phonecreddef:uri', + credentialDefinitionPrivate: phoneCredentialDefinitionPrivate, + keyCorrectnessProof: phoneKeyCorrectnessProof, + linkSecret, + linkSecretId: 'linkSecretId', + credentialId: 'phoneCredId', + revocationRegistryDefinitionId: 'phonerevregid:uri', + }) + + const requestedCredentials: AnonCredsRequestedCredentials = { + selfAttestedAttributes: { attr5_referent: 'football' }, + requestedAttributes: { + attr1_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, + attr2_referent: { credentialId: 'phoneCredId', credentialInfo: phoneCredentialInfo, revealed: true }, + attr3_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, + attr4_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, + }, + requestedPredicates: { + predicate1_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo }, + }, + } + + getByCredentialIdMock.mockResolvedValueOnce( + new AnonCredsCredentialRecord({ + credential: personCredential, + credentialId: 'personCredId', + linkSecretId: 'linkSecretId', + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + }) + ) + getByCredentialIdMock.mockResolvedValueOnce( + new AnonCredsCredentialRecord({ + credential: phoneCredential, + credentialId: 'phoneCredId', + linkSecretId: 'linkSecretId', + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + }) + ) + + const revocationRegistries = { + 'personrevregid:uri': { + tailsFilePath: personTailsPath, + definition: JSON.parse(anoncreds.getJson({ objectHandle: personRevRegDef })), + revocationStatusLists: { '1': {} as AnonCredsRevocationStatusList }, + }, + 'phonerevregid:uri': { + tailsFilePath: phoneTailsPath, + definition: JSON.parse(anoncreds.getJson({ objectHandle: phoneRevRegDef })), + revocationStatusLists: { '1': {} as AnonCredsRevocationStatusList }, + }, + } + + const proof = await anonCredsHolderService.createProof(agentContext, { + credentialDefinitions: { + 'personcreddef:uri': personCredentialDefinition as AnonCredsCredentialDefinition, + 'phonecreddef:uri': phoneCredentialDefinition as AnonCredsCredentialDefinition, + }, + proofRequest, + requestedCredentials, + schemas: { + 'phoneschema:uri': { attrNames: ['phoneNumber'], issuerId: 'issuer:uri', name: 'phoneschema', version: '1' }, + 'personschema:uri': { + attrNames: ['name', 'sex', 'height', 'age'], + issuerId: 'issuer:uri', + name: 'personschema', + version: '1', + }, + }, + revocationRegistries, + }) + + expect(getByCredentialIdMock).toHaveBeenCalledTimes(2) + // TODO: check proof object + }) + + describe('getCredentialsForProofRequest', () => { + const findByQueryMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'findByQuery') + + const proofRequest: AnonCredsProofRequest = { + nonce: anoncreds.generateNonce(), + name: 'pres_req_1', + version: '0.1', + requested_attributes: { + attr1_referent: { + name: 'name', + restrictions: [{ issuer_did: 'issuer:uri' }], + }, + attr2_referent: { + name: 'phoneNumber', + }, + attr3_referent: { + name: 'age', + restrictions: [{ schema_id: 'schemaid:uri', schema_name: 'schemaName' }, { schema_version: '1.0' }], + }, + attr4_referent: { + names: ['name', 'height'], + restrictions: [{ cred_def_id: 'crededefid:uri', issuer_id: 'issuerid:uri' }], + }, + }, + requested_predicates: { + predicate1_referent: { name: 'age', p_type: '>=' as const, p_value: 18 }, + }, + } + + beforeEach(() => { + findByQueryMock.mockResolvedValue([]) + }) + + afterEach(() => { + findByQueryMock.mockClear() + }) + + test('invalid referent', async () => { + await expect( + anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'name', + }) + ).rejects.toThrowError() + }) + + test('referent with single restriction', async () => { + await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'attr1_referent', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + attributes: ['name'], + issuerId: 'issuer:uri', + }) + }) + + test('referent without restrictions', async () => { + await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'attr2_referent', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + attributes: ['phoneNumber'], + }) + }) + + test('referent with multiple, complex restrictions', async () => { + await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'attr3_referent', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + attributes: ['age'], + $or: [{ schemaId: 'schemaid:uri', schemaName: 'schemaName' }, { schemaVersion: '1.0' }], + }) + }) + + test('referent with multiple names and restrictions', async () => { + await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'attr4_referent', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + attributes: ['name', 'height'], + credentialDefinitionId: 'crededefid:uri', + issuerId: 'issuerid:uri', + }) + }) + + test('predicate referent', async () => { + await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'predicate1_referent', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + attributes: ['age'], + }) + }) + }) + + test('deleteCredential', async () => { + getByCredentialIdMock.mockRejectedValueOnce(new Error()) + getByCredentialIdMock.mockResolvedValueOnce( + new AnonCredsCredentialRecord({ + credential: {} as AnonCredsCredential, + credentialId: 'personCredId', + linkSecretId: 'linkSecretId', + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + }) + ) + + expect(anonCredsHolderService.deleteCredential(agentContext, 'credentialId')).rejects.toThrowError() + + await anonCredsHolderService.deleteCredential(agentContext, 'credentialId') + + expect(getByCredentialIdMock).toHaveBeenCalledWith(agentContext, 'credentialId') + }) + + test('getCredential', async () => { + getByCredentialIdMock.mockRejectedValueOnce(new Error()) + + getByCredentialIdMock.mockResolvedValueOnce( + new AnonCredsCredentialRecord({ + credential: { + cred_def_id: 'credDefId', + schema_id: 'schemaId', + signature: 'signature', + signature_correctness_proof: 'signatureCorrectnessProof', + values: { attr1: { raw: 'value1', encoded: 'encvalue1' }, attr2: { raw: 'value2', encoded: 'encvalue2' } }, + rev_reg_id: 'revRegId', + } as AnonCredsCredential, + credentialId: 'myCredentialId', + credentialRevocationId: 'credentialRevocationId', + linkSecretId: 'linkSecretId', + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + }) + ) + expect( + anonCredsHolderService.getCredential(agentContext, { credentialId: 'myCredentialId' }) + ).rejects.toThrowError() + + const credentialInfo = await anonCredsHolderService.getCredential(agentContext, { credentialId: 'myCredentialId' }) + + expect(credentialInfo).toMatchObject({ + attributes: { attr1: 'value1', attr2: 'value2' }, + credentialDefinitionId: 'credDefId', + credentialId: 'myCredentialId', + revocationRegistryId: 'revRegId', + schemaId: 'schemaId', + credentialRevocationId: 'credentialRevocationId', + }) + }) + + test('storeCredential', async () => { + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = createCredentialDefinition({ + attributeNames: ['name', 'age', 'sex', 'height'], + issuerId: 'issuer:uri', + }) + + const linkSecret = createLinkSecret() + + mockFunction(anoncredsLinkSecretRepositoryMock.getByLinkSecretId).mockResolvedValue( + new AnonCredsLinkSecretRecord({ linkSecretId: 'linkSecretId', value: linkSecret }) + ) + + const schema: AnonCredsSchema = { + attrNames: ['name', 'sex', 'height', 'age'], + issuerId: 'issuerId', + name: 'schemaName', + version: '1', + } + + const { credential, revocationRegistryDefinition, credentialRequestMetadata } = createCredentialForHolder({ + attributes: { + name: 'John', + sex: 'M', + height: '179', + age: '19', + }, + credentialDefinition, + schemaId: 'personschema:uri', + credentialDefinitionId: 'personcreddef:uri', + credentialDefinitionPrivate, + keyCorrectnessProof, + linkSecret, + linkSecretId: 'linkSecretId', + credentialId: 'personCredId', + revocationRegistryDefinitionId: 'personrevregid:uri', + }) + + const saveCredentialMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'save') + + saveCredentialMock.mockResolvedValue() + + const credentialId = await anonCredsHolderService.storeCredential(agentContext, { + credential, + credentialDefinition, + schema, + credentialDefinitionId: 'personcreddefid:uri', + credentialRequestMetadata: JSON.parse(credentialRequestMetadata.toJson()), + credentialId: 'personCredId', + revocationRegistry: { + id: 'personrevregid:uri', + definition: JSON.parse(new RevocationRegistryDefinition(revocationRegistryDefinition.handle).toJson()), + }, + }) + + expect(credentialId).toBe('personCredId') + expect(saveCredentialMock).toHaveBeenCalledWith( + agentContext, + expect.objectContaining({ + // The stored credential is different from the one received originally + credentialId: 'personCredId', + linkSecretId: 'linkSecretId', + _tags: expect.objectContaining({ + issuerId: credentialDefinition.issuerId, + schemaName: 'schemaName', + schemaIssuerId: 'issuerId', + schemaVersion: '1', + }), + }) + ) + }) +}) diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts new file mode 100644 index 0000000000..3e23f27eb0 --- /dev/null +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -0,0 +1,224 @@ +import type { AnonCredsProofRequest } from '@aries-framework/anoncreds' + +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, + AnonCredsSchemaRepository, + AnonCredsSchemaRecord, + AnonCredsCredentialDefinitionRecord, + AnonCredsCredentialDefinitionRepository, + AnonCredsCredentialDefinitionPrivateRecord, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsKeyCorrectnessProofRecord, + AnonCredsLinkSecretRepository, + AnonCredsLinkSecretRecord, +} from '@aries-framework/anoncreds' +import { InjectionSymbols } from '@aries-framework/core' +import { anoncreds } from '@hyperledger/anoncreds-nodejs' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { encode } from '../../../../anoncreds/src/utils/credential' +import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { AnonCredsRsHolderService } from '../AnonCredsRsHolderService' +import { AnonCredsRsIssuerService } from '../AnonCredsRsIssuerService' +import { AnonCredsRsVerifierService } from '../AnonCredsRsVerifierService' + +const agentConfig = getAgentConfig('AnonCredsCredentialFormatServiceTest') +const anonCredsVerifierService = new AnonCredsRsVerifierService() +const anonCredsHolderService = new AnonCredsRsHolderService() +const anonCredsIssuerService = new AnonCredsRsIssuerService() +const storageService = new InMemoryStorageService() +const registry = new InMemoryAnonCredsRegistry() + +const agentContext = getAgentContext({ + registerInstances: [ + [InjectionSymbols.Stop$, new Subject()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.StorageService, storageService], + [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], + [AnonCredsHolderServiceSymbol, anonCredsHolderService], + [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + ], + agentConfig, +}) + +describe('AnonCredsRsServices', () => { + test('issuance flow without revocation', async () => { + const issuerId = 'issuer:uri' + + const schema = await anonCredsIssuerService.createSchema(agentContext, { + attrNames: ['name', 'age'], + issuerId, + name: 'Employee Credential', + version: '1.0.0', + }) + + const { schemaState, schemaMetadata } = await registry.registerSchema(agentContext, { + schema, + options: {}, + }) + + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = + await anonCredsIssuerService.createCredentialDefinition(agentContext, { + issuerId, + schemaId: schemaState.schemaId as string, + schema, + tag: 'Employee Credential', + supportRevocation: false, + }) + + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { + credentialDefinition, + options: {}, + }) + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + if (!credentialDefinitionPrivate || !keyCorrectnessProof) { + throw new Error('Failed to get private part of credential definition') + } + + await agentContext.dependencyManager.resolve(AnonCredsSchemaRepository).save( + agentContext, + new AnonCredsSchemaRecord({ + schema: schemaState.schema, + schemaId: schemaState.schemaId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionRepository).save( + agentContext, + new AnonCredsCredentialDefinitionRecord({ + credentialDefinition: credentialDefinitionState.credentialDefinition, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionPrivateRepository).save( + agentContext, + new AnonCredsCredentialDefinitionPrivateRecord({ + value: credentialDefinitionPrivate, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsKeyCorrectnessProofRepository).save( + agentContext, + new AnonCredsKeyCorrectnessProofRecord({ + value: keyCorrectnessProof, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + const credentialOffer = await anonCredsIssuerService.createCredentialOffer(agentContext, { + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + + const linkSecret = await anonCredsHolderService.createLinkSecret(agentContext, { linkSecretId: 'linkSecretId' }) + expect(linkSecret.linkSecretId).toBe('linkSecretId') + + await agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository).save( + agentContext, + new AnonCredsLinkSecretRecord({ + value: linkSecret.linkSecretValue, + linkSecretId: linkSecret.linkSecretId, + }) + ) + + const credentialRequestState = await anonCredsHolderService.createCredentialRequest(agentContext, { + credentialDefinition: credentialDefinitionState.credentialDefinition, + credentialOffer, + linkSecretId: linkSecret.linkSecretId, + }) + + const { credential } = await anonCredsIssuerService.createCredential(agentContext, { + credentialOffer, + credentialRequest: credentialRequestState.credentialRequest, + credentialValues: { name: { raw: 'John', encoded: encode('John') }, age: { raw: '25', encoded: encode('25') } }, + }) + + const credentialId = 'holderCredentialId' + + const storedId = await anonCredsHolderService.storeCredential(agentContext, { + credential, + credentialDefinition, + schema, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialRequestMetadata: credentialRequestState.credentialRequestMetadata, + credentialId, + }) + + expect(storedId).toEqual(credentialId) + + const credentialInfo = await anonCredsHolderService.getCredential(agentContext, { + credentialId, + }) + + expect(credentialInfo).toEqual({ + credentialId, + attributes: { + age: '25', + name: 'John', + }, + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + revocationRegistryId: null, + credentialRevocationId: undefined, // Should it be null in this case? + }) + + const proofRequest: AnonCredsProofRequest = { + nonce: anoncreds.generateNonce(), + name: 'pres_req_1', + version: '0.1', + requested_attributes: { + attr1_referent: { + name: 'name', + }, + attr2_referent: { + name: 'age', + }, + }, + requested_predicates: { + predicate1_referent: { name: 'age', p_type: '>=' as const, p_value: 18 }, + }, + } + + const proof = await anonCredsHolderService.createProof(agentContext, { + credentialDefinitions: { [credentialDefinitionState.credentialDefinitionId]: credentialDefinition }, + proofRequest, + requestedCredentials: { + requestedAttributes: { + attr1_referent: { credentialId, credentialInfo, revealed: true }, + attr2_referent: { credentialId, credentialInfo, revealed: true }, + }, + requestedPredicates: { + predicate1_referent: { credentialId, credentialInfo }, + }, + selfAttestedAttributes: {}, + }, + schemas: { [schemaState.schemaId]: schema }, + revocationRegistries: {}, + }) + + const verifiedProof = await anonCredsVerifierService.verifyProof({ + credentialDefinitions: { [credentialDefinitionState.credentialDefinitionId]: credentialDefinition }, + proof, + proofRequest, + schemas: { [schemaState.schemaId]: schema }, + revocationStates: {}, + }) + + expect(verifiedProof).toBeTruthy() + }) +}) diff --git a/packages/anoncreds-rs/src/services/__tests__/helpers.ts b/packages/anoncreds-rs/src/services/__tests__/helpers.ts new file mode 100644 index 0000000000..07d5b09f49 --- /dev/null +++ b/packages/anoncreds-rs/src/services/__tests__/helpers.ts @@ -0,0 +1,173 @@ +import type { AnonCredsCredentialInfo } from '@aries-framework/anoncreds' + +import { + anoncreds, + CredentialDefinition, + CredentialDefinitionPrivate, + CredentialOffer, + CredentialRequest, + KeyCorrectnessProof, + MasterSecret, + Schema, +} from '@hyperledger/anoncreds-shared' + +/** + * Creates a valid credential definition and returns its public and + * private part, including its key correctness proof + */ +export function createCredentialDefinition(options: { attributeNames: string[]; issuerId: string }) { + const { attributeNames, issuerId } = options + + const schema = Schema.create({ + issuerId, + attributeNames, + name: 'schema1', + version: '1', + }) + + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = CredentialDefinition.create({ + issuerId, + schema, + schemaId: 'schema:uri', + signatureType: 'CL', + supportRevocation: true, // FIXME: Revocation should not be mandatory but current anoncreds-rs is requiring it + tag: 'TAG', + }) + + return { + credentialDefinition: JSON.parse(credentialDefinition.toJson()), + credentialDefinitionPrivate: JSON.parse(credentialDefinitionPrivate.toJson()), + keyCorrectnessProof: JSON.parse(keyCorrectnessProof.toJson()), + schema: JSON.parse(schema.toJson()), + } +} + +/** + * Creates a valid credential offer and returns itsf + */ +export function createCredentialOffer(kcp: Record) { + const credentialOffer = CredentialOffer.create({ + credentialDefinitionId: 'creddef:uri', + keyCorrectnessProof: KeyCorrectnessProof.load(JSON.stringify(kcp)), + schemaId: 'schema:uri', + }) + return JSON.parse(credentialOffer.toJson()) +} + +/** + * + * @returns Creates a valid link secret value for anoncreds-rs + */ +export function createLinkSecret() { + return JSON.parse(MasterSecret.create().toJson()).value.ms as string +} + +export function createCredentialForHolder(options: { + credentialDefinition: Record + credentialDefinitionPrivate: Record + keyCorrectnessProof: Record + schemaId: string + credentialDefinitionId: string + attributes: Record + linkSecret: string + linkSecretId: string + credentialId: string + revocationRegistryDefinitionId: string +}) { + const { + credentialDefinition, + credentialDefinitionPrivate, + keyCorrectnessProof, + schemaId, + credentialDefinitionId, + attributes, + linkSecret, + linkSecretId, + credentialId, + revocationRegistryDefinitionId, + } = options + + const credentialOffer = CredentialOffer.create({ + credentialDefinitionId, + keyCorrectnessProof: KeyCorrectnessProof.load(JSON.stringify(keyCorrectnessProof)), + schemaId, + }) + + const { credentialRequest, credentialRequestMetadata } = CredentialRequest.create({ + credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), + credentialOffer, + masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecret } })), + masterSecretId: linkSecretId, + }) + + // FIXME: Revocation config should not be mandatory but current anoncreds-rs is requiring it + + const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate, tailsPath } = + createRevocationRegistryDefinition({ + credentialDefinitionId, + credentialDefinition, + }) + + const timeCreateRevStatusList = 12 + const revocationStatusList = anoncreds.createRevocationStatusList({ + timestamp: timeCreateRevStatusList, + issuanceByDefault: true, + revocationRegistryDefinition, + revocationRegistryDefinitionId: revocationRegistryDefinitionId, + }) + + // TODO: Use Credential.create (needs to update the paramters in anoncreds-rs) + const credentialObj = anoncreds.createCredential({ + credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)).handle, + credentialDefinitionPrivate: CredentialDefinitionPrivate.load(JSON.stringify(credentialDefinitionPrivate)).handle, + credentialOffer: credentialOffer.handle, + credentialRequest: credentialRequest.handle, + attributeRawValues: attributes, + revocationRegistryId: revocationRegistryDefinitionId, + revocationStatusList, + revocationConfiguration: { + registryIndex: 9, + revocationRegistryDefinition, + revocationRegistryDefinitionPrivate, + tailsPath, + }, + }) + const credential = anoncreds.getJson({ objectHandle: credentialObj }) + + const credentialInfo: AnonCredsCredentialInfo = { + attributes, + credentialDefinitionId, + credentialId, + schemaId, + } + return { + credential: JSON.parse(credential), + credentialInfo, + revocationRegistryDefinition, + tailsPath, + credentialRequestMetadata, + } +} + +export function createRevocationRegistryDefinition(options: { + credentialDefinitionId: string + credentialDefinition: Record +}) { + const { credentialDefinitionId, credentialDefinition } = options + const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate } = + anoncreds.createRevocationRegistryDefinition({ + credentialDefinitionId, + credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)).handle, + issuerId: 'mock:uri', + tag: 'some_tag', + revocationRegistryType: 'CL_ACCUM', + maximumCredentialNumber: 10, + }) + + const tailsPath = anoncreds.revocationRegistryDefinitionGetAttribute({ + objectHandle: revocationRegistryDefinition, + name: 'tails_location', + }) + + return { revocationRegistryDefinition, revocationRegistryDefinitionPrivate, tailsPath } +} diff --git a/packages/anoncreds-rs/src/services/index.ts b/packages/anoncreds-rs/src/services/index.ts new file mode 100644 index 0000000000..b675ab0025 --- /dev/null +++ b/packages/anoncreds-rs/src/services/index.ts @@ -0,0 +1,3 @@ +export { AnonCredsRsHolderService } from './AnonCredsRsHolderService' +export { AnonCredsRsIssuerService } from './AnonCredsRsIssuerService' +export { AnonCredsRsVerifierService } from './AnonCredsRsVerifierService' diff --git a/packages/anoncreds-rs/src/types.ts b/packages/anoncreds-rs/src/types.ts new file mode 100644 index 0000000000..2694976be7 --- /dev/null +++ b/packages/anoncreds-rs/src/types.ts @@ -0,0 +1,4 @@ +import type { Anoncreds } from '@hyperledger/anoncreds-shared' + +export const AnonCredsRsSymbol = Symbol('AnonCredsRs') +export type { Anoncreds } diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts new file mode 100644 index 0000000000..fc2ce9ec87 --- /dev/null +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -0,0 +1,277 @@ +import { + AnonCredsModuleConfig, + LegacyIndyCredentialFormatService, + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, + AnonCredsRegistryService, + AnonCredsSchemaRecord, + AnonCredsSchemaRepository, + AnonCredsCredentialDefinitionRepository, + AnonCredsCredentialDefinitionRecord, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsCredentialDefinitionPrivateRecord, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsKeyCorrectnessProofRecord, + AnonCredsLinkSecretRepository, + AnonCredsLinkSecretRecord, +} from '@aries-framework/anoncreds' +import { + CredentialState, + CredentialExchangeRecord, + CredentialPreviewAttribute, + InjectionSymbols, +} from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderService' +import { AnonCredsRsIssuerService } from '../src/services/AnonCredsRsIssuerService' +import { AnonCredsRsVerifierService } from '../src/services/AnonCredsRsVerifierService' + +const registry = new InMemoryAnonCredsRegistry() +const anonCredsModuleConfig = new AnonCredsModuleConfig({ + registries: [registry], +}) + +const agentConfig = getAgentConfig('LegacyIndyCredentialFormatService') +const anonCredsVerifierService = new AnonCredsRsVerifierService() +const anonCredsHolderService = new AnonCredsRsHolderService() +const anonCredsIssuerService = new AnonCredsRsIssuerService() + +const inMemoryStorageService = new InMemoryStorageService() +const agentContext = getAgentContext({ + registerInstances: [ + [InjectionSymbols.Stop$, new Subject()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.StorageService, inMemoryStorageService], + [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], + [AnonCredsHolderServiceSymbol, anonCredsHolderService], + [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + [AnonCredsRegistryService, new AnonCredsRegistryService()], + [AnonCredsModuleConfig, anonCredsModuleConfig], + ], + agentConfig, +}) + +const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() + +describe('LegacyIndyCredentialFormatService using anoncreds-rs', () => { + test('issuance flow starting from proposal without negotiation and without revocation', async () => { + // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) + const indyDid = 'TL1EaPFCZ8Si5aUrqScBDt' + + const schema = await anonCredsIssuerService.createSchema(agentContext, { + attrNames: ['name', 'age'], + issuerId: indyDid, + name: 'Employee Credential', + version: '1.0.0', + }) + + const { schemaState, schemaMetadata } = await registry.registerSchema(agentContext, { + schema, + options: {}, + }) + + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = + await anonCredsIssuerService.createCredentialDefinition(agentContext, { + issuerId: indyDid, + schemaId: schemaState.schemaId as string, + schema, + tag: 'Employee Credential', + supportRevocation: false, + }) + + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { + credentialDefinition, + options: {}, + }) + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + if (!credentialDefinitionPrivate || !keyCorrectnessProof) { + throw new Error('Failed to get private part of credential definition') + } + + await agentContext.dependencyManager.resolve(AnonCredsSchemaRepository).save( + agentContext, + new AnonCredsSchemaRecord({ + schema: schemaState.schema, + schemaId: schemaState.schemaId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionRepository).save( + agentContext, + new AnonCredsCredentialDefinitionRecord({ + credentialDefinition: credentialDefinitionState.credentialDefinition, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionPrivateRepository).save( + agentContext, + new AnonCredsCredentialDefinitionPrivateRecord({ + value: credentialDefinitionPrivate, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsKeyCorrectnessProofRepository).save( + agentContext, + new AnonCredsKeyCorrectnessProofRecord({ + value: keyCorrectnessProof, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + const linkSecret = await anonCredsHolderService.createLinkSecret(agentContext, { linkSecretId: 'linkSecretId' }) + expect(linkSecret.linkSecretId).toBe('linkSecretId') + + await agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository).save( + agentContext, + new AnonCredsLinkSecretRecord({ + value: linkSecret.linkSecretValue, + linkSecretId: linkSecret.linkSecretId, + }) + ) + + const holderCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalSent, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const issuerCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalReceived, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const credentialAttributes = [ + new CredentialPreviewAttribute({ + name: 'name', + value: 'John', + }), + new CredentialPreviewAttribute({ + name: 'age', + value: '25', + }), + ] + + // Holder creates proposal + holderCredentialRecord.credentialAttributes = credentialAttributes + const { attachment: proposalAttachment } = await legacyIndyCredentialFormatService.createProposal(agentContext, { + credentialRecord: holderCredentialRecord, + credentialFormats: { + indy: { + attributes: credentialAttributes, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + }, + }) + + // Issuer processes and accepts proposal + await legacyIndyCredentialFormatService.processProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: proposalAttachment, + }) + // Set attributes on the credential record, this is normally done by the protocol service + issuerCredentialRecord.credentialAttributes = credentialAttributes + const { attachment: offerAttachment } = await legacyIndyCredentialFormatService.acceptProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + proposalAttachment: proposalAttachment, + }) + + // Holder processes and accepts offer + await legacyIndyCredentialFormatService.processOffer(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: offerAttachment, + }) + const { attachment: requestAttachment } = await legacyIndyCredentialFormatService.acceptOffer(agentContext, { + credentialRecord: holderCredentialRecord, + offerAttachment, + credentialFormats: { + indy: { + linkSecretId: linkSecret.linkSecretId, + }, + }, + }) + + // Issuer processes and accepts request + await legacyIndyCredentialFormatService.processRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: requestAttachment, + }) + const { attachment: credentialAttachment } = await legacyIndyCredentialFormatService.acceptRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + requestAttachment, + offerAttachment, + }) + + // Holder processes and accepts credential + await legacyIndyCredentialFormatService.processCredential(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: credentialAttachment, + requestAttachment, + }) + + expect(holderCredentialRecord.credentials).toEqual([ + { credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String) }, + ]) + + const credentialId = holderCredentialRecord.credentials[0].credentialRecordId + const anonCredsCredential = await anonCredsHolderService.getCredential(agentContext, { + credentialId, + }) + + expect(anonCredsCredential).toEqual({ + credentialId, + attributes: { + age: '25', + name: 'John', + }, + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + revocationRegistryId: null, + credentialRevocationId: undefined, // FIXME: should be null? + }) + + expect(holderCredentialRecord.metadata.data).toEqual({ + '_anonCreds/anonCredsCredential': { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + '_anonCreds/anonCredsCredentialRequest': { + master_secret_blinding_data: expect.any(Object), + master_secret_name: expect.any(String), + nonce: expect.any(String), + }, + }) + + expect(issuerCredentialRecord.metadata.data).toEqual({ + '_anonCreds/anonCredsCredential': { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + }) + }) +}) diff --git a/packages/anoncreds-rs/tests/setup.ts b/packages/anoncreds-rs/tests/setup.ts new file mode 100644 index 0000000000..a5fef0aec8 --- /dev/null +++ b/packages/anoncreds-rs/tests/setup.ts @@ -0,0 +1,3 @@ +import '@hyperledger/anoncreds-nodejs' + +jest.setTimeout(60000) diff --git a/packages/anoncreds-rs/tsconfig.build.json b/packages/anoncreds-rs/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/anoncreds-rs/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/anoncreds-rs/tsconfig.json b/packages/anoncreds-rs/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/anoncreds-rs/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts index fd6ebf7fcb..e08109f56f 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts @@ -35,7 +35,9 @@ export interface AnonCredsAcceptProposalFormat { * This defines the module payload for calling CredentialsApi.acceptOffer. No options are available for this * method, so it's an empty object */ -export type AnonCredsAcceptOfferFormat = Record +export interface AnonCredsAcceptOfferFormat { + linkSecretId?: string +} /** * This defines the module payload for calling CredentialsApi.offerCredential diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index e1fd945937..6be55555a4 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -209,7 +209,12 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic public async acceptOffer( agentContext: AgentContext, - { credentialRecord, attachId, offerAttachment }: FormatAcceptOfferOptions + { + credentialRecord, + attachId, + offerAttachment, + credentialFormats, + }: FormatAcceptOfferOptions ): Promise { const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) @@ -232,6 +237,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const { credentialRequest, credentialRequestMetadata } = await holderService.createCredentialRequest(agentContext, { credentialOffer, credentialDefinition, + linkSecretId: credentialFormats?.indy?.linkSecretId, }) credentialRecord.metadata.set( @@ -357,6 +363,15 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic ) } + const schemaResult = await registryService + .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) + .getSchema(agentContext, anonCredsCredential.schema_id) + if (!schemaResult.schema) { + throw new AriesFrameworkError( + `Unable to resolve schema ${anonCredsCredential.schema_id}: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` + ) + } + // Resolve revocation registry if credential is revocable let revocationRegistryResult: null | GetRevocationRegistryDefinitionReturn = null if (anonCredsCredential.rev_reg_id) { @@ -381,6 +396,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic credential: anonCredsCredential, credentialDefinitionId: credentialDefinitionResult.credentialDefinitionId, credentialDefinition: credentialDefinitionResult.credentialDefinition, + schema: schemaResult.schema, revocationRegistry: revocationRegistryResult?.revocationRegistryDefinition ? { definition: revocationRegistryResult.revocationRegistryDefinition, diff --git a/packages/anoncreds/src/models/registry.ts b/packages/anoncreds/src/models/registry.ts index f4f3429ec2..31314ada51 100644 --- a/packages/anoncreds/src/models/registry.ts +++ b/packages/anoncreds/src/models/registry.ts @@ -19,17 +19,19 @@ export interface AnonCredsCredentialDefinition { export interface AnonCredsRevocationRegistryDefinition { issuerId: string - type: 'CL_ACCUM' + revocDefType: 'CL_ACCUM' credDefId: string tag: string - publicKeys: { - accumKey: { - z: string + value: { + publicKeys: { + accumKey: { + z: string + } } + maxCredNum: number + tailsLocation: string + tailsHash: string } - maxCredNum: number - tailsLocation: string - tailsHash: string } export interface AnonCredsRevocationStatusList { diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts new file mode 100644 index 0000000000..3d4d0958b7 --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts @@ -0,0 +1,76 @@ +import type { AnonCredsCredential } from '../models' + +import { BaseRecord, utils } from '@aries-framework/core' + +export interface AnonCredsCredentialRecordProps { + id?: string + credential: AnonCredsCredential + credentialId: string + credentialRevocationId?: string + linkSecretId: string + schemaName: string + schemaVersion: string + schemaIssuerId: string + issuerId: string +} + +export type DefaultAnonCredsCredentialTags = { + credentialId: string + linkSecretId: string + credentialDefinitionId: string + credentialRevocationId?: string + revocationRegistryId?: string + schemaId: string + attributes: string[] +} + +export type CustomAnonCredsCredentialTags = { + schemaName: string + schemaVersion: string + schemaIssuerId: string + issuerId: string +} + +export class AnonCredsCredentialRecord extends BaseRecord< + DefaultAnonCredsCredentialTags, + CustomAnonCredsCredentialTags +> { + public static readonly type = 'AnonCredsCredentialRecord' + public readonly type = AnonCredsCredentialRecord.type + + public readonly credentialId!: string + public readonly credentialRevocationId?: string + public readonly linkSecretId!: string + public readonly credential!: AnonCredsCredential + + public constructor(props: AnonCredsCredentialRecordProps) { + super() + + if (props) { + this.id = props.id ?? utils.uuid() + this.credentialId = props.credentialId + this.credential = props.credential + this.credentialRevocationId = props.credentialRevocationId + this.linkSecretId = props.linkSecretId + this.setTags({ + issuerId: props.issuerId, + schemaIssuerId: props.schemaIssuerId, + schemaName: props.schemaName, + schemaVersion: props.schemaVersion, + }) + } + } + + public getTags() { + return { + ...this._tags, + credentialDefinitionId: this.credential.cred_def_id, + schemaId: this.credential.schema_id, + credentialId: this.credentialId, + credentialRevocationId: this.credentialRevocationId, + revocationRegistryId: this.credential.rev_reg_id, + linkSecretId: this.linkSecretId, + attributes: Object.keys(this.credential.values), + } + } +} diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialRepository.ts b/packages/anoncreds/src/repository/AnonCredsCredentialRepository.ts new file mode 100644 index 0000000000..fb02878439 --- /dev/null +++ b/packages/anoncreds/src/repository/AnonCredsCredentialRepository.ts @@ -0,0 +1,31 @@ +import type { AgentContext } from '@aries-framework/core' + +import { Repository, InjectionSymbols, StorageService, EventEmitter, injectable, inject } from '@aries-framework/core' + +import { AnonCredsCredentialRecord } from './AnonCredsCredentialRecord' + +@injectable() +export class AnonCredsCredentialRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(AnonCredsCredentialRecord, storageService, eventEmitter) + } + + public async getByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.getSingleByQuery(agentContext, { credentialDefinitionId }) + } + + public async findByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { + return this.findSingleByQuery(agentContext, { credentialDefinitionId }) + } + + public async getByCredentialId(agentContext: AgentContext, credentialId: string) { + return this.getSingleByQuery(agentContext, { credentialId }) + } + + public async findByCredentialId(agentContext: AgentContext, credentialId: string) { + return this.findSingleByQuery(agentContext, { credentialId }) + } +} diff --git a/packages/anoncreds/src/repository/index.ts b/packages/anoncreds/src/repository/index.ts index 5e17e19941..c4fb3bbe80 100644 --- a/packages/anoncreds/src/repository/index.ts +++ b/packages/anoncreds/src/repository/index.ts @@ -1,3 +1,5 @@ +export * from './AnonCredsCredentialRecord' +export * from './AnonCredsCredentialRepository' export * from './AnonCredsCredentialDefinitionRecord' export * from './AnonCredsCredentialDefinitionRepository' export * from './AnonCredsCredentialDefinitionPrivateRecord' diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index fcbc5e913c..747e3fcfed 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -47,6 +47,7 @@ export interface StoreCredentialOptions { credentialRequestMetadata: AnonCredsCredentialRequestMetadata credential: AnonCredsCredential credentialDefinition: AnonCredsCredentialDefinition + schema: AnonCredsSchema credentialDefinitionId: string credentialId?: string revocationRegistry?: { diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index e7abd466c4..f905c92db9 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -43,16 +43,18 @@ const existingRevocationRegistryDefinitions = { 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG': { credDefId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', issuerId: 'VsKV7grR1BUE29mG2Fm2kX', - maxCredNum: 100, - type: 'CL_ACCUM', - publicKeys: { - accumKey: { - z: 'ab81257c-be63-4051-9e21-c7d384412f64', + revocDefType: 'CL_ACCUM', + value: { + publicKeys: { + accumKey: { + z: 'ab81257c-be63-4051-9e21-c7d384412f64', + }, }, + maxCredNum: 100, + tailsHash: 'ab81257c-be63-4051-9e21-c7d384412f64', + tailsLocation: 'http://localhost:7200/tails', }, tag: 'TAG', - tailsHash: 'ab81257c-be63-4051-9e21-c7d384412f64', - tailsLocation: 'http://localhost:7200/tails', }, } as const diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index ee9c82dfa0..91d22659c4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -33,7 +33,7 @@ export * from './storage/BaseRecord' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' export * from './storage/RepositoryEvents' -export { StorageService, Query, BaseRecordConstructor } from './storage/StorageService' +export { StorageService, Query, SimpleQuery, BaseRecordConstructor } from './storage/StorageService' export * from './storage/migration' export { getDirFromFilePath } from './utils/path' export { InjectionSymbols } from './constants' diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index 6a91928f70..e976d514e4 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -63,12 +63,14 @@ export function anonCredsRevocationRegistryDefinitionFromIndySdk( return { issuerId, credDefId: revocationRegistryDefinition.credDefId, - maxCredNum: revocationRegistryDefinition.value.maxCredNum, - publicKeys: revocationRegistryDefinition.value.publicKeys, + value: { + maxCredNum: revocationRegistryDefinition.value.maxCredNum, + publicKeys: revocationRegistryDefinition.value.publicKeys, + tailsHash: revocationRegistryDefinition.value.tailsHash, + tailsLocation: revocationRegistryDefinition.value.tailsLocation, + }, tag: revocationRegistryDefinition.tag, - tailsHash: revocationRegistryDefinition.value.tailsHash, - tailsLocation: revocationRegistryDefinition.value.tailsLocation, - type: 'CL_ACCUM', + revocDefType: 'CL_ACCUM', } } @@ -79,14 +81,14 @@ export function indySdkRevocationRegistryDefinitionFromAnonCreds( return { id: revocationRegistryDefinitionId, credDefId: revocationRegistryDefinition.credDefId, - revocDefType: revocationRegistryDefinition.type, + revocDefType: revocationRegistryDefinition.revocDefType, tag: revocationRegistryDefinition.tag, value: { issuanceType: 'ISSUANCE_BY_DEFAULT', // NOTE: we always use ISSUANCE_BY_DEFAULT when passing to the indy-sdk. It doesn't matter, as we have the revocation List with the full state - maxCredNum: revocationRegistryDefinition.maxCredNum, - publicKeys: revocationRegistryDefinition.publicKeys, - tailsHash: revocationRegistryDefinition.tailsHash, - tailsLocation: revocationRegistryDefinition.tailsLocation, + maxCredNum: revocationRegistryDefinition.value.maxCredNum, + publicKeys: revocationRegistryDefinition.value.publicKeys, + tailsHash: revocationRegistryDefinition.value.tailsHash, + tailsLocation: revocationRegistryDefinition.value.tailsLocation, }, ver: '1.0', } @@ -103,7 +105,7 @@ export function anonCredsRevocationStatusListFromIndySdk( const defaultState = isIssuanceByDefault ? 0 : 1 // Fill with default value - const revocationList = new Array(revocationRegistryDefinition.maxCredNum).fill(defaultState) + const revocationList = new Array(revocationRegistryDefinition.value.maxCredNum).fill(defaultState) // Set all `issuer` indexes to 0 (not revoked) for (const issued of delta.value.issued ?? []) { diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index 0b2a73ebb4..e1fc3f2f60 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -131,6 +131,7 @@ export class InMemoryStorageService implement } const records = Object.values(this.records) + .filter((record) => record.type === recordClass.type) .filter((record) => { const tags = record.tags as TagsBase diff --git a/yarn.lock b/yarn.lock index 4a8447c5fe..0f3abfb2f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -858,6 +858,24 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.5": + version "0.1.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.5.tgz#71b6dbcfab72f826bcead2b79dafe47fc8f1567c" + integrity sha512-BX/OxQjTMoCAJP4fgJEcGct1ZnNYgybO+VLD5LyzHW4nmTFOJo3TXy5IYHAJv61b/uNUQ/2GMYmPKLSLOVExNw== + dependencies: + "@hyperledger/anoncreds-shared" "0.1.0-dev.5" + "@mapbox/node-pre-gyp" "^1.0.10" + ffi-napi "4.0.3" + node-cache "5.1.2" + ref-array-di "1.2.2" + ref-napi "3.0.3" + ref-struct-di "1.1.1" + +"@hyperledger/anoncreds-shared@0.1.0-dev.5", "@hyperledger/anoncreds-shared@^0.1.0-dev.5": + version "0.1.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.5.tgz#653a8ec1ac83eae3af8aabb7fa5609bb0c3453b2" + integrity sha512-NPbjZd7WJN/eKtHtYcOy+E9Ebh0YkZ7bre59zWD3w66aiehZrSLbL5+pjY9shrSIN1h05t0XnvT1JZKTtXgqcQ== + "@hyperledger/aries-askar-nodejs@^0.1.0-dev.1": version "0.1.0-dev.1" resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.1.tgz#b384d422de48f0ce5918e1612d2ca32ebd160520" @@ -5357,7 +5375,7 @@ fetch-blob@^2.1.1: resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== -ffi-napi@^4.0.3: +ffi-napi@4.0.3, ffi-napi@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/ffi-napi/-/ffi-napi-4.0.3.tgz#27a8d42a8ea938457154895c59761fbf1a10f441" integrity sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg== @@ -8417,7 +8435,7 @@ node-addon-api@^3.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== -node-cache@^5.1.2: +node-cache@5.1.2, node-cache@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== @@ -9723,7 +9741,7 @@ reduce-flatten@^2.0.0: resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== -ref-array-di@^1.2.2: +ref-array-di@1.2.2, ref-array-di@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/ref-array-di/-/ref-array-di-1.2.2.tgz#ceee9d667d9c424b5a91bb813457cc916fb1f64d" integrity sha512-jhCmhqWa7kvCVrWhR/d7RemkppqPUdxEil1CtTtm7FkZV8LcHHCK3Or9GinUiFP5WY3k0djUkMvhBhx49Jb2iA== @@ -9731,7 +9749,7 @@ ref-array-di@^1.2.2: array-index "^1.0.0" debug "^3.1.0" -"ref-napi@^2.0.1 || ^3.0.2", ref-napi@^3.0.3: +ref-napi@3.0.3, "ref-napi@^2.0.1 || ^3.0.2", ref-napi@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/ref-napi/-/ref-napi-3.0.3.tgz#e259bfc2bbafb3e169e8cd9ba49037dd00396b22" integrity sha512-LiMq/XDGcgodTYOMppikEtJelWsKQERbLQsYm0IOOnzhwE9xYZC7x8txNnFC9wJNOkPferQI4vD4ZkC0mDyrOA== @@ -9741,7 +9759,7 @@ ref-array-di@^1.2.2: node-addon-api "^3.0.0" node-gyp-build "^4.2.1" -ref-struct-di@^1.1.0, ref-struct-di@^1.1.1: +ref-struct-di@1.1.1, ref-struct-di@^1.1.0, ref-struct-di@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ref-struct-di/-/ref-struct-di-1.1.1.tgz#5827b1d3b32372058f177547093db1fe1602dc10" integrity sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g== From 30857b92702f422db808f372b2416998cd0365ed Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Sat, 11 Feb 2023 22:50:10 +0100 Subject: [PATCH 033/139] fix(transport)!: added docs moved connection to connectionId (#1222) Signed-off-by: blu3beri --- packages/core/src/agent/MessageReceiver.ts | 2 +- packages/core/src/agent/TransportService.ts | 27 ++++++++++++++++--- .../agent/__tests__/TransportService.test.ts | 2 +- packages/core/src/agent/__tests__/stubs.ts | 3 +-- packages/core/src/foobarbaz | 0 5 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 packages/core/src/foobarbaz diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index befabec616..55d1368fc0 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -147,7 +147,7 @@ export class MessageReceiver { // We allow unready connections to be attached to the session as we want to be able to // use return routing to make connections. This is especially useful for creating connections // with mediators when you don't have a public endpoint yet. - session.connection = connection ?? undefined + session.connectionId = connection?.id messageContext.sessionId = session.id this.transportService.saveSession(session) } else if (session) { diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index 0eda25500c..9455a045bb 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -1,6 +1,5 @@ import type { AgentMessage } from './AgentMessage' import type { EnvelopeKeys } from './EnvelopeService' -import type { ConnectionRecord } from '../modules/connections/repository' import type { DidDocument } from '../modules/dids' import type { EncryptedMessage } from '../types' @@ -16,7 +15,7 @@ export class TransportService { } public findSessionByConnectionId(connectionId: string) { - return Object.values(this.transportSessionTable).find((session) => session?.connection?.id === connectionId) + return Object.values(this.transportSessionTable).find((session) => session?.connectionId === connectionId) } public hasInboundEndpoint(didDocument: DidDocument): boolean { @@ -36,12 +35,34 @@ interface TransportSessionTable { [sessionId: string]: TransportSession | undefined } +// In the framework Transport sessions are used for communication. A session is +// associated with a connection and it can be reused when we want to respond to +// a message. If the message, for example, does not contain any way to reply to +// this message, the session should be closed. When a new sequence of messages +// starts it can be used again. A session will be deleted when a WebSocket +// closes, for the WsTransportSession that is. export interface TransportSession { + // unique identifier for a transport session. This can a uuid, or anything else, as long + // as it uniquely identifies a transport. id: string + + // The type is something that explicitly defines the transport type. For WebSocket it would + // be "WebSocket" and for HTTP it would be "HTTP". type: string + + // The enveloping keys that can be used during the transport. This is used so the framework + // does not have to look up the associated keys for sending a message. keys?: EnvelopeKeys + + // A received message that will be used to check whether it has any return routing. inboundMessage?: AgentMessage - connection?: ConnectionRecord + + // A stored connection id used to find this session via the `TransportService` for a specific connection + connectionId?: string + + // Send an encrypted message send(encryptedMessage: EncryptedMessage): Promise + + // Close the session to prevent dangling sessions. close(): Promise } diff --git a/packages/core/src/agent/__tests__/TransportService.test.ts b/packages/core/src/agent/__tests__/TransportService.test.ts index c16d00478b..f46707fa3d 100644 --- a/packages/core/src/agent/__tests__/TransportService.test.ts +++ b/packages/core/src/agent/__tests__/TransportService.test.ts @@ -15,7 +15,7 @@ describe('TransportService', () => { test(`remove session saved for a given connection`, () => { const connection = getMockConnection({ id: 'test-123', role: DidExchangeRole.Responder }) const session = new DummyTransportSession('dummy-session-123') - session.connection = connection + session.connectionId = connection.id transportService.saveSession(session) expect(transportService.findSessionByConnectionId(connection.id)).toEqual(session) diff --git a/packages/core/src/agent/__tests__/stubs.ts b/packages/core/src/agent/__tests__/stubs.ts index 49fcaae660..afd7ec4aaa 100644 --- a/packages/core/src/agent/__tests__/stubs.ts +++ b/packages/core/src/agent/__tests__/stubs.ts @@ -1,4 +1,3 @@ -import type { ConnectionRecord } from '../../modules/connections' import type { AgentMessage } from '../AgentMessage' import type { EnvelopeKeys } from '../EnvelopeService' import type { TransportSession } from '../TransportService' @@ -8,7 +7,7 @@ export class DummyTransportSession implements TransportSession { public readonly type = 'http' public keys?: EnvelopeKeys public inboundMessage?: AgentMessage - public connection?: ConnectionRecord + public connectionId?: string public constructor(id: string) { this.id = id diff --git a/packages/core/src/foobarbaz b/packages/core/src/foobarbaz new file mode 100644 index 0000000000..e69de29bb2 From d61f6edeafffa771635493acb552e5c89d88626e Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Mon, 13 Feb 2023 09:23:17 +0100 Subject: [PATCH 034/139] chore(core): remove useless file (#1288) --- packages/core/src/foobarbaz | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 packages/core/src/foobarbaz diff --git a/packages/core/src/foobarbaz b/packages/core/src/foobarbaz deleted file mode 100644 index e69de29bb2..0000000000 From 51030d43a7e3cca3da29c5add38e35f731576927 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Mon, 13 Feb 2023 11:43:02 +0100 Subject: [PATCH 035/139] feat(indy-vdr): module registration (#1285) Signed-off-by: Karim Stekelenburg --- packages/indy-vdr/src/IndyVdrModule.ts | 24 +++++++++++++ packages/indy-vdr/src/IndyVdrModuleConfig.ts | 36 +++++++++++++++++++ .../src/__tests__/IndyVdrModule.test.ts | 36 +++++++++++++++++++ .../src/__tests__/IndyVdrModuleConfig.test.ts | 15 ++++++++ .../src/dids/IndyVdrSovDidResolver.ts | 2 +- .../__tests__/IndyVdrSovDidResolver.test.ts | 10 ++---- .../indy-vdr/src/pool/IndyVdrPoolService.ts | 10 +++--- packages/indy-vdr/tests/helpers.ts | 13 +++++++ .../indy-vdr-anoncreds-registry.e2e.test.ts | 26 ++++++-------- .../tests/indy-vdr-did-resolver.e2e.test.ts | 14 ++------ .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 6 ++-- 11 files changed, 149 insertions(+), 43 deletions(-) create mode 100644 packages/indy-vdr/src/IndyVdrModule.ts create mode 100644 packages/indy-vdr/src/IndyVdrModuleConfig.ts create mode 100644 packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts create mode 100644 packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts new file mode 100644 index 0000000000..6150435c51 --- /dev/null +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -0,0 +1,24 @@ +import type { IndyVdrModuleConfigOptions } from './IndyVdrModuleConfig' +import type { DependencyManager, Module } from '@aries-framework/core' + +import { IndyVdrModuleConfig } from './IndyVdrModuleConfig' +import { IndyVdrPoolService } from './pool/IndyVdrPoolService' + +/** + * @public + * */ +export class IndyVdrModule implements Module { + public readonly config: IndyVdrModuleConfig + + public constructor(config: IndyVdrModuleConfigOptions) { + this.config = new IndyVdrModuleConfig(config) + } + + public register(dependencyManager: DependencyManager) { + // Config + dependencyManager.registerInstance(IndyVdrModuleConfig, this.config) + + // Services + dependencyManager.registerSingleton(IndyVdrPoolService) + } +} diff --git a/packages/indy-vdr/src/IndyVdrModuleConfig.ts b/packages/indy-vdr/src/IndyVdrModuleConfig.ts new file mode 100644 index 0000000000..6b6b2eaddb --- /dev/null +++ b/packages/indy-vdr/src/IndyVdrModuleConfig.ts @@ -0,0 +1,36 @@ +import type { IndyVdrPoolConfig } from './pool' + +export interface IndyVdrModuleConfigOptions { + /** + * Array of indy networks to connect to. + * + * [@default](https://github.com/default) [] + * + * @example + * ``` + * { + * isProduction: false, + * genesisTransactions: 'xxx', + * indyNamespace: 'localhost:test', + * transactionAuthorAgreement: { + * version: '1', + * acceptanceMechanism: 'accept' + * } + * } + * ``` + */ + networks: [IndyVdrPoolConfig, ...IndyVdrPoolConfig[]] +} + +export class IndyVdrModuleConfig { + private options: IndyVdrModuleConfigOptions + + public constructor(options: IndyVdrModuleConfigOptions) { + this.options = options + } + + /** See {@link IndyVdrModuleConfigOptions.networks} */ + public get networks() { + return this.options.networks + } +} diff --git a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts new file mode 100644 index 0000000000..4d904fa133 --- /dev/null +++ b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts @@ -0,0 +1,36 @@ +import type { DependencyManager } from '@aries-framework/core' + +import { IndyVdrModule } from '../IndyVdrModule' +import { IndyVdrModuleConfig } from '../IndyVdrModuleConfig' +import { IndyVdrPoolService } from '../pool' + +const dependencyManager = { + registerInstance: jest.fn(), + registerSingleton: jest.fn(), +} as unknown as DependencyManager + +describe('IndyVdrModule', () => { + test('registers dependencies on the dependency manager', () => { + const indyVdrModule = new IndyVdrModule({ + networks: [ + { + isProduction: false, + genesisTransactions: 'xxx', + indyNamespace: 'localhost:test', + transactionAuthorAgreement: { + version: '1', + acceptanceMechanism: 'accept', + }, + }, + ], + }) + + indyVdrModule.register(dependencyManager) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyVdrPoolService) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(IndyVdrModuleConfig, indyVdrModule.config) + }) +}) diff --git a/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts b/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts new file mode 100644 index 0000000000..db86ddb519 --- /dev/null +++ b/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts @@ -0,0 +1,15 @@ +import type { IndyVdrPoolConfig } from '../pool' + +import { IndyVdrModuleConfig } from '../IndyVdrModuleConfig' + +describe('IndyVdrModuleConfig', () => { + test('sets values', () => { + const networkConfig = {} as IndyVdrPoolConfig + + const config = new IndyVdrModuleConfig({ + networks: [networkConfig], + }) + + expect(config.networks).toEqual([networkConfig]) + }) +}) diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts index 92fbefa20f..b563a3122e 100644 --- a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -4,7 +4,7 @@ import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from ' import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrError, IndyVdrNotFoundError } from '../error' -import { IndyVdrPoolService } from '../pool' +import { IndyVdrPoolService } from '../pool/IndyVdrPoolService' import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts index 269aaa1a46..c8001ccd19 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts @@ -2,27 +2,23 @@ import { JsonTransformer } from '@aries-framework/core' import { parseDid } from '../../../../core/src/modules/dids/domain/parse' import { getAgentConfig, getAgentContext, mockProperty } from '../../../../core/tests/helpers' -import { IndyVdrPool, IndyVdrPoolService } from '../../pool' +import { IndyVdrPool } from '../../pool/IndyVdrPool' +import { IndyVdrPoolService } from '../../pool/IndyVdrPoolService' import { IndyVdrSovDidResolver } from '../IndyVdrSovDidResolver' import didSovR1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' import didSovWJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' -jest.mock('../../pool/IndyVdrPoolService') -const IndyVdrPoolServiceMock = IndyVdrPoolService as jest.Mock -const poolServiceMock = new IndyVdrPoolServiceMock() - jest.mock('../../pool/IndyVdrPool') const IndyVdrPoolMock = IndyVdrPool as jest.Mock const poolMock = new IndyVdrPoolMock() mockProperty(poolMock, 'indyNamespace', 'local') -jest.spyOn(poolServiceMock, 'getPoolForDid').mockResolvedValue(poolMock) const agentConfig = getAgentConfig('IndyVdrSovDidResolver') const agentContext = getAgentContext({ agentConfig, - registerInstances: [[IndyVdrPoolService, poolServiceMock]], + registerInstances: [[IndyVdrPoolService, { getPoolForDid: jest.fn().mockReturnValue(poolMock) }]], }) const resolver = new IndyVdrSovDidResolver() diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index b6a1a0f989..3fa6177465 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -1,10 +1,10 @@ -import type { IndyVdrPoolConfig } from './IndyVdrPool' import type { AgentContext } from '@aries-framework/core' import type { GetNymResponse } from '@hyperledger/indy-vdr-shared' import { Logger, InjectionSymbols, injectable, inject, CacheModuleConfig } from '@aries-framework/core' import { GetNymRequest } from '@hyperledger/indy-vdr-shared' +import { IndyVdrModuleConfig } from '../IndyVdrModuleConfig' import { IndyVdrError, IndyVdrNotFoundError, IndyVdrNotConfiguredError } from '../error' import { isSelfCertifiedDid, DID_INDY_REGEX } from '../utils/did' import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' @@ -22,13 +22,13 @@ export interface CachedDidResponse { export class IndyVdrPoolService { public pools: IndyVdrPool[] = [] private logger: Logger + private indyVdrModuleConfig: IndyVdrModuleConfig - public constructor(@inject(InjectionSymbols.Logger) logger: Logger) { + public constructor(@inject(InjectionSymbols.Logger) logger: Logger, indyVdrModuleConfig: IndyVdrModuleConfig) { this.logger = logger - } + this.indyVdrModuleConfig = indyVdrModuleConfig - public setPools(poolConfigs: IndyVdrPoolConfig[]) { - this.pools = poolConfigs.map((poolConfig) => new IndyVdrPool(poolConfig, this.logger)) + this.pools = this.indyVdrModuleConfig.networks.map((poolConfig) => new IndyVdrPool(poolConfig, this.logger)) } /** diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index 2ac21a7711..ecaf154ee9 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -4,8 +4,21 @@ import type { AgentContext, Key } from '@aries-framework/core' import { KeyType } from '@aries-framework/core' import { AttribRequest, NymRequest } from '@hyperledger/indy-vdr-shared' +import { genesisTransactions } from '../../core/tests/helpers' +import { IndyVdrModuleConfig } from '../src/IndyVdrModuleConfig' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' +export const indyVdrModuleConfig = new IndyVdrModuleConfig({ + networks: [ + { + genesisTransactions, + indyNamespace: 'pool:localtest', + isProduction: false, + transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, + }, + ], +}) + export async function createDidOnLedger( indyVdrPoolService: IndyVdrPoolService, agentContext: AgentContext, diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index 9d214ea43d..d45597ce8f 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -1,21 +1,15 @@ import { Agent } from '@aries-framework/core' -import { agentDependencies, genesisTransactions, getAgentConfig } from '../../core/tests/helpers' +import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' import { IndyVdrAnonCredsRegistry } from '../src/anoncreds/IndyVdrAnonCredsRegistry' import { IndyVdrPoolService } from '../src/pool' +import { indyVdrModuleConfig } from './helpers' + const agentConfig = getAgentConfig('IndyVdrAnonCredsRegistry') // TODO: update to module once available -const indyVdrPoolService = new IndyVdrPoolService(agentConfig.logger) -indyVdrPoolService.setPools([ - { - genesisTransactions, - indyNamespace: 'local:test', - isProduction: false, - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, -]) +const indyVdrPoolService = new IndyVdrPoolService(agentConfig.logger, indyVdrModuleConfig) const indyVdrAnonCredsRegistry = new IndyVdrAnonCredsRegistry() @@ -47,7 +41,7 @@ describe('IndyVdrAnonCredsRegistry', () => { const schemaResult = await indyVdrAnonCredsRegistry.registerSchema(agent.context, { options: { - didIndyNamespace: 'local:test', + didIndyNamespace: 'pool:localtest', }, schema: { attrNames: ['age'], @@ -71,7 +65,7 @@ describe('IndyVdrAnonCredsRegistry', () => { registrationMetadata: {}, schemaMetadata: { indyLedgerSeqNo: expect.any(Number), - didIndyNamespace: 'local:test', + didIndyNamespace: 'pool:localtest', }, }) @@ -89,7 +83,7 @@ describe('IndyVdrAnonCredsRegistry', () => { schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, resolutionMetadata: {}, schemaMetadata: { - didIndyNamespace: 'local:test', + didIndyNamespace: 'pool:localtest', indyLedgerSeqNo: expect.any(Number), }, }) @@ -116,13 +110,13 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }, options: { - didIndyNamespace: 'local:test', + didIndyNamespace: 'pool:localtest', }, }) expect(credentialDefinitionResult).toMatchObject({ credentialDefinitionMetadata: { - didIndyNamespace: 'local:test', + didIndyNamespace: 'pool:localtest', }, credentialDefinitionState: { credentialDefinition: { @@ -182,7 +176,7 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }, credentialDefinitionMetadata: { - didIndyNamespace: 'local:test', + didIndyNamespace: 'pool:localtest', }, resolutionMetadata: {}, }) diff --git a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts index 72a09afc83..e9f3aa4eab 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts @@ -10,13 +10,13 @@ import { } from '@aries-framework/core' import { parseDid } from '../../core/src/modules/dids/domain/parse' -import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' import { IndyVdrSovDidResolver } from '../src/dids' import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' -import { createDidOnLedger } from './helpers' +import { createDidOnLedger, indyVdrModuleConfig } from './helpers' const logger = testLogger const wallet = new IndyWallet(agentDependencies, logger, new SigningProviderRegistry([])) @@ -25,26 +25,18 @@ const agentConfig = getAgentConfig('IndyVdrResolver E2E', { logger }) const cache = new InMemoryLruCache({ limit: 200 }) const indyVdrSovDidResolver = new IndyVdrSovDidResolver() -const config = { - isProduction: false, - genesisTransactions, - indyNamespace: `pool:localtest`, - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, -} as const - let signerKey: Key const agentContext = getAgentContext({ wallet, agentConfig, registerInstances: [ - [IndyVdrPoolService, new IndyVdrPoolService(logger)], + [IndyVdrPoolService, new IndyVdrPoolService(logger, indyVdrModuleConfig)], [CacheModuleConfig, new CacheModuleConfig({ cache })], ], }) const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) -indyVdrPoolService.setPools([config]) describe('IndyVdrSov', () => { beforeAll(async () => { diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index 95a3882ff1..fea5b8fe0f 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -9,7 +9,9 @@ import { IndyVdrPool } from '../src/pool' import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' -const indyVdrPoolService = new IndyVdrPoolService(testLogger) +import { indyVdrModuleConfig } from './helpers' + +const indyVdrPoolService = new IndyVdrPoolService(testLogger, indyVdrModuleConfig) const wallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) const agentConfig = getAgentConfig('IndyVdrPoolService') const agentContext = getAgentContext({ wallet, agentConfig }) @@ -23,8 +25,6 @@ const config = { let signerKey: Key -indyVdrPoolService.setPools([config]) - describe('IndyVdrPoolService', () => { beforeAll(async () => { await indyVdrPoolService.connectToPools() From fb8d58b8222b130fdf660075450e762a943c4483 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 13 Feb 2023 12:57:35 +0100 Subject: [PATCH 036/139] refactor(proofs)!: generalize proofs api and improve consistency with credentials module (#1279) Signed-off-by: Timo Glastra --- demo/src/Alice.ts | 5 +- demo/src/Faber.ts | 18 +- package.json | 2 - .../LegacyIndyCredentialFormatService.ts | 114 +- .../tests/bbs-signatures.e2e.test.ts | 3 - ...proof.credentials.propose-offerBbs.test.ts | 2 +- packages/core/src/agent/AgentModules.ts | 4 +- packages/core/src/agent/BaseAgent.ts | 7 +- .../core/src/agent/__tests__/Agent.test.ts | 9 +- .../src/decorators/attachment/Attachment.ts | 4 +- .../connections/services/ConnectionService.ts | 4 +- .../src/modules/credentials/CredentialsApi.ts | 181 +-- .../credentials/CredentialsApiOptions.ts | 38 +- .../modules/credentials/CredentialsModule.ts | 4 +- .../formats/CredentialFormatService.ts | 84 +- .../formats/CredentialFormatServiceOptions.ts | 45 +- .../IndyCredentialFormatService.test.ts | 12 +- .../JsonLdCredentialFormatService.test.ts | 14 +- .../indy/IndyCredentialFormatService.ts | 119 +- .../formats/indy/models/IndyCredentialInfo.ts | 11 +- .../jsonld/JsonLdCredentialFormatService.ts | 98 +- .../models/CredentialFormatSpec.ts | 6 +- .../credentials/models/CredentialState.ts | 1 + .../protocol/BaseCredentialProtocol.ts | 77 +- .../protocol/CredentialProtocol.ts | 64 +- .../CredentialProtocolOptions.ts | 55 +- .../protocol/v1/V1CredentialProtocol.ts | 124 +- .../V1CredentialProtocolCred.test.ts | 110 +- .../V1CredentialProtocolProposeOffer.test.ts | 40 +- .../v1/messages/V1IssueCredentialMessage.ts | 2 +- .../v1/messages/V1OfferCredentialMessage.ts | 2 +- .../v1/messages/V1ProposeCredentialMessage.ts | 8 - .../v2/CredentialFormatCoordinator.ts | 2 +- .../protocol/v2/V2CredentialProtocol.ts | 123 +- .../V2CredentialProtocolCred.test.ts | 101 +- .../V2CredentialProtocolOffer.test.ts | 21 +- .../v2/handlers/V2OfferCredentialHandler.ts | 7 +- .../v2/messages/V2IssueCredentialMessage.ts | 6 +- .../v2/messages/V2OfferCredentialMessage.ts | 2 +- .../v2/messages/V2ProposeCredentialMessage.ts | 23 +- .../v2/messages/V2RequestCredentialMessage.ts | 2 +- .../credentials/util/composeAutoAccept.ts | 1 - .../proofs/ProofResponseCoordinator.ts | 93 -- .../core/src/modules/proofs/ProofService.ts | 261 ---- packages/core/src/modules/proofs/ProofsApi.ts | 536 ++++---- .../src/modules/proofs/ProofsApiOptions.ts | 204 ++- .../core/src/modules/proofs/ProofsModule.ts | 70 +- .../src/modules/proofs/ProofsModuleConfig.ts | 30 +- .../proofs/__tests__/ProofsModule.test.ts | 51 +- .../__tests__/ProofsModuleConfig.test.ts | 26 + .../src/modules/proofs/__tests__/fixtures.ts | 30 + .../src/modules/proofs/formats/ProofFormat.ts | 47 +- .../proofs/formats/ProofFormatConstants.ts | 4 - .../proofs/formats/ProofFormatService.ts | 125 +- .../formats/ProofFormatServiceOptions.ts | 110 +- .../errors/InvalidEncodedValueError.ts | 3 + .../errors/MissingIndyProofMessageError.ts | 3 + .../core/src/modules/proofs/formats/index.ts | 9 +- .../proofs/formats/indy/IndyProofFormat.ts | 79 +- .../formats/indy/IndyProofFormatService.ts | 763 +++++------ .../indy/IndyProofFormatsServiceOptions.ts | 38 - .../{ => formats/indy}/__tests__/groupKeys.ts | 8 +- .../formats/indy/__tests__/util.test.ts | 541 ++++++++ .../errors/MissingIndyProofMessageError.ts | 3 - .../proofs/formats/indy/errors/index.ts | 1 - .../src/modules/proofs/formats/indy/index.ts | 4 +- .../indy}/models/PartialProof.ts | 0 .../indy}/models/ProofAttribute.ts | 0 .../formats/indy/models/ProofAttributeInfo.ts | 8 +- .../indy}/models/ProofIdentifier.ts | 0 .../formats/indy/models/ProofPredicateInfo.ts | 17 +- .../formats/indy/models/ProofRequest.ts | 33 +- .../formats/indy/models/RequestedAttribute.ts | 14 +- .../indy/models/RequestedCredentials.ts | 26 +- .../formats/indy/models/RequestedPredicate.ts | 13 +- .../indy}/models/RequestedProof.ts | 0 .../models}/__tests__/ProofRequest.test.ts | 8 +- .../src/modules/proofs/formats/indy/util.ts | 266 ++++ .../formats/models/ProofAttachmentFormat.ts | 7 - .../models/ProofFormatServiceOptions.ts | 64 - .../modules/proofs/formats/models/index.ts | 2 - packages/core/src/modules/proofs/index.ts | 7 +- .../proofs/messages/PresentationAckMessage.ts | 10 - .../core/src/modules/proofs/messages/index.ts | 1 - .../models/GetRequestedCredentialsConfig.ts | 19 - .../modules/proofs/models/ModuleOptions.ts | 22 - .../proofs/models/ProofServiceOptions.ts | 85 -- .../modules/proofs/models/SharedOptions.ts | 62 - .../{ => models}/__tests__/ProofState.test.ts | 2 +- .../core/src/modules/proofs/models/index.ts | 1 - .../proofs/protocol/BaseProofProtocol.ts | 286 +++++ .../modules/proofs/protocol/ProofProtocol.ts | 117 ++ .../proofs/protocol/ProofProtocolOptions.ts | 165 +++ .../core/src/modules/proofs/protocol/index.ts | 4 + .../proofs/protocol/v1/V1ProofProtocol.ts | 1111 +++++++++++++++++ .../proofs/protocol/v1/V1ProofService.ts | 1111 ----------------- .../v1/__tests__/V1ProofProtocol.test.ts} | 127 +- .../__tests__/indy-proof-negotiation.test.ts | 61 +- ...ts => indy-proof-presentation.test.e2e.ts} | 20 +- ...est.ts => indy-proof-proposal.test.e2e.ts} | 5 +- .../v1-connectionless-proofs.e2e.test.ts} | 57 +- ...t.ts => v1-indy-proof-request.e2e.test.ts} | 50 +- .../v1/__tests__/v1-indy-proofs.e2e.test.ts} | 158 +-- .../v1-proofs-auto-accept.e2e.test.ts} | 40 +- .../v1/handlers/V1PresentationAckHandler.ts | 10 +- .../v1/handlers/V1PresentationHandler.ts | 84 +- .../V1PresentationProblemReportHandler.ts | 10 +- .../handlers/V1ProposePresentationHandler.ts | 93 +- .../handlers/V1RequestPresentationHandler.ts | 120 +- .../src/modules/proofs/protocol/v1/index.ts | 2 +- .../v1/messages/V1PresentationMessage.ts | 34 +- .../messages/V1ProposePresentationMessage.ts | 20 +- .../messages/V1RequestPresentationMessage.ts | 58 +- .../v1/models/V1PresentationPreview.ts | 44 +- .../proofs/protocol/v1/models/index.ts | 4 - .../protocol/v2/ProofFormatCoordinator.ts | 519 ++++++++ .../proofs/protocol/v2/V2ProofProtocol.ts | 1069 ++++++++++++++++ .../proofs/protocol/v2/V2ProofService.ts | 953 -------------- .../v2/__tests__/V2ProofProtocol.test.ts} | 145 +-- ...v2-indy-connectionless-proofs.e2e.test.ts} | 62 +- ...t.ts => v2-indy-proof-negotiation.test.ts} | 106 +- ...=> v2-indy-proof-presentation.e2e.test.ts} | 37 +- ....ts => v2-indy-proof-proposal.e2e.test.ts} | 18 +- ...t.ts => v2-indy-proof-request.e2e.test.ts} | 24 +- .../v2-indy-proofs-auto-accept.2e.test.ts} | 56 +- .../v2/__tests__/v2-indy-proofs.e2e.test.ts} | 230 ++-- .../v2/handlers/V2PresentationAckHandler.ts | 10 +- .../v2/handlers/V2PresentationHandler.ts | 61 +- .../V2PresentationProblemReportHandler.ts | 6 +- .../handlers/V2ProposePresentationHandler.ts | 93 +- .../handlers/V2RequestPresentationHandler.ts | 94 +- .../src/modules/proofs/protocol/v2/index.ts | 2 +- .../v2/messages/V2PresentationAckMessage.ts | 6 - .../v2/messages/V2PresentationMessage.ts | 43 +- .../V2PresentationProblemReportMessage.ts | 12 - .../messages/V2ProposalPresentationMessage.ts | 100 -- .../messages/V2ProposePresentationMessage.ts | 62 + .../messages/V2RequestPresentationMessage.ts | 70 +- .../proofs/protocol/v2/messages/index.ts | 2 +- .../proofs/repository/ProofExchangeRecord.ts | 8 + .../modules/proofs/utils/composeAutoAccept.ts | 11 + .../src/modules/vc/W3cCredentialService.ts | 11 +- .../vc/__tests__/W3cCredentialService.test.ts | 3 - .../vc/__tests__/contexts/citizenship_v2.ts | 45 + .../modules/vc/__tests__/contexts/index.ts | 2 + .../vc/__tests__/contexts/submission.ts | 15 + .../vc/__tests__/contexts/vaccination_v2.ts | 88 ++ .../modules/vc/__tests__/documentLoader.ts | 14 +- .../core/src/modules/vc/__tests__/fixtures.ts | 30 + packages/core/src/modules/vc/constants.ts | 1 + .../vc/models/W3cCredentialServiceOptions.ts | 2 - .../src/storage/migration/UpdateAssistant.ts | 2 +- .../utils/__tests__/indyProofRequest.test.ts | 81 -- packages/core/src/utils/index.ts | 1 + packages/core/src/utils/indyProofRequest.ts | 2 +- packages/core/src/utils/version.ts | 4 +- packages/core/tests/generic-records.test.ts | 4 +- packages/core/tests/helpers.ts | 145 +-- packages/core/tests/logger.ts | 55 +- packages/core/tests/oob.test.ts | 4 +- .../core/tests/proofs-sub-protocol.test.ts | 36 +- packages/openid4vc-client/tests/fixtures.ts | 2 +- .../tests/openid4vc-client.e2e.test.ts | 12 +- tests/e2e-test.ts | 25 +- yarn.lock | 5 - 165 files changed, 7121 insertions(+), 6319 deletions(-) rename packages/core/src/modules/credentials/{ => protocol}/CredentialProtocolOptions.ts (68%) delete mode 100644 packages/core/src/modules/proofs/ProofResponseCoordinator.ts delete mode 100644 packages/core/src/modules/proofs/ProofService.ts create mode 100644 packages/core/src/modules/proofs/__tests__/ProofsModuleConfig.test.ts delete mode 100644 packages/core/src/modules/proofs/formats/ProofFormatConstants.ts create mode 100644 packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts create mode 100644 packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts rename packages/core/src/modules/proofs/{ => formats/indy}/__tests__/groupKeys.ts (79%) create mode 100644 packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/errors/MissingIndyProofMessageError.ts rename packages/core/src/modules/proofs/{protocol/v1 => formats/indy}/models/PartialProof.ts (100%) rename packages/core/src/modules/proofs/{protocol/v1 => formats/indy}/models/ProofAttribute.ts (100%) rename packages/core/src/modules/proofs/{protocol/v1 => formats/indy}/models/ProofIdentifier.ts (100%) rename packages/core/src/modules/proofs/{protocol/v1 => formats/indy}/models/RequestedProof.ts (100%) rename packages/core/src/modules/proofs/{ => formats/indy/models}/__tests__/ProofRequest.test.ts (87%) create mode 100644 packages/core/src/modules/proofs/formats/indy/util.ts delete mode 100644 packages/core/src/modules/proofs/formats/models/ProofAttachmentFormat.ts delete mode 100644 packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts delete mode 100644 packages/core/src/modules/proofs/formats/models/index.ts delete mode 100644 packages/core/src/modules/proofs/messages/PresentationAckMessage.ts delete mode 100644 packages/core/src/modules/proofs/messages/index.ts delete mode 100644 packages/core/src/modules/proofs/models/GetRequestedCredentialsConfig.ts delete mode 100644 packages/core/src/modules/proofs/models/ModuleOptions.ts delete mode 100644 packages/core/src/modules/proofs/models/ProofServiceOptions.ts delete mode 100644 packages/core/src/modules/proofs/models/SharedOptions.ts rename packages/core/src/modules/proofs/{ => models}/__tests__/ProofState.test.ts (92%) create mode 100644 packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts create mode 100644 packages/core/src/modules/proofs/protocol/ProofProtocol.ts create mode 100644 packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts create mode 100644 packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts delete mode 100644 packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts rename packages/core/src/modules/proofs/{__tests__/V1ProofService.test.ts => protocol/v1/__tests__/V1ProofProtocol.test.ts} (63%) rename packages/core/src/modules/proofs/protocol/v1/__tests__/{indy-proof-presentation.test.ts => indy-proof-presentation.test.e2e.ts} (93%) rename packages/core/src/modules/proofs/protocol/v1/__tests__/{indy-proof-proposal.test.ts => indy-proof-proposal.test.e2e.ts} (95%) rename packages/core/{tests/v1-connectionless-proofs.test.ts => src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts} (89%) rename packages/core/src/modules/proofs/protocol/v1/__tests__/{indy-proof-request.test.ts => v1-indy-proof-request.e2e.test.ts} (70%) rename packages/core/{tests/v1-indy-proofs.test.ts => src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts} (81%) rename packages/core/{tests/v1-proofs-auto-accept.test.ts => src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts} (82%) create mode 100644 packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts delete mode 100644 packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts rename packages/core/src/modules/proofs/{__tests__/V2ProofService.test.ts => protocol/v2/__tests__/V2ProofProtocol.test.ts} (61%) rename packages/core/{tests/v2-connectionless-proofs.test.ts => src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts} (88%) rename packages/core/src/modules/proofs/protocol/v2/__tests__/{indy-proof-negotiation.test.ts => v2-indy-proof-negotiation.test.ts} (84%) rename packages/core/src/modules/proofs/protocol/v2/__tests__/{indy-proof-presentation.test.ts => v2-indy-proof-presentation.e2e.test.ts} (89%) rename packages/core/src/modules/proofs/protocol/v2/__tests__/{indy-proof-proposal.test.ts => v2-indy-proof-proposal.e2e.test.ts} (84%) rename packages/core/src/modules/proofs/protocol/v2/__tests__/{indy-proof-request.test.ts => v2-indy-proof-request.e2e.test.ts} (87%) rename packages/core/{tests/v2-proofs-auto-accept.test.ts => src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts} (79%) rename packages/core/{tests/v2-indy-proofs.test.ts => src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts} (77%) delete mode 100644 packages/core/src/modules/proofs/protocol/v2/messages/V2ProposalPresentationMessage.ts create mode 100644 packages/core/src/modules/proofs/protocol/v2/messages/V2ProposePresentationMessage.ts create mode 100644 packages/core/src/modules/proofs/utils/composeAutoAccept.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/citizenship_v2.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/submission.ts create mode 100644 packages/core/src/modules/vc/__tests__/contexts/vaccination_v2.ts delete mode 100644 packages/core/src/utils/__tests__/indyProofRequest.test.ts diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 252c04c632..aa705ca7a4 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -52,11 +52,8 @@ export class Alice extends BaseAgent { } public async acceptProofRequest(proofRecord: ProofExchangeRecord) { - const requestedCredentials = await this.agent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await this.agent.proofs.selectCredentialsForRequest({ proofRecordId: proofRecord.id, - config: { - filterByPresentationPreview: true, - }, }) await this.agent.proofs.acceptRequest({ diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 267349b785..a19906d0fa 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -2,13 +2,7 @@ import type { ConnectionRecord, ConnectionStateChangedEvent } from '@aries-frame import type { CredDef, Schema } from 'indy-sdk' import type BottomBar from 'inquirer/lib/ui/bottom-bar' -import { - AttributeFilter, - ProofAttributeInfo, - utils, - V1CredentialPreview, - ConnectionEventTypes, -} from '@aries-framework/core' +import { utils, V1CredentialPreview, ConnectionEventTypes } from '@aries-framework/core' import { ui } from 'inquirer' import { BaseAgent } from './BaseAgent' @@ -171,15 +165,16 @@ export class Faber extends BaseAgent { private async newProofAttribute() { await this.printProofFlow(greenText(`Creating new proof attribute for 'name' ...\n`)) const proofAttribute = { - name: new ProofAttributeInfo({ + name: { name: 'name', restrictions: [ - new AttributeFilter({ + { credentialDefinitionId: this.credentialDefinition?.id, - }), + }, ], - }), + }, } + return proofAttribute } @@ -195,7 +190,6 @@ export class Faber extends BaseAgent { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: proofAttribute, }, }, diff --git a/package.json b/package.json index 24f487b9a2..582f91a77e 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "test": "jest", "lint": "eslint --ignore-path .gitignore .", "validate": "yarn lint && yarn check-types && yarn check-format", - "prepare": "husky install", "run-mediator": "ts-node ./samples/mediator.ts", "next-version-bump": "ts-node ./scripts/get-next-bump.ts" }, @@ -48,7 +47,6 @@ "eslint-plugin-import": "^2.23.4", "eslint-plugin-prettier": "^3.4.0", "express": "^4.17.1", - "husky": "^7.0.1", "indy-sdk": "^1.16.0-dev-1636", "jest": "^27.0.4", "lerna": "^4.0.0", diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 6be55555a4..7b2dbf3b72 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -10,20 +10,20 @@ import type { AnonCredsCredentialMetadata } from '../utils/metadata' import type { CredentialFormatService, AgentContext, - FormatCreateProposalOptions, - FormatCreateProposalReturn, - FormatProcessOptions, - FormatAcceptProposalOptions, - FormatCreateOfferReturn, - FormatCreateOfferOptions, - FormatAcceptOfferOptions, + CredentialFormatCreateProposalOptions, + CredentialFormatCreateProposalReturn, + CredentialFormatProcessOptions, + CredentialFormatAcceptProposalOptions, + CredentialFormatCreateOfferReturn, + CredentialFormatCreateOfferOptions, + CredentialFormatAcceptOfferOptions, CredentialFormatCreateReturn, - FormatAcceptRequestOptions, - FormatProcessCredentialOptions, - FormatAutoRespondProposalOptions, - FormatAutoRespondOfferOptions, - FormatAutoRespondRequestOptions, - FormatAutoRespondCredentialOptions, + CredentialFormatAcceptRequestOptions, + CredentialFormatProcessCredentialOptions, + CredentialFormatAutoRespondProposalOptions, + CredentialFormatAutoRespondOfferOptions, + CredentialFormatAutoRespondRequestOptions, + CredentialFormatAutoRespondCredentialOptions, CredentialExchangeRecord, CredentialPreviewAttributeOptions, LinkedAttachment, @@ -80,8 +80,8 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic */ public async createProposal( agentContext: AgentContext, - { credentialFormats, credentialRecord }: FormatCreateProposalOptions - ): Promise { + { credentialFormats, credentialRecord }: CredentialFormatCreateProposalOptions + ): Promise { const format = new CredentialFormatSpec({ format: INDY_CRED_FILTER, }) @@ -106,7 +106,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic } const proposalJson = JsonTransformer.toJSON(proposal) - const attachment = this.getFormatData(proposalJson, format.attachId) + const attachment = this.getFormatData(proposalJson, format.attachmentId) const { previewAttributes } = this.getCredentialLinkedAttachments( indyFormat.attributes, @@ -122,7 +122,10 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic return { format, attachment, previewAttributes } } - public async processProposal(agentContext: AgentContext, { attachment }: FormatProcessOptions): Promise { + public async processProposal( + agentContext: AgentContext, + { attachment }: CredentialFormatProcessOptions + ): Promise { const proposalJson = attachment.getDataAsJson() // fromJSON also validates @@ -132,12 +135,12 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic public async acceptProposal( agentContext: AgentContext, { - attachId, + attachmentId, credentialFormats, credentialRecord, proposalAttachment, - }: FormatAcceptProposalOptions - ): Promise { + }: CredentialFormatAcceptProposalOptions + ): Promise { const indyFormat = credentialFormats?.indy const credentialProposal = JsonTransformer.fromJSON(proposalAttachment.getDataAsJson(), IndyCredPropose) @@ -158,7 +161,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const { format, attachment, previewAttributes } = await this.createIndyOffer(agentContext, { credentialRecord, - attachId, + attachmentId, attributes, credentialDefinitionId, linkedAttachments: indyFormat?.linkedAttachments, @@ -176,8 +179,12 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic */ public async createOffer( agentContext: AgentContext, - { credentialFormats, credentialRecord, attachId }: FormatCreateOfferOptions - ): Promise { + { + credentialFormats, + credentialRecord, + attachmentId, + }: CredentialFormatCreateOfferOptions + ): Promise { const indyFormat = credentialFormats.indy if (!indyFormat) { @@ -186,7 +193,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const { format, attachment, previewAttributes } = await this.createIndyOffer(agentContext, { credentialRecord, - attachId, + attachmentId, attributes: indyFormat.attributes, credentialDefinitionId: indyFormat.credentialDefinitionId, linkedAttachments: indyFormat.linkedAttachments, @@ -195,7 +202,10 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic return { format, attachment, previewAttributes } } - public async processOffer(agentContext: AgentContext, { attachment, credentialRecord }: FormatProcessOptions) { + public async processOffer( + agentContext: AgentContext, + { attachment, credentialRecord }: CredentialFormatProcessOptions + ) { agentContext.config.logger.debug(`Processing indy credential offer for credential record ${credentialRecord.id}`) const credOffer = attachment.getDataAsJson() @@ -211,10 +221,10 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic agentContext: AgentContext, { credentialRecord, - attachId, + attachmentId, offerAttachment, credentialFormats, - }: FormatAcceptOfferOptions + }: CredentialFormatAcceptOfferOptions ): Promise { const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) @@ -250,11 +260,11 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic }) const format = new CredentialFormatSpec({ - attachId, + attachmentId, format: INDY_CRED_REQUEST, }) - const attachment = this.getFormatData(credentialRequest, format.attachId) + const attachment = this.getFormatData(credentialRequest, format.attachmentId) return { format, attachment } } @@ -269,7 +279,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic * We don't have any models to validate an indy request object, for now this method does nothing */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async processRequest(agentContext: AgentContext, options: FormatProcessOptions): Promise { + public async processRequest(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise { // not needed for Indy } @@ -277,10 +287,10 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic agentContext: AgentContext, { credentialRecord, - attachId, + attachmentId, offerAttachment, requestAttachment, - }: FormatAcceptRequestOptions + }: CredentialFormatAcceptRequestOptions ): Promise { // Assert credential attributes const credentialAttributes = credentialRecord.credentialAttributes @@ -314,11 +324,11 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic } const format = new CredentialFormatSpec({ - attachId, + attachmentId, format: INDY_CRED, }) - const attachment = this.getFormatData(credential, format.attachId) + const attachment = this.getFormatData(credential, format.attachmentId) return { format, attachment } } @@ -329,7 +339,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic */ public async processCredential( agentContext: AgentContext, - { credentialRecord, attachment }: FormatProcessCredentialOptions + { credentialRecord, attachment }: CredentialFormatProcessCredentialOptions ): Promise { const credentialRequestMetadata = credentialRecord.metadata.get( AnonCredsCredentialRequestMetadataKey @@ -428,15 +438,15 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic } /** - * Gets the attachment object for a given attachId. We need to get out the correct attachId for + * Gets the attachment object for a given attachmentId. We need to get out the correct attachmentId for * indy and then find the corresponding attachment (if there is one) - * @param formats the formats object containing the attachId + * @param formats the formats object containing the attachmentId * @param messageAttachments the attachments containing the payload * @returns The Attachment if found or undefined * */ public getAttachment(formats: CredentialFormatSpec[], messageAttachments: Attachment[]): Attachment | undefined { - const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachId) + const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachmentId) const supportedAttachment = messageAttachments.find((attachment) => supportedAttachmentIds.includes(attachment.id)) return supportedAttachment @@ -449,9 +459,9 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic await anonCredsHolderService.deleteCredential(agentContext, credentialRecordId) } - public shouldAutoRespondToProposal( + public async shouldAutoRespondToProposal( agentContext: AgentContext, - { offerAttachment, proposalAttachment }: FormatAutoRespondProposalOptions + { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondProposalOptions ) { const credentialProposalJson = proposalAttachment.getDataAsJson() const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) @@ -464,9 +474,9 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id } - public shouldAutoRespondToOffer( + public async shouldAutoRespondToOffer( agentContext: AgentContext, - { offerAttachment, proposalAttachment }: FormatAutoRespondOfferOptions + { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondOfferOptions ) { const credentialProposalJson = proposalAttachment.getDataAsJson() const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) @@ -479,19 +489,19 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id } - public shouldAutoRespondToRequest( + public async shouldAutoRespondToRequest( agentContext: AgentContext, - { offerAttachment, requestAttachment }: FormatAutoRespondRequestOptions + { offerAttachment, requestAttachment }: CredentialFormatAutoRespondRequestOptions ) { const credentialOfferJson = offerAttachment.getDataAsJson() const credentialRequestJson = requestAttachment.getDataAsJson() - return credentialOfferJson.cred_def_id == credentialRequestJson.cred_def_id + return credentialOfferJson.cred_def_id === credentialRequestJson.cred_def_id } - public shouldAutoRespondToCredential( + public async shouldAutoRespondToCredential( agentContext: AgentContext, - { credentialRecord, requestAttachment, credentialAttachment }: FormatAutoRespondCredentialOptions + { credentialRecord, requestAttachment, credentialAttachment }: CredentialFormatAutoRespondCredentialOptions ) { const credentialJson = credentialAttachment.getDataAsJson() const credentialRequestJson = requestAttachment.getDataAsJson() @@ -511,24 +521,24 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic agentContext: AgentContext, { credentialRecord, - attachId, + attachmentId, credentialDefinitionId, attributes, linkedAttachments, }: { credentialDefinitionId: string credentialRecord: CredentialExchangeRecord - attachId?: string + attachmentId?: string attributes: CredentialPreviewAttributeOptions[] linkedAttachments?: LinkedAttachment[] } - ): Promise { + ): Promise { const anonCredsIssuerService = agentContext.dependencyManager.resolve(AnonCredsIssuerServiceSymbol) // if the proposal has an attachment Id use that, otherwise the generated id of the formats object const format = new CredentialFormatSpec({ - attachId: attachId, + attachmentId: attachmentId, format: INDY_CRED_ABSTRACT, }) @@ -548,7 +558,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic credentialDefinitionId: offer.cred_def_id, }) - const attachment = this.getFormatData(offer, format.attachId) + const attachment = this.getFormatData(offer, format.attachmentId) return { format, attachment, previewAttributes } } diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index 8e579225fe..936dfbe22e 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -252,10 +252,7 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { const result = await w3cCredentialService.verifyPresentation(agentContext, { presentation: vp, - proofType: 'Ed25519Signature2018', challenge: 'e950bfe5-d7ec-4303-ad61-6983fb976ac9', - verificationMethod: - 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', }) expect(result.verified).toBe(true) diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index 997c73c18b..a22408ce87 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -87,7 +87,7 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { testLogger.test('Alice sends (v2, Indy) credential proposal to Faber') - const credentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ connectionId: aliceConnection.id, protocolVersion: 'v2', credentialFormats: { diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 3f9512bdba..20f0d6cbb7 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -36,9 +36,11 @@ export type AgentModulesInput = Partial & ModulesMap * Defines the input type for the default agent modules. This is overwritten as we * want the input type to allow for generics to be passed in for the credentials module. */ -export type DefaultAgentModulesInput = Omit & { +export type DefaultAgentModulesInput = Omit & { // eslint-disable-next-line @typescript-eslint/no-explicit-any credentials: CredentialsModule + // eslint-disable-next-line @typescript-eslint/no-explicit-any + proofs: ProofsModule } /** diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 5d47157f59..704b2b5d98 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -3,6 +3,7 @@ import type { AgentApi, EmptyModuleMap, ModulesMap, WithoutDefaultModules, Custo import type { TransportSession } from './TransportService' import type { Logger } from '../logger' import type { CredentialsModule } from '../modules/credentials' +import type { ProofsModule } from '../modules/proofs' import type { DependencyManager } from '../plugins' import { AriesFrameworkError } from '../error' @@ -14,7 +15,7 @@ import { DiscoverFeaturesApi } from '../modules/discover-features' import { GenericRecordsApi } from '../modules/generic-records' import { LedgerApi } from '../modules/ledger' import { OutOfBandApi } from '../modules/oob' -import { ProofsApi } from '../modules/proofs/ProofsApi' +import { ProofsApi } from '../modules/proofs' import { MediatorApi, RecipientApi } from '../modules/routing' import { StorageUpdateService } from '../storage' import { UpdateAssistant } from '../storage/migration/UpdateAssistant' @@ -44,7 +45,7 @@ export abstract class BaseAgent - public readonly proofs: ProofsApi + public readonly proofs: CustomOrDefaultApi public readonly mediator: MediatorApi public readonly mediationRecipient: RecipientApi public readonly basicMessages: BasicMessagesApi @@ -88,7 +89,7 @@ export abstract class BaseAgent - this.proofs = this.dependencyManager.resolve(ProofsApi) + this.proofs = this.dependencyManager.resolve(ProofsApi) as CustomOrDefaultApi this.mediator = this.dependencyManager.resolve(MediatorApi) this.mediationRecipient = this.dependencyManager.resolve(RecipientApi) this.basicMessages = this.dependencyManager.resolve(BasicMessagesApi) diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index 2eca3350be..6f27e6125a 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -16,8 +16,6 @@ import { IndyLedgerService } from '../../modules/ledger' import { LedgerApi } from '../../modules/ledger/LedgerApi' import { ProofRepository } from '../../modules/proofs' import { ProofsApi } from '../../modules/proofs/ProofsApi' -import { V1ProofService } from '../../modules/proofs/protocol/v1' -import { V2ProofService } from '../../modules/proofs/protocol/v2' import { MediationRecipientService, MediationRepository, @@ -161,8 +159,6 @@ describe('Agent', () => { expect(container.resolve(ConnectionRepository)).toBeInstanceOf(ConnectionRepository) expect(container.resolve(TrustPingService)).toBeInstanceOf(TrustPingService) - expect(container.resolve(V1ProofService)).toBeInstanceOf(V1ProofService) - expect(container.resolve(V2ProofService)).toBeInstanceOf(V2ProofService) expect(container.resolve(ProofsApi)).toBeInstanceOf(ProofsApi) expect(container.resolve(ProofRepository)).toBeInstanceOf(ProofRepository) @@ -204,8 +200,6 @@ describe('Agent', () => { expect(container.resolve(ConnectionRepository)).toBe(container.resolve(ConnectionRepository)) expect(container.resolve(TrustPingService)).toBe(container.resolve(TrustPingService)) - expect(container.resolve(V1ProofService)).toBe(container.resolve(V1ProofService)) - expect(container.resolve(V2ProofService)).toBe(container.resolve(V2ProofService)) expect(container.resolve(ProofsApi)).toBe(container.resolve(ProofsApi)) expect(container.resolve(ProofRepository)).toBe(container.resolve(ProofRepository)) @@ -263,10 +257,11 @@ describe('Agent', () => { 'https://didcomm.org/messagepickup/2.0', 'https://didcomm.org/out-of-band/1.1', 'https://didcomm.org/present-proof/1.0', + 'https://didcomm.org/present-proof/2.0', 'https://didcomm.org/revocation_notification/1.0', 'https://didcomm.org/revocation_notification/2.0', ]) ) - expect(protocols.length).toEqual(14) + expect(protocols.length).toEqual(15) }) }) diff --git a/packages/core/src/decorators/attachment/Attachment.ts b/packages/core/src/decorators/attachment/Attachment.ts index b39fa52d8d..50a91e8edb 100644 --- a/packages/core/src/decorators/attachment/Attachment.ts +++ b/packages/core/src/decorators/attachment/Attachment.ts @@ -25,7 +25,7 @@ export interface AttachmentOptions { mimeType?: string lastmodTime?: Date byteCount?: number - data: AttachmentData + data: AttachmentDataOptions } export interface AttachmentDataOptions { @@ -97,7 +97,7 @@ export class Attachment { this.mimeType = options.mimeType this.lastmodTime = options.lastmodTime this.byteCount = options.byteCount - this.data = options.data + this.data = new AttachmentData(options.data) } } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index f228f4fa64..26e971e393 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -447,8 +447,8 @@ export class ConnectionService { previousSentMessage, previousReceivedMessage, }: { - previousSentMessage?: AgentMessage - previousReceivedMessage?: AgentMessage + previousSentMessage?: AgentMessage | null + previousReceivedMessage?: AgentMessage | null } = {} ) { const { connection, message } = messageContext diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index e69fe6c6a5..06e7f6a712 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -1,23 +1,23 @@ -import type { CredentialFormatsFromProtocols, DeleteCredentialOptions } from './CredentialProtocolOptions' import type { AcceptCredentialOptions, AcceptCredentialOfferOptions, AcceptCredentialProposalOptions, AcceptCredentialRequestOptions, - CreateOfferOptions, + CreateCredentialOfferOptions, FindCredentialMessageReturn, FindCredentialOfferMessageReturn, FindCredentialProposalMessageReturn, FindCredentialRequestMessageReturn, - GetFormatDataReturn, + GetCredentialFormatDataReturn, NegotiateCredentialOfferOptions, NegotiateCredentialProposalOptions, OfferCredentialOptions, ProposeCredentialOptions, SendCredentialProblemReportOptions, - CredentialProtocolMap, + DeleteCredentialOptions, } from './CredentialsApiOptions' import type { CredentialProtocol } from './protocol/CredentialProtocol' +import type { CredentialFormatsFromProtocols } from './protocol/CredentialProtocolOptions' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' import type { AgentMessage } from '../../agent/AgentMessage' import type { Query } from '../../storage/StorageService' @@ -63,7 +63,7 @@ export interface CredentialsApi { acceptCredential(options: AcceptCredentialOptions): Promise // out of band - createOffer(options: CreateOfferOptions): Promise<{ + createOffer(options: CreateCredentialOfferOptions): Promise<{ message: AgentMessage credentialRecord: CredentialExchangeRecord }> @@ -77,7 +77,7 @@ export interface CredentialsApi { findById(credentialRecordId: string): Promise deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise update(credentialRecord: CredentialExchangeRecord): Promise - getFormatData(credentialRecordId: string): Promise>> + getFormatData(credentialRecordId: string): Promise>> // DidComm Message Records findProposalMessage(credentialExchangeId: string): Promise> @@ -89,7 +89,7 @@ export interface CredentialsApi { @injectable() export class CredentialsApi implements CredentialsApi { /** - * Configuration for the connections module + * Configuration for the credentials module */ public readonly config: CredentialsModuleConfig @@ -100,7 +100,6 @@ export class CredentialsApi implements Credent private didCommMessageRepository: DidCommMessageRepository private routingService: RoutingService private logger: Logger - private credentialProtocolMap: CredentialProtocolMap public constructor( messageSender: MessageSender, @@ -123,58 +122,49 @@ export class CredentialsApi implements Credent this.didCommMessageRepository = didCommMessageRepository this.logger = logger this.config = config - - // Dynamically build service map. This will be extracted once services are registered dynamically - this.credentialProtocolMap = config.credentialProtocols.reduce( - (protocolMap, service) => ({ - ...protocolMap, - [service.version]: service, - }), - {} - ) as CredentialProtocolMap } - private getProtocol>(protocolVersion: PVT): CredentialProtocol { - if (!this.credentialProtocolMap[protocolVersion]) { + private getProtocol(protocolVersion: PVT): CredentialProtocol { + const credentialProtocol = this.config.credentialProtocols.find((protocol) => protocol.version === protocolVersion) + + if (!credentialProtocol) { throw new AriesFrameworkError(`No credential protocol registered for protocol version ${protocolVersion}`) } - return this.credentialProtocolMap[protocolVersion] as CredentialProtocol + return credentialProtocol } /** * Initiate a new credential exchange as holder by sending a credential proposal message - * to the connection with the specified credential options + * to the connection with the specified connection id. * * @param options configuration to use for the proposal * @returns Credential exchange record associated with the sent proposal message */ public async proposeCredential(options: ProposeCredentialOptions): Promise { - const service = this.getProtocol(options.protocolVersion) + const protocol = this.getProtocol(options.protocolVersion) - this.logger.debug(`Got a credentialProtocol object for version ${options.protocolVersion}`) + const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) - const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + // Assert + connectionRecord.assertReady() // will get back a credential record -> map to Credential Exchange Record - const { credentialRecord, message } = await service.createProposal(this.agentContext, { - connection, + const { credentialRecord, message } = await protocol.createProposal(this.agentContext, { + connectionRecord, credentialFormats: options.credentialFormats, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, }) - this.logger.debug('We have a message (sending outbound): ', message) - // send the message here const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: credentialRecord, }) - this.logger.debug('In proposeCredential: Send Proposal to Issuer') await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -196,11 +186,15 @@ export class CredentialsApi implements Credent ) } - // with version we can get the Service - const service = this.getProtocol(credentialRecord.protocolVersion) + // with version we can get the protocol + const protocol = this.getProtocol(credentialRecord.protocolVersion) + const connectionRecord = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + + // Assert + connectionRecord.assertReady() // will get back a credential record -> map to Credential Exchange Record - const { message } = await service.acceptProposal(this.agentContext, { + const { message } = await protocol.acceptProposal(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -208,10 +202,9 @@ export class CredentialsApi implements Credent }) // send the message - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: credentialRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -237,9 +230,9 @@ export class CredentialsApi implements Credent } // with version we can get the Service - const service = this.getProtocol(credentialRecord.protocolVersion) + const protocol = this.getProtocol(credentialRecord.protocolVersion) - const { message } = await service.negotiateProposal(this.agentContext, { + const { message } = await protocol.negotiateProposal(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -265,22 +258,22 @@ export class CredentialsApi implements Credent * @returns Credential exchange record associated with the sent credential offer message */ public async offerCredential(options: OfferCredentialOptions): Promise { - const connection = await this.connectionService.getById(this.agentContext, options.connectionId) - const service = this.getProtocol(options.protocolVersion) + const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) + const protocol = this.getProtocol(options.protocolVersion) this.logger.debug(`Got a credentialProtocol object for version ${options.protocolVersion}`) - const { message, credentialRecord } = await service.createOffer(this.agentContext, { + const { message, credentialRecord } = await protocol.createOffer(this.agentContext, { credentialFormats: options.credentialFormats, autoAcceptCredential: options.autoAcceptCredential, comment: options.comment, - connection, + connectionRecord, }) this.logger.debug('Offer Message successfully created; message= ', message) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: credentialRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -298,16 +291,19 @@ export class CredentialsApi implements Credent public async acceptOffer(options: AcceptCredentialOfferOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) - const service = this.getProtocol(credentialRecord.protocolVersion) + const protocol = this.getProtocol(credentialRecord.protocolVersion) - this.logger.debug(`Got a credentialProtocol object for this version; version = ${service.version}`) - const offerMessage = await service.findOfferMessage(this.agentContext, credentialRecord.id) + this.logger.debug(`Got a credentialProtocol object for this version; version = ${protocol.version}`) + const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) // Use connection if present if (credentialRecord.connectionId) { - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const { message } = await service.acceptOffer(this.agentContext, { + // Assert + connectionRecord.assertReady() + + const { message } = await protocol.acceptOffer(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -316,7 +312,7 @@ export class CredentialsApi implements Credent const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: credentialRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -334,7 +330,7 @@ export class CredentialsApi implements Credent }) const recipientService = offerMessage.service - const { message } = await service.acceptOffer(this.agentContext, { + const { message } = await protocol.acceptOffer(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -375,8 +371,8 @@ export class CredentialsApi implements Credent credentialRecord.assertState(CredentialState.OfferReceived) // with version we can get the Service - const service = this.getProtocol(credentialRecord.protocolVersion) - await service.updateState(this.agentContext, credentialRecord, CredentialState.Declined) + const protocol = this.getProtocol(credentialRecord.protocolVersion) + await protocol.updateState(this.agentContext, credentialRecord, CredentialState.Declined) return credentialRecord } @@ -384,24 +380,28 @@ export class CredentialsApi implements Credent public async negotiateOffer(options: NegotiateCredentialOfferOptions): Promise { const credentialRecord = await this.getById(options.credentialRecordId) - const service = this.getProtocol(credentialRecord.protocolVersion) - const { message } = await service.negotiateOffer(this.agentContext, { - credentialFormats: options.credentialFormats, - credentialRecord, - comment: options.comment, - autoAcceptCredential: options.autoAcceptCredential, - }) - if (!credentialRecord.connectionId) { throw new AriesFrameworkError( `No connection id for credential record ${credentialRecord.id} not found. Connection-less issuance does not support negotiation` ) } - const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) + + // Assert + connectionRecord.assertReady() + + const protocol = this.getProtocol(credentialRecord.protocolVersion) + const { message } = await protocol.negotiateOffer(this.agentContext, { + credentialFormats: options.credentialFormats, + credentialRecord, + comment: options.comment, + autoAcceptCredential: options.autoAcceptCredential, + }) + const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: credentialRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -415,14 +415,14 @@ export class CredentialsApi implements Credent * @param options The credential options to use for the offer * @returns The credential record and credential offer message */ - public async createOffer(options: CreateOfferOptions): Promise<{ + public async createOffer(options: CreateCredentialOfferOptions): Promise<{ message: AgentMessage credentialRecord: CredentialExchangeRecord }> { - const service = this.getProtocol(options.protocolVersion) + const protocol = this.getProtocol(options.protocolVersion) this.logger.debug(`Got a credentialProtocol object for version ${options.protocolVersion}`) - const { message, credentialRecord } = await service.createOffer(this.agentContext, { + const { message, credentialRecord } = await protocol.createOffer(this.agentContext, { credentialFormats: options.credentialFormats, comment: options.comment, autoAcceptCredential: options.autoAcceptCredential, @@ -444,11 +444,11 @@ export class CredentialsApi implements Credent const credentialRecord = await this.getById(options.credentialRecordId) // with version we can get the Service - const service = this.getProtocol(credentialRecord.protocolVersion) + const protocol = this.getProtocol(credentialRecord.protocolVersion) this.logger.debug(`Got a credentialProtocol object for version ${credentialRecord.protocolVersion}`) - const { message } = await service.acceptRequest(this.agentContext, { + const { message } = await protocol.acceptRequest(this.agentContext, { credentialRecord, credentialFormats: options.credentialFormats, comment: options.comment, @@ -456,8 +456,8 @@ export class CredentialsApi implements Credent }) this.logger.debug('We have a credential message (sending outbound): ', message) - const requestMessage = await service.findRequestMessage(this.agentContext, credentialRecord.id) - const offerMessage = await service.findOfferMessage(this.agentContext, credentialRecord.id) + const requestMessage = await protocol.findRequestMessage(this.agentContext, credentialRecord.id) + const offerMessage = await protocol.findOfferMessage(this.agentContext, credentialRecord.id) // Use connection if present if (credentialRecord.connectionId) { @@ -516,16 +516,16 @@ export class CredentialsApi implements Credent const credentialRecord = await this.getById(options.credentialRecordId) // with version we can get the Service - const service = this.getProtocol(credentialRecord.protocolVersion) + const protocol = this.getProtocol(credentialRecord.protocolVersion) this.logger.debug(`Got a credentialProtocol object for version ${credentialRecord.protocolVersion}`) - const { message } = await service.acceptCredential(this.agentContext, { + const { message } = await protocol.acceptCredential(this.agentContext, { credentialRecord, }) - const requestMessage = await service.findRequestMessage(this.agentContext, credentialRecord.id) - const credentialMessage = await service.findCredentialMessage(this.agentContext, credentialRecord.id) + const requestMessage = await protocol.findRequestMessage(this.agentContext, credentialRecord.id) + const credentialMessage = await protocol.findCredentialMessage(this.agentContext, credentialRecord.id) if (credentialRecord.connectionId) { const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) @@ -578,12 +578,15 @@ export class CredentialsApi implements Credent } const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const service = this.getProtocol(credentialRecord.protocolVersion) - const problemReportMessage = service.createProblemReport(this.agentContext, { message: options.message }) - problemReportMessage.setThread({ + const protocol = this.getProtocol(credentialRecord.protocolVersion) + const { message } = await protocol.createProblemReport(this.agentContext, { + description: options.description, + credentialRecord, + }) + message.setThread({ threadId: credentialRecord.threadId, }) - const outboundMessageContext = new OutboundMessageContext(problemReportMessage, { + const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, connection, associatedRecord: credentialRecord, @@ -595,11 +598,11 @@ export class CredentialsApi implements Credent public async getFormatData( credentialRecordId: string - ): Promise>> { + ): Promise>> { const credentialRecord = await this.getById(credentialRecordId) - const service = this.getProtocol(credentialRecord.protocolVersion) + const protocol = this.getProtocol(credentialRecord.protocolVersion) - return service.getFormatData(this.agentContext, credentialRecordId) + return protocol.getFormatData(this.agentContext, credentialRecordId) } /** @@ -650,8 +653,8 @@ export class CredentialsApi implements Credent */ public async deleteById(credentialId: string, options?: DeleteCredentialOptions) { const credentialRecord = await this.getById(credentialId) - const service = this.getProtocol(credentialRecord.protocolVersion) - return service.delete(this.agentContext, credentialRecord, options) + const protocol = this.getProtocol(credentialRecord.protocolVersion) + return protocol.delete(this.agentContext, credentialRecord, options) } /** @@ -664,33 +667,33 @@ export class CredentialsApi implements Credent } public async findProposalMessage(credentialExchangeId: string): Promise> { - const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + const protocol = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findProposalMessage( + return protocol.findProposalMessage( this.agentContext, credentialExchangeId ) as FindCredentialProposalMessageReturn } public async findOfferMessage(credentialExchangeId: string): Promise> { - const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + const protocol = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findOfferMessage(this.agentContext, credentialExchangeId) as FindCredentialOfferMessageReturn + return protocol.findOfferMessage(this.agentContext, credentialExchangeId) as FindCredentialOfferMessageReturn } public async findRequestMessage(credentialExchangeId: string): Promise> { - const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + const protocol = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findRequestMessage( + return protocol.findRequestMessage( this.agentContext, credentialExchangeId ) as FindCredentialRequestMessageReturn } public async findCredentialMessage(credentialExchangeId: string): Promise> { - const service = await this.getServiceForCredentialExchangeId(credentialExchangeId) + const protocol = await this.getServiceForCredentialExchangeId(credentialExchangeId) - return service.findCredentialMessage(this.agentContext, credentialExchangeId) as FindCredentialMessageReturn + return protocol.findCredentialMessage(this.agentContext, credentialExchangeId) as FindCredentialMessageReturn } private async getServiceForCredentialExchangeId(credentialExchangeId: string) { diff --git a/packages/core/src/modules/credentials/CredentialsApiOptions.ts b/packages/core/src/modules/credentials/CredentialsApiOptions.ts index 24fb0a86d1..19e9f17295 100644 --- a/packages/core/src/modules/credentials/CredentialsApiOptions.ts +++ b/packages/core/src/modules/credentials/CredentialsApiOptions.ts @@ -1,10 +1,14 @@ -import type { CredentialFormatsFromProtocols, GetFormatDataReturn } from './CredentialProtocolOptions' import type { CredentialFormatPayload } from './formats' -import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' +import type { AutoAcceptCredential } from './models' import type { CredentialProtocol } from './protocol/CredentialProtocol' +import type { + CredentialFormatsFromProtocols, + DeleteCredentialOptions, + GetCredentialFormatDataReturn, +} from './protocol/CredentialProtocolOptions' -// re-export GetFormatDataReturn type from service, as it is also used in the module -export type { GetFormatDataReturn } +// re-export GetCredentialFormatDataReturn type from protocol, as it is also used in the api +export type { GetCredentialFormatDataReturn, DeleteCredentialOptions } export type FindCredentialProposalMessageReturn = ReturnType< CPs[number]['findProposalMessage'] @@ -25,23 +29,6 @@ export type FindCredentialMessageReturn = CPs[number]['version'] -/** - * Get the service map for usage in the credentials module. Will return a type mapping of protocol version to service. - * - * @example - * ``` - * type ProtocolMap = CredentialProtocolMap<[IndyCredentialFormatService], [V1CredentialProtocol]> - * - * // equal to - * type ProtocolMap = { - * v1: V1CredentialProtocol - * } - * ``` - */ -export type CredentialProtocolMap = { - [CP in CPs[number] as CP['version']]: CredentialProtocol -} - interface BaseOptions { autoAcceptCredential?: AutoAcceptCredential comment?: string @@ -79,17 +66,18 @@ export interface NegotiateCredentialProposalOptions extends BaseOptions { +export interface CreateCredentialOfferOptions + extends BaseOptions { protocolVersion: CredentialProtocolVersionType credentialFormats: CredentialFormatPayload, 'createOffer'> } /** - * Interface for CredentialsApi.offerCredentials. Extends CreateOfferOptions, will send an offer + * Interface for CredentialsApi.offerCredential. Extends CreateCredentialOfferOptions, will send an offer */ export interface OfferCredentialOptions extends BaseOptions, - CreateOfferOptions { + CreateCredentialOfferOptions { connectionId: string } @@ -138,5 +126,5 @@ export interface AcceptCredentialOptions { */ export interface SendCredentialProblemReportOptions { credentialRecordId: string - message: string + description: string } diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index b141f63f8a..a7d762e248 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -20,7 +20,7 @@ import { CredentialRepository } from './repository' */ export type DefaultCredentialProtocols = [V1CredentialProtocol, V2CredentialProtocol] -// CredentialModuleOptions makes the credentialProtocols property optional from the config, as it will set it when not provided. +// CredentialsModuleOptions makes the credentialProtocols property optional from the config, as it will set it when not provided. export type CredentialsModuleOptions = Optional< CredentialsModuleConfigOptions, 'credentialProtocols' @@ -38,7 +38,7 @@ export class CredentialsModule } diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts index 20d98623d3..ac1ffde0a9 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -1,23 +1,22 @@ import type { CredentialFormat } from './CredentialFormat' import type { - FormatCreateProposalOptions, - FormatCreateProposalReturn, - FormatProcessOptions, - FormatCreateOfferOptions, - FormatCreateOfferReturn, - FormatCreateRequestOptions, + CredentialFormatCreateProposalOptions, + CredentialFormatCreateProposalReturn, + CredentialFormatProcessOptions, + CredentialFormatCreateOfferOptions, + CredentialFormatCreateOfferReturn, + CredentialFormatCreateRequestOptions, CredentialFormatCreateReturn, - FormatAcceptRequestOptions, - FormatAcceptOfferOptions, - FormatAcceptProposalOptions, - FormatAutoRespondCredentialOptions, - FormatAutoRespondOfferOptions, - FormatAutoRespondProposalOptions, - FormatAutoRespondRequestOptions, - FormatProcessCredentialOptions, + CredentialFormatAcceptRequestOptions, + CredentialFormatAcceptOfferOptions, + CredentialFormatAcceptProposalOptions, + CredentialFormatAutoRespondCredentialOptions, + CredentialFormatAutoRespondOfferOptions, + CredentialFormatAutoRespondProposalOptions, + CredentialFormatAutoRespondRequestOptions, + CredentialFormatProcessCredentialOptions, } from './CredentialFormatServiceOptions' import type { AgentContext } from '../../../agent' -import type { Attachment } from '../../../decorators/attachment/Attachment' export interface CredentialFormatService { formatKey: CF['formatKey'] @@ -26,39 +25,58 @@ export interface CredentialFormatService - ): Promise - processProposal(agentContext: AgentContext, options: FormatProcessOptions): Promise - acceptProposal(agentContext: AgentContext, options: FormatAcceptProposalOptions): Promise + options: CredentialFormatCreateProposalOptions + ): Promise + processProposal(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise + acceptProposal( + agentContext: AgentContext, + options: CredentialFormatAcceptProposalOptions + ): Promise // offer methods - createOffer(agentContext: AgentContext, options: FormatCreateOfferOptions): Promise - processOffer(agentContext: AgentContext, options: FormatProcessOptions): Promise - acceptOffer(agentContext: AgentContext, options: FormatAcceptOfferOptions): Promise + createOffer( + agentContext: AgentContext, + options: CredentialFormatCreateOfferOptions + ): Promise + processOffer(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise + acceptOffer( + agentContext: AgentContext, + options: CredentialFormatAcceptOfferOptions + ): Promise // request methods createRequest( agentContext: AgentContext, - options: FormatCreateRequestOptions + options: CredentialFormatCreateRequestOptions ): Promise - processRequest(agentContext: AgentContext, options: FormatProcessOptions): Promise + processRequest(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise acceptRequest( agentContext: AgentContext, - options: FormatAcceptRequestOptions + options: CredentialFormatAcceptRequestOptions ): Promise // credential methods - processCredential(agentContext: AgentContext, options: FormatProcessCredentialOptions): Promise + processCredential(agentContext: AgentContext, options: CredentialFormatProcessCredentialOptions): Promise // auto accept methods - shouldAutoRespondToProposal(agentContext: AgentContext, options: FormatAutoRespondProposalOptions): boolean - shouldAutoRespondToOffer(agentContext: AgentContext, options: FormatAutoRespondOfferOptions): boolean - shouldAutoRespondToRequest(agentContext: AgentContext, options: FormatAutoRespondRequestOptions): boolean - shouldAutoRespondToCredential(agentContext: AgentContext, options: FormatAutoRespondCredentialOptions): boolean + shouldAutoRespondToProposal( + agentContext: AgentContext, + options: CredentialFormatAutoRespondProposalOptions + ): Promise + shouldAutoRespondToOffer( + agentContext: AgentContext, + options: CredentialFormatAutoRespondOfferOptions + ): Promise + shouldAutoRespondToRequest( + agentContext: AgentContext, + options: CredentialFormatAutoRespondRequestOptions + ): Promise + shouldAutoRespondToCredential( + agentContext: AgentContext, + options: CredentialFormatAutoRespondCredentialOptions + ): Promise deleteCredentialById(agentContext: AgentContext, credentialId: string): Promise - supportsFormat(format: string): boolean - - getFormatData(data: unknown, id: string): Attachment + supportsFormat(formatIdentifier: string): boolean } diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts index 2f494da4ae..2d79961ebb 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts @@ -46,93 +46,88 @@ export interface CredentialFormatCreateReturn { } /** - * Base return type for all process methods. + * Base return type for all credential process methods. */ -export interface FormatProcessOptions { +export interface CredentialFormatProcessOptions { attachment: Attachment credentialRecord: CredentialExchangeRecord } -export interface FormatProcessCredentialOptions extends FormatProcessOptions { +export interface CredentialFormatProcessCredentialOptions extends CredentialFormatProcessOptions { requestAttachment: Attachment } -export interface FormatCreateProposalOptions { +export interface CredentialFormatCreateProposalOptions { credentialRecord: CredentialExchangeRecord credentialFormats: CredentialFormatPayload<[CF], 'createProposal'> + attachmentId?: string } -export interface FormatAcceptProposalOptions { +export interface CredentialFormatAcceptProposalOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload<[CF], 'acceptProposal'> - attachId?: string + attachmentId?: string proposalAttachment: Attachment } -export interface FormatCreateProposalReturn extends CredentialFormatCreateReturn { +export interface CredentialFormatCreateProposalReturn extends CredentialFormatCreateReturn { previewAttributes?: CredentialPreviewAttribute[] } -export interface FormatCreateOfferOptions { +export interface CredentialFormatCreateOfferOptions { credentialRecord: CredentialExchangeRecord credentialFormats: CredentialFormatPayload<[CF], 'createOffer'> - attachId?: string + attachmentId?: string } -export interface FormatAcceptOfferOptions { +export interface CredentialFormatAcceptOfferOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload<[CF], 'acceptOffer'> - attachId?: string + attachmentId?: string offerAttachment: Attachment } -export interface FormatCreateOfferReturn extends CredentialFormatCreateReturn { +export interface CredentialFormatCreateOfferReturn extends CredentialFormatCreateReturn { previewAttributes?: CredentialPreviewAttribute[] } -export interface FormatCreateRequestOptions { +export interface CredentialFormatCreateRequestOptions { credentialRecord: CredentialExchangeRecord credentialFormats: CredentialFormatPayload<[CF], 'createRequest'> } -export interface FormatAcceptRequestOptions { +export interface CredentialFormatAcceptRequestOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload<[CF], 'acceptRequest'> - attachId?: string + attachmentId?: string requestAttachment: Attachment offerAttachment?: Attachment } -export interface FormatAcceptCredentialOptions { - credentialRecord: CredentialExchangeRecord - attachId?: string - requestAttachment: Attachment - offerAttachment?: Attachment -} // Auto accept method interfaces -export interface FormatAutoRespondProposalOptions { +export interface CredentialFormatAutoRespondProposalOptions { credentialRecord: CredentialExchangeRecord proposalAttachment: Attachment offerAttachment: Attachment } -export interface FormatAutoRespondOfferOptions { +export interface CredentialFormatAutoRespondOfferOptions { credentialRecord: CredentialExchangeRecord proposalAttachment: Attachment offerAttachment: Attachment } -export interface FormatAutoRespondRequestOptions { +export interface CredentialFormatAutoRespondRequestOptions { credentialRecord: CredentialExchangeRecord proposalAttachment?: Attachment offerAttachment: Attachment requestAttachment: Attachment } -export interface FormatAutoRespondCredentialOptions { +export interface CredentialFormatAutoRespondCredentialOptions { credentialRecord: CredentialExchangeRecord proposalAttachment?: Attachment offerAttachment?: Attachment diff --git a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts index 90b6db58c0..bbe6f379f8 100644 --- a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts @@ -136,7 +136,7 @@ const mockCredentialRecord = ({ id: '', formats: [ { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, format: 'hlindy/cred-abstract@v2.0', }, ], @@ -248,7 +248,7 @@ describe('Indy CredentialFormatService', () => { ]) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'hlindy/cred-filter@v2.0', }) }) @@ -299,7 +299,7 @@ describe('Indy CredentialFormatService', () => { ]) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'hlindy/cred-abstract@v2.0', }) }) @@ -360,7 +360,7 @@ describe('Indy CredentialFormatService', () => { }, }) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'hlindy/cred-req@v2.0', }) @@ -387,7 +387,7 @@ describe('Indy CredentialFormatService', () => { credentialRecord, requestAttachment, offerAttachment, - attachId: INDY_CREDENTIAL_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_ATTACHMENT_ID, }) expect(attachment).toMatchObject({ @@ -407,7 +407,7 @@ describe('Indy CredentialFormatService', () => { }, }) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'hlindy/cred@v2.0', }) }) diff --git a/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts index b94e44651b..59761f5808 100644 --- a/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/__tests__/JsonLdCredentialFormatService.test.ts @@ -117,7 +117,7 @@ const mockCredentialRecord = ({ id: '', formats: [ { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, format: 'hlindy/cred-abstract@v2.0', }, ], @@ -220,7 +220,7 @@ describe('JsonLd CredentialFormatService', () => { }) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'aries/ld-proof-vc-detail@v1.0', }) }) @@ -255,7 +255,7 @@ describe('JsonLd CredentialFormatService', () => { expect(previewAttributes).toBeUndefined() expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'aries/ld-proof-vc-detail@v1.0', }) }) @@ -294,7 +294,7 @@ describe('JsonLd CredentialFormatService', () => { }, }) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'aries/ld-proof-vc-detail@v1.0', }) }) @@ -362,7 +362,7 @@ describe('JsonLd CredentialFormatService', () => { }, }) expect(format).toMatchObject({ - attachId: expect.any(String), + attachmentId: expect.any(String), format: 'aries/ld-proof-vc@1.0', }) }) @@ -552,7 +552,7 @@ describe('JsonLd CredentialFormatService', () => { }) // indirectly test areCredentialsEqual as black box rather than expose that method in the API - let areCredentialsEqual = jsonLdFormatService.shouldAutoRespondToProposal(agentContext, { + let areCredentialsEqual = await jsonLdFormatService.shouldAutoRespondToProposal(agentContext, { credentialRecord, proposalAttachment: message1, offerAttachment: message2, @@ -570,7 +570,7 @@ describe('JsonLd CredentialFormatService', () => { base64: JsonEncoder.toBase64(inputDoc2), }) - areCredentialsEqual = jsonLdFormatService.shouldAutoRespondToProposal(agentContext, { + areCredentialsEqual = await jsonLdFormatService.shouldAutoRespondToProposal(agentContext, { credentialRecord, proposalAttachment: message1, offerAttachment: message2, diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts index aca8aec43a..e62124e6f2 100644 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts @@ -5,20 +5,20 @@ import type { CredentialPreviewAttributeOptions } from '../../models/CredentialP import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' import type { CredentialFormatService } from '../CredentialFormatService' import type { - FormatAcceptOfferOptions, - FormatAcceptProposalOptions, - FormatAcceptRequestOptions, - FormatAutoRespondCredentialOptions, - FormatAutoRespondOfferOptions, - FormatAutoRespondProposalOptions, - FormatAutoRespondRequestOptions, - FormatCreateOfferOptions, - FormatCreateOfferReturn, - FormatCreateProposalOptions, - FormatCreateProposalReturn, + CredentialFormatAcceptOfferOptions, + CredentialFormatAcceptProposalOptions, + CredentialFormatAcceptRequestOptions, + CredentialFormatAutoRespondCredentialOptions, + CredentialFormatAutoRespondOfferOptions, + CredentialFormatAutoRespondProposalOptions, + CredentialFormatAutoRespondRequestOptions, + CredentialFormatCreateOfferOptions, + CredentialFormatCreateOfferReturn, + CredentialFormatCreateProposalOptions, + CredentialFormatCreateProposalReturn, CredentialFormatCreateReturn, - FormatProcessOptions, - FormatProcessCredentialOptions, + CredentialFormatProcessOptions, + CredentialFormatProcessCredentialOptions, } from '../CredentialFormatServiceOptions' import type * as Indy from 'indy-sdk' @@ -62,10 +62,11 @@ export class IndyCredentialFormatService implements CredentialFormatService - ): Promise { + { credentialFormats, credentialRecord, attachmentId }: CredentialFormatCreateProposalOptions + ): Promise { const format = new CredentialFormatSpec({ format: INDY_CRED_FILTER, + attachmentId, }) const indyFormat = credentialFormats.indy @@ -86,7 +87,7 @@ export class IndyCredentialFormatService implements CredentialFormatService { + public async processProposal( + agentContext: AgentContext, + { attachment }: CredentialFormatProcessOptions + ): Promise { const proposalJson = attachment.getDataAsJson() // fromJSON also validates @@ -112,12 +116,12 @@ export class IndyCredentialFormatService implements CredentialFormatService - ): Promise { + }: CredentialFormatAcceptProposalOptions + ): Promise { const indyFormat = credentialFormats?.indy const credentialProposal = JsonTransformer.fromJSON(proposalAttachment.getDataAsJson(), IndyCredPropose) @@ -137,7 +141,7 @@ export class IndyCredentialFormatService implements CredentialFormatService - ): Promise { + { credentialFormats, credentialRecord, attachmentId }: CredentialFormatCreateOfferOptions + ): Promise { const indyFormat = credentialFormats.indy if (!indyFormat) { @@ -165,7 +169,7 @@ export class IndyCredentialFormatService implements CredentialFormatService() @@ -188,7 +195,12 @@ export class IndyCredentialFormatService implements CredentialFormatService + { + credentialFormats, + credentialRecord, + attachmentId, + offerAttachment, + }: CredentialFormatAcceptOfferOptions ): Promise { const indyFormat = credentialFormats?.indy @@ -219,11 +231,11 @@ export class IndyCredentialFormatService implements CredentialFormatService { + public async processRequest(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise { // not needed for Indy } public async acceptRequest( agentContext: AgentContext, - { credentialRecord, attachId, offerAttachment, requestAttachment }: FormatAcceptRequestOptions + { + credentialRecord, + attachmentId, + offerAttachment, + requestAttachment, + }: CredentialFormatAcceptRequestOptions ): Promise { // Assert credential attributes const credentialAttributes = credentialRecord.credentialAttributes @@ -278,11 +295,11 @@ export class IndyCredentialFormatService implements CredentialFormatService { const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyRequest) @@ -357,15 +374,15 @@ export class IndyCredentialFormatService implements CredentialFormatService this.supportsFormat(f.format)).map((f) => f.attachId) + const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachmentId) const supportedAttachments = messageAttachments.filter((attachment) => supportedAttachmentIds.includes(attachment.id) ) @@ -379,9 +396,9 @@ export class IndyCredentialFormatService implements CredentialFormatService() const credentialRequestJson = requestAttachment.getDataAsJson() - return credentialOfferJson.cred_def_id == credentialRequestJson.cred_def_id + return credentialOfferJson.cred_def_id === credentialRequestJson.cred_def_id } - public shouldAutoRespondToCredential( + public async shouldAutoRespondToCredential( agentContext: AgentContext, - { credentialRecord, requestAttachment, credentialAttachment }: FormatAutoRespondCredentialOptions + { credentialRecord, requestAttachment, credentialAttachment }: CredentialFormatAutoRespondCredentialOptions ) { const credentialJson = credentialAttachment.getDataAsJson() const credentialRequestJson = requestAttachment.getDataAsJson() @@ -441,23 +458,23 @@ export class IndyCredentialFormatService implements CredentialFormatService { + ): Promise { const indyIssuerService = agentContext.dependencyManager.resolve(IndyIssuerService) // if the proposal has an attachment Id use that, otherwise the generated id of the formats object const format = new CredentialFormatSpec({ - attachId: attachId, + attachmentId, format: INDY_CRED_ABSTRACT, }) @@ -475,7 +492,7 @@ export class IndyCredentialFormatService implements CredentialFormatService + schemaId: string + credentialDefinitionId: string + revocationRegistryId?: string + credentialRevocationId?: string +} + export class IndyCredentialInfo { - public constructor(options: IndyCredentialInfo) { + public constructor(options: IndyCredentialInfoOptions) { if (options) { this.referent = options.referent this.attributes = options.attributes diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index 36f88eb4db..52be8f6493 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -7,21 +7,21 @@ import type { import type { AgentContext } from '../../../../agent' import type { CredentialFormatService } from '../CredentialFormatService' import type { - FormatAcceptOfferOptions, - FormatAcceptProposalOptions, - FormatAcceptRequestOptions, - FormatAutoRespondOfferOptions, - FormatAutoRespondProposalOptions, - FormatAutoRespondRequestOptions, - FormatCreateOfferOptions, - FormatCreateOfferReturn, - FormatCreateProposalOptions, - FormatCreateProposalReturn, - FormatCreateRequestOptions, + CredentialFormatAcceptOfferOptions, + CredentialFormatAcceptProposalOptions, + CredentialFormatAcceptRequestOptions, + CredentialFormatAutoRespondOfferOptions, + CredentialFormatAutoRespondProposalOptions, + CredentialFormatAutoRespondRequestOptions, + CredentialFormatCreateOfferOptions, + CredentialFormatCreateOfferReturn, + CredentialFormatCreateProposalOptions, + CredentialFormatCreateProposalReturn, + CredentialFormatCreateRequestOptions, CredentialFormatCreateReturn, - FormatProcessCredentialOptions, - FormatProcessOptions, - FormatAutoRespondCredentialOptions, + CredentialFormatProcessCredentialOptions, + CredentialFormatProcessOptions, + CredentialFormatAutoRespondCredentialOptions, } from '../CredentialFormatServiceOptions' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' @@ -52,8 +52,8 @@ export class JsonLdCredentialFormatService implements CredentialFormatService - ): Promise { + { credentialFormats }: CredentialFormatCreateProposalOptions + ): Promise { const format = new CredentialFormatSpec({ format: JSONLD_VC_DETAIL, }) @@ -67,7 +67,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService { + public async processProposal( + agentContext: AgentContext, + { attachment }: CredentialFormatProcessOptions + ): Promise { const credProposalJson = attachment.getDataAsJson() if (!credProposalJson) { @@ -88,11 +91,11 @@ export class JsonLdCredentialFormatService implements CredentialFormatService - ): Promise { + { attachmentId, proposalAttachment }: CredentialFormatAcceptProposalOptions + ): Promise { // if the offer has an attachment Id use that, otherwise the generated id of the formats object const format = new CredentialFormatSpec({ - attachId, + attachmentId, format: JSONLD_VC_DETAIL, }) @@ -101,7 +104,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService - ): Promise { + { credentialFormats, attachmentId }: CredentialFormatCreateOfferOptions + ): Promise { // if the offer has an attachment Id use that, otherwise the generated id of the formats object const format = new CredentialFormatSpec({ - attachId, + attachmentId, format: JSONLD_VC_DETAIL, }) @@ -131,12 +134,12 @@ export class JsonLdCredentialFormatService implements CredentialFormatService() if (!credentialOfferJson) { @@ -148,7 +151,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService + { attachmentId, offerAttachment }: CredentialFormatAcceptOfferOptions ): Promise { const credentialOffer = offerAttachment.getDataAsJson() @@ -156,11 +159,11 @@ export class JsonLdCredentialFormatService implements CredentialFormatService + { credentialFormats }: CredentialFormatCreateRequestOptions ): Promise { const jsonLdFormat = credentialFormats?.jsonld @@ -188,12 +191,15 @@ export class JsonLdCredentialFormatService implements CredentialFormatService { + public async processRequest( + agentContext: AgentContext, + { attachment }: CredentialFormatProcessOptions + ): Promise { const requestJson = attachment.getDataAsJson() if (!requestJson) { @@ -206,7 +212,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService + { credentialFormats, attachmentId, requestAttachment }: CredentialFormatAcceptRequestOptions ): Promise { const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) @@ -222,7 +228,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService { const w3cCredentialService = agentContext.dependencyManager.resolve(W3cCredentialService) @@ -385,30 +391,30 @@ export class JsonLdCredentialFormatService implements CredentialFormatService() const w3cCredential = JsonTransformer.fromJSON(credentialJson, W3cVerifiableCredential) @@ -432,7 +438,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService + options: CreateCredentialProposalOptions ): Promise> public abstract processProposal( messageContext: InboundMessageContext ): Promise public abstract acceptProposal( agentContext: AgentContext, - options: AcceptProposalOptions + options: AcceptCredentialProposalOptions ): Promise> public abstract negotiateProposal( agentContext: AgentContext, - options: NegotiateProposalOptions + options: NegotiateCredentialProposalOptions ): Promise> // methods for offer public abstract createOffer( agentContext: AgentContext, - options: CreateOfferOptions + options: CreateCredentialOfferOptions ): Promise> public abstract processOffer(messageContext: InboundMessageContext): Promise public abstract acceptOffer( agentContext: AgentContext, - options: AcceptOfferOptions + options: AcceptCredentialOfferOptions ): Promise> public abstract negotiateOffer( agentContext: AgentContext, - options: NegotiateOfferOptions + options: NegotiateCredentialOfferOptions ): Promise> // methods for request public abstract createRequest( agentContext: AgentContext, - options: CreateRequestOptions + options: CreateCredentialRequestOptions ): Promise> public abstract processRequest(messageContext: InboundMessageContext): Promise public abstract acceptRequest( agentContext: AgentContext, - options: AcceptRequestOptions + options: AcceptCredentialRequestOptions ): Promise> // methods for issue @@ -101,8 +101,8 @@ export abstract class BaseCredentialProtocol> public abstract findProposalMessage( agentContext: AgentContext, @@ -123,25 +123,10 @@ export abstract class BaseCredentialProtocol>> + ): Promise>> public abstract register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void - /** - * Decline a credential offer - * @param credentialRecord The credential to be declined - */ - public async declineOffer( - agentContext: AgentContext, - credentialRecord: CredentialExchangeRecord - ): Promise { - credentialRecord.assertState(CredentialState.OfferReceived) - - await this.updateState(agentContext, credentialRecord, CredentialState.Declined) - - return credentialRecord - } - /** * Process a received credential {@link ProblemReportMessage}. * @@ -155,17 +140,17 @@ export abstract class BaseCredentialProtocol { + public findById(agentContext: AgentContext, proofRecordId: string): Promise { const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) - return credentialRepository.findById(agentContext, connectionId) + return credentialRepository.findById(agentContext, proofRecordId) } public async delete( diff --git a/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts index 77665a8236..b91939bbcf 100644 --- a/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts @@ -1,3 +1,18 @@ +import type { + CreateCredentialProposalOptions, + CredentialProtocolMsgReturnType, + DeleteCredentialOptions, + AcceptCredentialProposalOptions, + NegotiateCredentialProposalOptions, + CreateCredentialOfferOptions, + NegotiateCredentialOfferOptions, + CreateCredentialRequestOptions, + AcceptCredentialOfferOptions, + AcceptCredentialRequestOptions, + AcceptCredentialOptions, + GetCredentialFormatDataReturn, + CreateCredentialProblemReportOptions, +} from './CredentialProtocolOptions' import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' @@ -5,21 +20,6 @@ import type { InboundMessageContext } from '../../../agent/models/InboundMessage import type { DependencyManager } from '../../../plugins' import type { Query } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' -import type { - CreateProposalOptions, - CredentialProtocolMsgReturnType, - DeleteCredentialOptions, - AcceptProposalOptions, - NegotiateProposalOptions, - CreateOfferOptions, - NegotiateOfferOptions, - CreateRequestOptions, - AcceptOfferOptions, - AcceptRequestOptions, - AcceptCredentialOptions, - GetFormatDataReturn, - CreateProblemReportOptions, -} from '../CredentialProtocolOptions' import type { CredentialFormatService, ExtractCredentialFormats } from '../formats' import type { CredentialState } from '../models/CredentialState' import type { CredentialExchangeRecord } from '../repository' @@ -30,42 +30,42 @@ export interface CredentialProtocol + options: CreateCredentialProposalOptions ): Promise> processProposal(messageContext: InboundMessageContext): Promise acceptProposal( agentContext: AgentContext, - options: AcceptProposalOptions + options: AcceptCredentialProposalOptions ): Promise> negotiateProposal( agentContext: AgentContext, - options: NegotiateProposalOptions + options: NegotiateCredentialProposalOptions ): Promise> // methods for offer createOffer( agentContext: AgentContext, - options: CreateOfferOptions + options: CreateCredentialOfferOptions ): Promise> processOffer(messageContext: InboundMessageContext): Promise acceptOffer( agentContext: AgentContext, - options: AcceptOfferOptions + options: AcceptCredentialOfferOptions ): Promise> negotiateOffer( agentContext: AgentContext, - options: NegotiateOfferOptions + options: NegotiateCredentialOfferOptions ): Promise> // methods for request createRequest( agentContext: AgentContext, - options: CreateRequestOptions + options: CreateCredentialRequestOptions ): Promise> processRequest(messageContext: InboundMessageContext): Promise acceptRequest( agentContext: AgentContext, - options: AcceptRequestOptions + options: AcceptCredentialRequestOptions ): Promise> // methods for issue @@ -79,7 +79,11 @@ export interface CredentialProtocol): Promise // methods for problem-report - createProblemReport(agentContext: AgentContext, options: CreateProblemReportOptions): ProblemReportMessage + createProblemReport( + agentContext: AgentContext, + options: CreateCredentialProblemReportOptions + ): Promise> + processProblemReport(messageContext: InboundMessageContext): Promise findProposalMessage(agentContext: AgentContext, credentialExchangeId: string): Promise findOfferMessage(agentContext: AgentContext, credentialExchangeId: string): Promise @@ -88,13 +92,7 @@ export interface CredentialProtocol>> - - declineOffer( - agentContext: AgentContext, - credentialRecord: CredentialExchangeRecord - ): Promise - processProblemReport(messageContext: InboundMessageContext): Promise + ): Promise>> // Repository methods updateState( @@ -102,13 +100,13 @@ export interface CredentialProtocol - getById(agentContext: AgentContext, credentialRecordId: string): Promise + getById(agentContext: AgentContext, credentialExchangeId: string): Promise getAll(agentContext: AgentContext): Promise findAllByQuery( agentContext: AgentContext, query: Query ): Promise - findById(agentContext: AgentContext, connectionId: string): Promise + findById(agentContext: AgentContext, credentialExchangeId: string): Promise delete( agentContext: AgentContext, credentialRecord: CredentialExchangeRecord, diff --git a/packages/core/src/modules/credentials/CredentialProtocolOptions.ts b/packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts similarity index 68% rename from packages/core/src/modules/credentials/CredentialProtocolOptions.ts rename to packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts index 5b934bdb0c..bbcbfcf3a1 100644 --- a/packages/core/src/modules/credentials/CredentialProtocolOptions.ts +++ b/packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts @@ -1,15 +1,15 @@ +import type { CredentialProtocol } from './CredentialProtocol' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { ConnectionRecord } from '../../connections/repository/ConnectionRecord' import type { CredentialFormat, CredentialFormatPayload, CredentialFormatService, ExtractCredentialFormats, -} from './formats' -import type { CredentialPreviewAttributeOptions } from './models' -import type { AutoAcceptCredential } from './models/CredentialAutoAcceptType' -import type { CredentialProtocol } from './protocol/CredentialProtocol' -import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' -import type { AgentMessage } from '../../agent/AgentMessage' -import type { ConnectionRecord } from '../connections/repository/ConnectionRecord' +} from '../formats' +import type { CredentialPreviewAttributeOptions } from '../models' +import type { AutoAcceptCredential } from '../models/CredentialAutoAcceptType' +import type { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' /** * Get the format data payload for a specific message from a list of CredentialFormat interfaces and a message @@ -20,7 +20,7 @@ import type { ConnectionRecord } from '../connections/repository/ConnectionRecor * @example * ``` * - * type OfferFormatData = FormatDataMessagePayload<[IndyCredentialFormat, JsonLdCredentialFormat], 'offer'> + * type OfferFormatData = CredentialFormatDataMessagePayload<[IndyCredentialFormat, JsonLdCredentialFormat], 'createOffer'> * * // equal to * type OfferFormatData = { @@ -33,7 +33,7 @@ import type { ConnectionRecord } from '../connections/repository/ConnectionRecor * } * ``` */ -export type FormatDataMessagePayload< +export type CredentialFormatDataMessagePayload< CFs extends CredentialFormat[] = CredentialFormat[], M extends keyof CredentialFormat['formatData'] = keyof CredentialFormat['formatData'] > = { @@ -80,66 +80,66 @@ export type CredentialFormatsFromProtocols = * } * ``` */ -export type GetFormatDataReturn = { +export type GetCredentialFormatDataReturn = { proposalAttributes?: CredentialPreviewAttributeOptions[] - proposal?: FormatDataMessagePayload - offer?: FormatDataMessagePayload + proposal?: CredentialFormatDataMessagePayload + offer?: CredentialFormatDataMessagePayload offerAttributes?: CredentialPreviewAttributeOptions[] - request?: FormatDataMessagePayload - credential?: FormatDataMessagePayload + request?: CredentialFormatDataMessagePayload + credential?: CredentialFormatDataMessagePayload } -export interface CreateProposalOptions { - connection: ConnectionRecord +export interface CreateCredentialProposalOptions { + connectionRecord: ConnectionRecord credentialFormats: CredentialFormatPayload, 'createProposal'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface AcceptProposalOptions { +export interface AcceptCredentialProposalOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload, 'acceptProposal'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface NegotiateProposalOptions { +export interface NegotiateCredentialProposalOptions { credentialRecord: CredentialExchangeRecord credentialFormats: CredentialFormatPayload, 'createOffer'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface CreateOfferOptions { +export interface CreateCredentialOfferOptions { // Create offer can also be used for connection-less, so connection is optional - connection?: ConnectionRecord + connectionRecord?: ConnectionRecord credentialFormats: CredentialFormatPayload, 'createOffer'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface AcceptOfferOptions { +export interface AcceptCredentialOfferOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload, 'acceptOffer'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface NegotiateOfferOptions { +export interface NegotiateCredentialOfferOptions { credentialRecord: CredentialExchangeRecord credentialFormats: CredentialFormatPayload, 'createProposal'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface CreateRequestOptions { - connection: ConnectionRecord +export interface CreateCredentialRequestOptions { + connectionRecord: ConnectionRecord credentialFormats: CredentialFormatPayload, 'createRequest'> autoAcceptCredential?: AutoAcceptCredential comment?: string } -export interface AcceptRequestOptions { +export interface AcceptCredentialRequestOptions { credentialRecord: CredentialExchangeRecord credentialFormats?: CredentialFormatPayload, 'acceptRequest'> autoAcceptCredential?: AutoAcceptCredential @@ -150,8 +150,9 @@ export interface AcceptCredentialOptions { credentialRecord: CredentialExchangeRecord } -export interface CreateProblemReportOptions { - message: string +export interface CreateCredentialProblemReportOptions { + credentialRecord: CredentialExchangeRecord + description: string } export interface CredentialProtocolMsgReturnType { diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts index 879ef75fd3..59338c7835 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts @@ -4,25 +4,25 @@ import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import type { DependencyManager } from '../../../../plugins' import type { ProblemReportMessage } from '../../../problem-reports' +import type { GetCredentialFormatDataReturn } from '../../CredentialsApiOptions' +import type { CredentialFormatService, ExtractCredentialFormats, IndyCredentialFormat } from '../../formats' +import type { CredentialProtocol } from '../CredentialProtocol' import type { AcceptCredentialOptions, - AcceptOfferOptions, - AcceptProposalOptions, - AcceptRequestOptions, - CreateOfferOptions, - CreateProblemReportOptions, - CreateProposalOptions, + AcceptCredentialOfferOptions, + AcceptCredentialProposalOptions, + AcceptCredentialRequestOptions, + CreateCredentialOfferOptions, + CreateCredentialProblemReportOptions, + CreateCredentialProposalOptions, CredentialProtocolMsgReturnType, - NegotiateOfferOptions, - NegotiateProposalOptions, -} from '../../CredentialProtocolOptions' -import type { GetFormatDataReturn } from '../../CredentialsApiOptions' -import type { CredentialFormatService, ExtractCredentialFormats, IndyCredentialFormat } from '../../formats' + NegotiateCredentialOfferOptions, + NegotiateCredentialProposalOptions, +} from '../CredentialProtocolOptions' import { Protocol } from '../../../../agent/models/features' import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' -import { injectable } from '../../../../plugins' import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' import { JsonTransformer } from '../../../../utils' import { isLinkedAttachment } from '../../../../utils/attachment' @@ -60,15 +60,19 @@ import { } from './messages' import { V1CredentialPreview } from './messages/V1CredentialPreview' +type IndyCredentialFormatServiceLike = CredentialFormatService + export interface V1CredentialProtocolConfig { // indyCredentialFormat must be a service that implements the `IndyCredentialFormat` interface, however it doesn't // have to be the IndyCredentialFormatService implementation per se. - indyCredentialFormat: CredentialFormatService + indyCredentialFormat: IndyCredentialFormatServiceLike } -@injectable() -export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialFormatService]> { - private indyCredentialFormat: CredentialFormatService +export class V1CredentialProtocol + extends BaseCredentialProtocol<[IndyCredentialFormatServiceLike]> + implements CredentialProtocol<[IndyCredentialFormatServiceLike]> +{ + private indyCredentialFormat: IndyCredentialFormatServiceLike public constructor({ indyCredentialFormat }: V1CredentialProtocolConfig) { super() @@ -77,7 +81,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm } /** - * The version of the issue credential protocol this service supports + * The version of the issue credential protocol this protocol supports */ public readonly version = 'v1' @@ -115,11 +119,11 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm public async createProposal( agentContext: AgentContext, { - connection, + connectionRecord, credentialFormats, comment, autoAcceptCredential, - }: CreateProposalOptions<[CredentialFormatService]> + }: CreateCredentialProposalOptions<[IndyCredentialFormatServiceLike]> ): Promise> { this.assertOnlyIndyFormat(credentialFormats) @@ -136,11 +140,11 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm // Create record const credentialRecord = new CredentialExchangeRecord({ - connectionId: connection.id, + connectionId: connectionRecord.id, threadId: uuid(), state: CredentialState.ProposalSent, linkedAttachments: linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), - autoAcceptCredential: autoAcceptCredential, + autoAcceptCredential, protocolVersion: 'v1', }) @@ -218,18 +222,18 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferSent) - const proposalCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, }) - const offerCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const previousSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proposalCredentialMessage ?? undefined, - previousSentMessage: offerCredentialMessage ?? undefined, + previousReceivedMessage, + previousSentMessage, }) await this.indyCredentialFormat.processProposal(messageContext.agentContext, { @@ -287,7 +291,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialFormats, comment, autoAcceptCredential, - }: AcceptProposalOptions<[CredentialFormatService]> + }: AcceptCredentialProposalOptions<[IndyCredentialFormatServiceLike]> ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') @@ -307,7 +311,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialRecord.credentialAttributes = proposalMessage.credentialPreview?.attributes const { attachment, previewAttributes } = await this.indyCredentialFormat.acceptProposal(agentContext, { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, credentialFormats, credentialRecord, proposalAttachment: new Attachment({ @@ -349,7 +353,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection * associated with the credential record. * - * @param options configuration for the offer see {@link NegotiateProposalOptions} + * @param options configuration for the offer see {@link NegotiateCredentialProposalOptions} * @returns Credential record associated with the credential offer and the corresponding new offer message * */ @@ -360,17 +364,17 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialRecord, comment, autoAcceptCredential, - }: NegotiateProposalOptions<[CredentialFormatService]> + }: NegotiateCredentialProposalOptions<[IndyCredentialFormatServiceLike]> ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalReceived) - if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) + this.assertOnlyIndyFormat(credentialFormats) const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) const { attachment, previewAttributes } = await this.indyCredentialFormat.createOffer(agentContext, { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, credentialFormats, credentialRecord, }) @@ -416,11 +420,11 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialFormats, autoAcceptCredential, comment, - connection, - }: CreateOfferOptions<[CredentialFormatService]> + connectionRecord, + }: CreateCredentialOfferOptions<[IndyCredentialFormatServiceLike]> ): Promise> { // Assert - if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) + this.assertOnlyIndyFormat(credentialFormats) const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -431,7 +435,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm // Create record const credentialRecord = new CredentialExchangeRecord({ - connectionId: connection?.id, + connectionId: connectionRecord?.id, threadId: uuid(), linkedAttachments: credentialFormats.indy.linkedAttachments?.map( (linkedAttachments) => linkedAttachments.attachment @@ -442,7 +446,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm }) const { attachment, previewAttributes } = await this.indyCredentialFormat.createOffer(agentContext, { - attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, credentialFormats, credentialRecord, }) @@ -499,11 +503,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm agentContext.config.logger.debug(`Processing credential offer with id ${offerMessage.id}`) - let credentialRecord = await this.findByThreadAndConnectionId( - messageContext.agentContext, - offerMessage.threadId, - connection?.id - ) + let credentialRecord = await this.findByThreadAndConnectionId(agentContext, offerMessage.threadId, connection?.id) const offerAttachment = offerMessage.getOfferAttachmentById(INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) if (!offerAttachment) { @@ -513,11 +513,11 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm } if (credentialRecord) { - const proposalCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const previousSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1ProposeCredentialMessage, }) - const offerCredentialMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: credentialRecord.id, messageClass: V1OfferCredentialMessage, }) @@ -526,8 +526,8 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalSent) connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: offerCredentialMessage ?? undefined, - previousSentMessage: proposalCredentialMessage ?? undefined, + previousReceivedMessage, + previousSentMessage, }) await this.indyCredentialFormat.processOffer(messageContext.agentContext, { @@ -587,7 +587,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialFormats, comment, autoAcceptCredential, - }: AcceptOfferOptions<[CredentialFormatService]> + }: AcceptCredentialOfferOptions<[IndyCredentialFormatServiceLike]> ): Promise> { // Assert credential credentialRecord.assertProtocolVersion('v1') @@ -610,7 +610,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm const { attachment } = await this.indyCredentialFormat.acceptOffer(agentContext, { credentialRecord, credentialFormats, - attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, offerAttachment, }) @@ -654,7 +654,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialRecord, autoAcceptCredential, comment, - }: NegotiateOfferOptions<[CredentialFormatService]> + }: NegotiateCredentialOfferOptions<[IndyCredentialFormatServiceLike]> ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') @@ -670,7 +670,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm } if (!credentialFormats.indy) { - throw new AriesFrameworkError('Missing indy credential format in v1 create proposal call.') + throw new AriesFrameworkError('Missing indy credential format in v1 negotiate proposal call.') } const { linkedAttachments } = credentialFormats.indy @@ -808,11 +808,12 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialFormats, comment, autoAcceptCredential, - }: AcceptRequestOptions<[CredentialFormatService]> + }: AcceptCredentialRequestOptions<[IndyCredentialFormatServiceLike]> ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.RequestReceived) + if (credentialFormats) this.assertOnlyIndyFormat(credentialFormats) const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -834,17 +835,17 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm ) } - const { attachment: credentialsAttach } = await this.indyCredentialFormat.acceptRequest(agentContext, { + const { attachment } = await this.indyCredentialFormat.acceptRequest(agentContext, { credentialRecord, requestAttachment, offerAttachment, - attachId: INDY_CREDENTIAL_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_ATTACHMENT_ID, credentialFormats, }) const issueMessage = new V1IssueCredentialMessage({ comment, - credentialAttachments: [credentialsAttach], + credentialAttachments: [attachment], attachments: credentialRecord.linkedAttachments, }) @@ -902,8 +903,8 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.RequestSent) connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: offerCredentialMessage ?? undefined, - previousSentMessage: requestCredentialMessage ?? undefined, + previousReceivedMessage: offerCredentialMessage, + previousSentMessage: requestCredentialMessage, }) const issueAttachment = issueMessage.getCredentialAttachmentById(INDY_CREDENTIAL_ATTACHMENT_ID) @@ -1014,13 +1015,18 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm * @returns a {@link V1CredentialProblemReportMessage} * */ - public createProblemReport(agentContext: AgentContext, options: CreateProblemReportOptions): ProblemReportMessage { - return new V1CredentialProblemReportMessage({ + public async createProblemReport( + agentContext: AgentContext, + { credentialRecord, description }: CreateCredentialProblemReportOptions + ): Promise> { + const message = new V1CredentialProblemReportMessage({ description: { - en: options.message, + en: description, code: CredentialProblemReportReason.IssuanceAbandoned, }, }) + + return { message, credentialRecord } } // AUTO RESPOND METHODS @@ -1215,7 +1221,7 @@ export class V1CredentialProtocol extends BaseCredentialProtocol<[CredentialForm public async getFormatData( agentContext: AgentContext, credentialExchangeId: string - ): Promise]>>> { + ): Promise>> { // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. const [proposalMessage, offerMessage, requestMessage, credentialMessage] = await Promise.all([ this.findProposalMessage(agentContext, credentialExchangeId), diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts index f6ba0a6c22..d563555bd5 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -9,7 +9,6 @@ import type { CustomCredentialTags } from '../../../repository/CredentialExchang import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' -import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' @@ -22,7 +21,6 @@ import { uuid } from '../../../../../utils/uuid' import { AckStatus } from '../../../../common' import { DidExchangeState } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' -import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' import { credDef, credReq } from '../../../__tests__/fixtures' import { CredentialProblemReportReason } from '../../../errors/CredentialProblemReportReason' @@ -52,23 +50,17 @@ import { jest.mock('../../../repository/CredentialRepository') jest.mock('../../../formats/indy/IndyCredentialFormatService') jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') -jest.mock('../../../../routing/services/RoutingService') jest.mock('../../../../connections/services/ConnectionService') -jest.mock('../../../../../agent/Dispatcher') // Mock typed object const CredentialRepositoryMock = CredentialRepository as jest.Mock const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock -const DispatcherMock = Dispatcher as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const routingService = new RoutingServiceMock() const indyCredentialFormatService = new IndyCredentialFormatServiceMock() -const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -235,10 +227,8 @@ describe('V1CredentialProtocol', () => { registerInstances: [ [CredentialRepository, credentialRepository], [DidCommMessageRepository, didCommMessageRepository], - [RoutingService, routingService], - [Dispatcher, dispatcher], - [ConnectionService, connectionService], [EventEmitter, eventEmitter], + [ConnectionService, connectionService], ], agentConfig, }) @@ -280,7 +270,7 @@ describe('V1CredentialProtocol', () => { attachment: requestAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, }), }) @@ -310,7 +300,7 @@ describe('V1CredentialProtocol', () => { expect(credentialRepository.update).toHaveBeenCalledTimes(1) expect(indyCredentialFormatService.acceptOffer).toHaveBeenCalledWith(agentContext, { credentialRecord, - attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, offerAttachment, credentialFormats: { indy: { @@ -337,7 +327,7 @@ describe('V1CredentialProtocol', () => { attachment: requestAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, }), }) @@ -443,7 +433,7 @@ describe('V1CredentialProtocol', () => { attachment: credentialAttachment, format: new CredentialFormatSpec({ format: 'the-format', - attachId: 'the-attach-id', + attachmentId: 'the-attach-id', }), }) @@ -473,7 +463,7 @@ describe('V1CredentialProtocol', () => { attachment: credentialAttachment, format: new CredentialFormatSpec({ format: 'the-format', - attachId: 'the-attach-id', + attachmentId: 'the-attach-id', }), }) @@ -513,7 +503,7 @@ describe('V1CredentialProtocol', () => { attachment: credentialAttachment, format: new CredentialFormatSpec({ format: 'the-format', - attachId: 'the-attach-id', + attachmentId: 'the-attach-id', }), }) @@ -536,7 +526,7 @@ describe('V1CredentialProtocol', () => { credentialRecord, requestAttachment, offerAttachment, - attachId: INDY_CREDENTIAL_ATTACHMENT_ID, + attachmentId: INDY_CREDENTIAL_ATTACHMENT_ID, }) }) }) @@ -708,7 +698,6 @@ describe('V1CredentialProtocol', () => { describe('createProblemReport', () => { const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249746' - const message = 'Indy error' let credential: CredentialExchangeRecord beforeEach(() => { @@ -719,16 +708,19 @@ describe('V1CredentialProtocol', () => { }) }) - test('returns problem report message base once get error', () => { + test('returns problem report message base once get error', async () => { // given mockFunction(credentialRepository.getById).mockReturnValue(Promise.resolve(credential)) // when - const credentialProblemReportMessage = credentialProtocol.createProblemReport(agentContext, { message }) + const { message } = await credentialProtocol.createProblemReport(agentContext, { + description: 'Indy error', + credentialRecord: credential, + }) - credentialProblemReportMessage.setThread({ threadId }) + message.setThread({ threadId }) // then - expect(credentialProblemReportMessage.toJSON()).toMatchObject({ + expect(message.toJSON()).toMatchObject({ '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/1.0/problem-report', '~thread': { @@ -736,7 +728,7 @@ describe('V1CredentialProtocol', () => { }, description: { code: CredentialProblemReportReason.IssuanceAbandoned, - en: message, + en: 'Indy error', }, }) }) @@ -909,74 +901,4 @@ describe('V1CredentialProtocol', () => { expect(didCommMessageRepository.delete).toHaveBeenCalledTimes(3) }) }) - - describe('declineOffer', () => { - const threadId = 'fd9c5ddb-ec11-4acd-bc32-540736249754' - let credential: CredentialExchangeRecord - - beforeEach(() => { - credential = mockCredentialRecord({ - state: CredentialState.OfferReceived, - tags: { threadId }, - }) - }) - - test(`updates state to ${CredentialState.Declined}`, async () => { - // given - const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update') - - // when - await credentialProtocol.declineOffer(agentContext, credential) - - // then - const expectedCredentialState = { - state: CredentialState.Declined, - } - expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1) - expect(repositoryUpdateSpy).toHaveBeenNthCalledWith( - 1, - agentContext, - expect.objectContaining(expectedCredentialState) - ) - }) - - test(`emits stateChange event from ${CredentialState.OfferReceived} to ${CredentialState.Declined}`, async () => { - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // given - mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential)) - - // when - await credentialProtocol.declineOffer(agentContext, credential) - - // then - expect(eventListenerMock).toHaveBeenCalledTimes(1) - const [[event]] = eventListenerMock.mock.calls - expect(event).toMatchObject({ - type: 'CredentialStateChanged', - metadata: { - contextCorrelationId: 'mock', - }, - payload: { - previousState: CredentialState.OfferReceived, - credentialRecord: expect.objectContaining({ - state: CredentialState.Declined, - }), - }, - }) - }) - - const validState = CredentialState.OfferReceived - const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) - test(`throws an error when state transition is invalid`, async () => { - await Promise.all( - invalidCredentialStates.map(async (state) => { - await expect( - credentialProtocol.declineOffer(agentContext, mockCredentialRecord({ state, tags: { threadId } })) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) - }) - ) - }) - }) }) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts index 78841df7fe..d1c27861b2 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts @@ -1,5 +1,5 @@ import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { CreateOfferOptions, CreateProposalOptions } from '../../../CredentialProtocolOptions' +import type { CreateCredentialOfferOptions, CreateCredentialProposalOptions } from '../../CredentialProtocolOptions' import { Subject } from 'rxjs' @@ -70,7 +70,7 @@ const agentContext = getAgentContext({ // @ts-ignore indyCredentialFormatService.credentialRecordType = 'indy' -const connection = getMockConnection({ +const connectionRecord = getMockConnection({ id: '123', state: DidExchangeState.Completed, }) @@ -107,7 +107,7 @@ describe('V1CredentialProtocolProposeOffer', () => { beforeEach(async () => { // mock function implementations - mockFunction(connectionService.getById).mockResolvedValue(connection) + mockFunction(connectionService.getById).mockResolvedValue(connectionRecord) mockFunction(indyLedgerService.getCredentialDefinition).mockResolvedValue(credDef) mockFunction(indyLedgerService.getSchema).mockResolvedValue(schema) @@ -121,8 +121,8 @@ describe('V1CredentialProtocolProposeOffer', () => { }) describe('createProposal', () => { - const proposeOptions: CreateProposalOptions<[IndyCredentialFormatService]> = { - connection, + const proposeOptions: CreateCredentialProposalOptions<[IndyCredentialFormatService]> = { + connectionRecord: connectionRecord, credentialFormats: { indy: { credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', @@ -136,6 +136,7 @@ describe('V1CredentialProtocolProposeOffer', () => { }, comment: 'v1 propose credential test', } + test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread id`, async () => { const repositorySaveSpy = jest.spyOn(credentialRepository, 'save') @@ -143,7 +144,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: proposalAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-proposal', + attachmentId: 'indy-proposal', }), }) @@ -157,7 +158,7 @@ describe('V1CredentialProtocolProposeOffer', () => { type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), - connectionId: connection.id, + connectionId: connectionRecord.id, state: CredentialState.ProposalSent, }) ) @@ -171,7 +172,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: proposalAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-proposal', + attachmentId: 'indy-proposal', }), }) @@ -196,7 +197,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: proposalAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-proposal', + attachmentId: 'indy-proposal', }), previewAttributes: credentialPreview.attributes, }) @@ -233,9 +234,9 @@ describe('V1CredentialProtocolProposeOffer', () => { }) describe('createOffer', () => { - const offerOptions: CreateOfferOptions<[IndyCredentialFormatService]> = { + const offerOptions: CreateCredentialOfferOptions<[IndyCredentialFormatService]> = { comment: 'some comment', - connection, + connectionRecord, credentialFormats: { indy: { attributes: credentialPreview.attributes, @@ -249,7 +250,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: offerAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-offer', + attachmentId: 'indy-offer', }), previewAttributes: credentialPreview.attributes, }) @@ -267,7 +268,7 @@ describe('V1CredentialProtocolProposeOffer', () => { id: expect.any(String), createdAt: expect.any(Date), threadId: createdCredentialRecord.threadId, - connectionId: connection.id, + connectionId: connectionRecord.id, state: CredentialState.OfferSent, }) }) @@ -280,7 +281,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: offerAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-offer', + attachmentId: 'indy-offer', }), previewAttributes: credentialPreview.attributes, }) @@ -306,7 +307,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: offerAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-offer', + attachmentId: 'indy-offer', }), }) @@ -320,7 +321,7 @@ describe('V1CredentialProtocolProposeOffer', () => { attachment: offerAttachment, format: new CredentialFormatSpec({ format: 'indy', - attachId: 'indy-offer', + attachmentId: 'indy-offer', }), previewAttributes: credentialPreview.attributes, }) @@ -356,7 +357,10 @@ describe('V1CredentialProtocolProposeOffer', () => { credentialPreview: credentialPreview, offerAttachments: [offerAttachment], }) - const messageContext = new InboundMessageContext(credentialOfferMessage, { agentContext, connection }) + const messageContext = new InboundMessageContext(credentialOfferMessage, { + agentContext, + connection: connectionRecord, + }) test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { // when @@ -371,7 +375,7 @@ describe('V1CredentialProtocolProposeOffer', () => { id: expect.any(String), createdAt: expect.any(Date), threadId: credentialOfferMessage.id, - connectionId: connection.id, + connectionId: connectionRecord.id, state: CredentialState.OfferReceived, credentialAttributes: undefined, }) diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts index e6979ded12..7879222141 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts @@ -55,6 +55,6 @@ export class V1IssueCredentialMessage extends AgentMessage { } public getCredentialAttachmentById(id: string): Attachment | undefined { - return this.credentialAttachments.find((attachment) => attachment.id == id) + return this.credentialAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts index 5e83b70348..51f19b24de 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts @@ -70,6 +70,6 @@ export class V1OfferCredentialMessage extends AgentMessage { } public getOfferAttachmentById(id: string): Attachment | undefined { - return this.offerAttachments.find((attachment) => attachment.id == id) + return this.offerAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts index 3e2f46e8c6..2772595d33 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts @@ -121,12 +121,4 @@ export class V1ProposeCredentialMessage extends AgentMessage { @IsOptional() @Matches(indyDidRegex) public issuerDid?: string - - public getAttachment(): Attachment | undefined { - if (this.appendedAttachments) { - return this.appendedAttachments[0] - } else { - return undefined - } - } } diff --git a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts index 97e5489378..1def0ac9f4 100644 --- a/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts +++ b/packages/core/src/modules/credentials/protocol/v2/CredentialFormatCoordinator.ts @@ -568,6 +568,6 @@ export class CredentialFormatCoordinator if (!format) throw new AriesFrameworkError(`No attachment found for service ${credentialFormatService.formatKey}`) - return format.attachId + return format.attachmentId } } diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts index 7fac43d7e0..df39187210 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts @@ -5,21 +5,6 @@ import type { MessageHandlerInboundMessage } from '../../../../agent/MessageHand import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import type { DependencyManager } from '../../../../plugins' import type { ProblemReportMessage } from '../../../problem-reports' -import type { - AcceptCredentialOptions, - AcceptOfferOptions, - AcceptProposalOptions, - AcceptRequestOptions, - CreateOfferOptions, - CreateProposalOptions, - CreateRequestOptions, - CredentialProtocolMsgReturnType, - FormatDataMessagePayload, - CreateProblemReportOptions, - GetFormatDataReturn, - NegotiateOfferOptions, - NegotiateProposalOptions, -} from '../../CredentialProtocolOptions' import type { CredentialFormat, CredentialFormatPayload, @@ -27,6 +12,22 @@ import type { ExtractCredentialFormats, } from '../../formats' import type { CredentialFormatSpec } from '../../models/CredentialFormatSpec' +import type { CredentialProtocol } from '../CredentialProtocol' +import type { + AcceptCredentialOptions, + AcceptCredentialOfferOptions, + AcceptCredentialProposalOptions, + AcceptCredentialRequestOptions, + CreateCredentialOfferOptions, + CreateCredentialProposalOptions, + CreateCredentialRequestOptions, + CredentialProtocolMsgReturnType, + CredentialFormatDataMessagePayload, + CreateCredentialProblemReportOptions, + GetCredentialFormatDataReturn, + NegotiateCredentialOfferOptions, + NegotiateCredentialProposalOptions, +} from '../CredentialProtocolOptions' import { Protocol } from '../../../../agent/models/features/Protocol' import { AriesFrameworkError } from '../../../../error' @@ -64,9 +65,10 @@ export interface V2CredentialProtocolConfig extends BaseCredentialProtocol { +export class V2CredentialProtocol + extends BaseCredentialProtocol + implements CredentialProtocol +{ private credentialFormatCoordinator = new CredentialFormatCoordinator() private credentialFormats: CFs @@ -95,7 +97,7 @@ export class V2CredentialProtocol< new V2CredentialProblemReportHandler(this), ]) - // Register Issue Credential V1 in feature registry, with supported roles + // Register Issue Credential V2 in feature registry, with supported roles featureRegistry.register( new Protocol({ id: 'https://didcomm.org/issue-credential/2.0', @@ -113,7 +115,7 @@ export class V2CredentialProtocol< */ public async createProposal( agentContext: AgentContext, - { connection, credentialFormats, comment, autoAcceptCredential }: CreateProposalOptions + { connectionRecord, credentialFormats, comment, autoAcceptCredential }: CreateCredentialProposalOptions ): Promise> { agentContext.config.logger.debug('Get the Format Service and Create Proposal Message') @@ -125,7 +127,7 @@ export class V2CredentialProtocol< } const credentialRecord = new CredentialExchangeRecord({ - connectionId: connection.id, + connectionId: connectionRecord.id, threadId: uuid(), state: CredentialState.ProposalSent, autoAcceptCredential, @@ -169,7 +171,7 @@ export class V2CredentialProtocol< connection?.id ) - const formatServices = this.getFormatServicesFromMessage(agentContext, proposalMessage.formats) + const formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to process proposal. No supported formats`) } @@ -230,7 +232,7 @@ export class V2CredentialProtocol< public async acceptProposal( agentContext: AgentContext, - { credentialRecord, credentialFormats, autoAcceptCredential, comment }: AcceptProposalOptions + { credentialRecord, credentialFormats, autoAcceptCredential, comment }: AcceptCredentialProposalOptions ): Promise> { // Assert credentialRecord.assertProtocolVersion('v2') @@ -249,7 +251,7 @@ export class V2CredentialProtocol< messageClass: V2ProposeCredentialMessage, }) - formatServices = this.getFormatServicesFromMessage(agentContext, proposalMessage.formats) + formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) } // If the format services list is still empty, throw an error as we don't support any @@ -277,13 +279,13 @@ export class V2CredentialProtocol< * Negotiate a credential proposal as issuer (by sending a credential offer message) to the connection * associated with the credential record. * - * @param options configuration for the offer see {@link NegotiateProposalOptions} + * @param options configuration for the offer see {@link NegotiateCredentialProposalOptions} * @returns Credential exchange record associated with the credential offer * */ public async negotiateProposal( agentContext: AgentContext, - { credentialRecord, credentialFormats, autoAcceptCredential, comment }: NegotiateProposalOptions + { credentialRecord, credentialFormats, autoAcceptCredential, comment }: NegotiateCredentialProposalOptions ): Promise> { // Assert credentialRecord.assertProtocolVersion('v2') @@ -324,7 +326,7 @@ export class V2CredentialProtocol< */ public async createOffer( agentContext: AgentContext, - { credentialFormats, autoAcceptCredential, comment, connection }: CreateOfferOptions + { credentialFormats, autoAcceptCredential, comment, connectionRecord }: CreateCredentialOfferOptions ): Promise> { const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) @@ -334,7 +336,7 @@ export class V2CredentialProtocol< } const credentialRecord = new CredentialExchangeRecord({ - connectionId: connection?.id, + connectionId: connectionRecord?.id, threadId: uuid(), state: CredentialState.OfferSent, autoAcceptCredential, @@ -380,7 +382,7 @@ export class V2CredentialProtocol< connection?.id ) - const formatServices = this.getFormatServicesFromMessage(agentContext, offerMessage.formats) + const formatServices = this.getFormatServicesFromMessage(offerMessage.formats) if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to process offer. No supported formats`) } @@ -441,7 +443,7 @@ export class V2CredentialProtocol< public async acceptOffer( agentContext: AgentContext, - { credentialRecord, autoAcceptCredential, comment, credentialFormats }: AcceptOfferOptions + { credentialRecord, autoAcceptCredential, comment, credentialFormats }: AcceptCredentialOfferOptions ) { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -460,7 +462,7 @@ export class V2CredentialProtocol< messageClass: V2OfferCredentialMessage, }) - formatServices = this.getFormatServicesFromMessage(agentContext, offerMessage.formats) + formatServices = this.getFormatServicesFromMessage(offerMessage.formats) } // If the format services list is still empty, throw an error as we don't support any @@ -494,7 +496,7 @@ export class V2CredentialProtocol< */ public async negotiateOffer( agentContext: AgentContext, - { credentialRecord, credentialFormats, autoAcceptCredential, comment }: NegotiateOfferOptions + { credentialRecord, credentialFormats, autoAcceptCredential, comment }: NegotiateCredentialOfferOptions ): Promise> { // Assert credentialRecord.assertProtocolVersion('v2') @@ -531,7 +533,7 @@ export class V2CredentialProtocol< */ public async createRequest( agentContext: AgentContext, - { credentialFormats, autoAcceptCredential, comment, connection }: CreateRequestOptions + { credentialFormats, autoAcceptCredential, comment, connectionRecord }: CreateCredentialRequestOptions ): Promise> { const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) @@ -541,7 +543,7 @@ export class V2CredentialProtocol< } const credentialRecord = new CredentialExchangeRecord({ - connectionId: connection.id, + connectionId: connectionRecord.id, threadId: uuid(), state: CredentialState.RequestSent, autoAcceptCredential, @@ -591,7 +593,7 @@ export class V2CredentialProtocol< connection?.id ) - const formatServices = this.getFormatServicesFromMessage(agentContext, requestMessage.formats) + const formatServices = this.getFormatServicesFromMessage(requestMessage.formats) if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to process request. No supported formats`) } @@ -654,7 +656,7 @@ export class V2CredentialProtocol< public async acceptRequest( agentContext: AgentContext, - { credentialRecord, autoAcceptCredential, comment, credentialFormats }: AcceptRequestOptions + { credentialRecord, autoAcceptCredential, comment, credentialFormats }: AcceptCredentialRequestOptions ) { const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -673,7 +675,7 @@ export class V2CredentialProtocol< messageClass: V2RequestCredentialMessage, }) - formatServices = this.getFormatServicesFromMessage(agentContext, requestMessage.formats) + formatServices = this.getFormatServicesFromMessage(requestMessage.formats) } // If the format services list is still empty, throw an error as we don't support any @@ -740,7 +742,7 @@ export class V2CredentialProtocol< previousSentMessage: requestMessage, }) - const formatServices = this.getFormatServicesFromMessage(agentContext, requestMessage.formats) + const formatServices = this.getFormatServicesFromMessage(credentialMessage.formats) if (formatServices.length === 0) { throw new AriesFrameworkError(`Unable to process credential. No supported formats`) } @@ -837,13 +839,20 @@ export class V2CredentialProtocol< * @returns a {@link V2CredentialProblemReportMessage} * */ - public createProblemReport(agentContext: AgentContext, options: CreateProblemReportOptions): ProblemReportMessage { - return new V2CredentialProblemReportMessage({ + public async createProblemReport( + agentContext: AgentContext, + { credentialRecord, description }: CreateCredentialProblemReportOptions + ): Promise> { + const message = new V2CredentialProblemReportMessage({ description: { - en: options.message, + en: description, code: CredentialProblemReportReason.IssuanceAbandoned, }, }) + + message.setThread({ threadId: credentialRecord.threadId }) + + return { credentialRecord, message } } // AUTO ACCEPT METHODS @@ -872,7 +881,7 @@ export class V2CredentialProtocol< // NOTE: we take the formats from the offerMessage so we always check all services that we last sent // Otherwise we'll only check the formats from the proposal, which could be different from the formats // we use. - const formatServices = this.getFormatServicesFromMessage(agentContext, offerMessage.formats) + const formatServices = this.getFormatServicesFromMessage(offerMessage.formats) for (const formatService of formatServices) { const offerAttachment = this.credentialFormatCoordinator.getAttachmentForService( @@ -887,7 +896,7 @@ export class V2CredentialProtocol< proposalMessage.proposalAttachments ) - const shouldAutoRespondToFormat = formatService.shouldAutoRespondToProposal(agentContext, { + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToProposal(agentContext, { credentialRecord, offerAttachment, proposalAttachment, @@ -926,7 +935,6 @@ export class V2CredentialProtocol< credentialRecord.autoAcceptCredential, credentialsModuleConfig.autoAcceptCredentials ) - // Handle always / never cases if (autoAccept === AutoAcceptCredential.Always) return true if (autoAccept === AutoAcceptCredential.Never) return false @@ -937,7 +945,7 @@ export class V2CredentialProtocol< // NOTE: we take the formats from the proposalMessage so we always check all services that we last sent // Otherwise we'll only check the formats from the offer, which could be different from the formats // we use. - const formatServices = this.getFormatServicesFromMessage(agentContext, proposalMessage.formats) + const formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) for (const formatService of formatServices) { const offerAttachment = this.credentialFormatCoordinator.getAttachmentForService( @@ -952,7 +960,7 @@ export class V2CredentialProtocol< proposalMessage.proposalAttachments ) - const shouldAutoRespondToFormat = formatService.shouldAutoRespondToOffer(agentContext, { + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToOffer(agentContext, { credentialRecord, offerAttachment, proposalAttachment, @@ -1001,7 +1009,7 @@ export class V2CredentialProtocol< // NOTE: we take the formats from the offerMessage so we always check all services that we last sent // Otherwise we'll only check the formats from the request, which could be different from the formats // we use. - const formatServices = this.getFormatServicesFromMessage(agentContext, offerMessage.formats) + const formatServices = this.getFormatServicesFromMessage(offerMessage.formats) for (const formatService of formatServices) { const offerAttachment = this.credentialFormatCoordinator.getAttachmentForService( @@ -1024,7 +1032,7 @@ export class V2CredentialProtocol< requestMessage.requestAttachments ) - const shouldAutoRespondToFormat = formatService.shouldAutoRespondToRequest(agentContext, { + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToRequest(agentContext, { credentialRecord, offerAttachment, requestAttachment, @@ -1066,7 +1074,7 @@ export class V2CredentialProtocol< // NOTE: we take the formats from the requestMessage so we always check all services that we last sent // Otherwise we'll only check the formats from the credential, which could be different from the formats // we use. - const formatServices = this.getFormatServicesFromMessage(agentContext, requestMessage.formats) + const formatServices = this.getFormatServicesFromMessage(requestMessage.formats) for (const formatService of formatServices) { const offerAttachment = offerMessage @@ -1097,7 +1105,7 @@ export class V2CredentialProtocol< credentialMessage.credentialAttachments ) - const shouldAutoRespondToFormat = formatService.shouldAutoRespondToCredential(agentContext, { + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToCredential(agentContext, { credentialRecord, offerAttachment, credentialAttachment, @@ -1150,7 +1158,7 @@ export class V2CredentialProtocol< public async getFormatData( agentContext: AgentContext, credentialExchangeId: string - ): Promise>> { + ): Promise>> { // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. const [proposalMessage, offerMessage, requestMessage, credentialMessage] = await Promise.all([ this.findProposalMessage(agentContext, credentialExchangeId), @@ -1168,7 +1176,7 @@ export class V2CredentialProtocol< credential: [credentialMessage?.formats, credentialMessage?.credentialAttachments], } as const - const formatData: GetFormatDataReturn = { + const formatData: GetCredentialFormatDataReturn = { proposalAttributes: proposalMessage?.credentialPreview?.attributes, offerAttributes: offerMessage?.credentialPreview?.attributes, } @@ -1179,8 +1187,8 @@ export class V2CredentialProtocol< if (!formats || !attachments) continue // Find all format services associated with the message - const formatServices = this.getFormatServicesFromMessage(agentContext, formats) - const messageFormatData: FormatDataMessagePayload = {} + const formatServices = this.getFormatServicesFromMessage(formats) + const messageFormatData: CredentialFormatDataMessagePayload = {} // Loop through all of the format services, for each we will extract the attachment data and assign this to the object // using the unique format key (e.g. indy) @@ -1190,7 +1198,7 @@ export class V2CredentialProtocol< messageFormatData[formatService.formatKey] = attachment.getDataAsJson() } - formatData[messageKey as Exclude] = + formatData[messageKey as Exclude] = messageFormatData } @@ -1202,10 +1210,7 @@ export class V2CredentialProtocol< * @param messageFormats the format objects containing the format name (eg indy) * @return the credential format service objects in an array - derived from format object keys */ - private getFormatServicesFromMessage( - agentContext: AgentContext, - messageFormats: CredentialFormatSpec[] - ): CredentialFormatService[] { + private getFormatServicesFromMessage(messageFormats: CredentialFormatSpec[]): CredentialFormatService[] { const formatServices = new Set() for (const msg of messageFormats) { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts index 5939cb70a5..8cddb48b7d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts @@ -9,7 +9,6 @@ import { Subject } from 'rxjs' import { AriesFrameworkError, CredentialFormatSpec } from '../../../../..' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' -import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' @@ -19,7 +18,6 @@ import { JsonEncoder } from '../../../../../utils/JsonEncoder' import { AckStatus } from '../../../../common/messages/AckMessage' import { DidExchangeState } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' -import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' import { credReq } from '../../../__tests__/fixtures' import { CredentialProblemReportReason } from '../../../errors/CredentialProblemReportReason' @@ -53,16 +51,12 @@ const CredentialRepositoryMock = CredentialRepository as jest.Mock const JsonLdCredentialFormatServiceMock = JsonLdCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock -const DispatcherMock = Dispatcher as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const routingService = new RoutingServiceMock() const indyCredentialFormatService = new IndyCredentialFormatServiceMock() const jsonLdCredentialFormatService = new JsonLdCredentialFormatServiceMock() -const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -83,8 +77,6 @@ const agentContext = getAgentContext({ registerInstances: [ [CredentialRepository, credentialRepository], [DidCommMessageRepository, didCommMessageRepository], - [RoutingService, routingService], - [Dispatcher, dispatcher], [ConnectionService, connectionService], [EventEmitter, eventEmitter], ], @@ -129,7 +121,7 @@ const credentialAttachment = new Attachment({ }) const requestFormat = new CredentialFormatSpec({ - attachId: 'request-attachment-id', + attachmentId: 'request-attachment-id', format: 'hlindy/cred-filter@v2.0', }) @@ -143,17 +135,17 @@ const proposalAttachment = new Attachment({ }) const offerFormat = new CredentialFormatSpec({ - attachId: 'offer-attachment-id', + attachmentId: 'offer-attachment-id', format: 'hlindy/cred-abstract@v2.0', }) const proposalFormat = new CredentialFormatSpec({ - attachId: 'proposal-attachment-id', + attachmentId: 'proposal-attachment-id', format: 'hlindy/cred-abstract@v2.0', }) const credentialFormat = new CredentialFormatSpec({ - attachId: 'credential-attachment-id', + attachmentId: 'credential-attachment-id', format: 'hlindy/cred@v2.0', }) @@ -687,22 +679,25 @@ describe('credentialProtocol', () => { }) describe('createProblemReport', () => { - test('returns problem report message base once get error', () => { + test('returns problem report message base once get error', async () => { // given const credentialRecord = mockCredentialRecord({ state: CredentialState.OfferReceived, threadId: 'somethreadid', connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - const message = 'Indy error' + const description = 'Indy error' mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) // when - const credentialProblemReportMessage = credentialProtocol.createProblemReport(agentContext, { message }) + const { message } = await credentialProtocol.createProblemReport(agentContext, { + description, + credentialRecord, + }) - credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) + message.setThread({ threadId: 'somethreadid' }) // then - expect(credentialProblemReportMessage.toJSON()).toMatchObject({ + expect(message.toJSON()).toMatchObject({ '@id': expect.any(String), '@type': 'https://didcomm.org/issue-credential/2.0/problem-report', '~thread': { @@ -710,21 +705,21 @@ describe('credentialProtocol', () => { }, description: { code: CredentialProblemReportReason.IssuanceAbandoned, - en: message, + en: description, }, }) }) }) describe('processProblemReport', () => { - const credentialProblemReportMessage = new V2CredentialProblemReportMessage({ + const message = new V2CredentialProblemReportMessage({ description: { en: 'Indy error', code: CredentialProblemReportReason.IssuanceAbandoned, }, }) - credentialProblemReportMessage.setThread({ threadId: 'somethreadid' }) - const messageContext = new InboundMessageContext(credentialProblemReportMessage, { + message.setThread({ threadId: 'somethreadid' }) + const messageContext = new InboundMessageContext(message, { connection, agentContext, }) @@ -875,68 +870,4 @@ describe('credentialProtocol', () => { expect(didCommMessageRepository.delete).toHaveBeenCalledTimes(3) }) }) - - describe('declineOffer', () => { - test(`updates state to ${CredentialState.Declined}`, async () => { - const credentialRecord = mockCredentialRecord({ - state: CredentialState.OfferReceived, - }) - - // when - await credentialProtocol.declineOffer(agentContext, credentialRecord) - - // then - - expect(credentialRepository.update).toHaveBeenNthCalledWith( - 1, - agentContext, - expect.objectContaining({ - state: CredentialState.Declined, - }) - ) - }) - - test(`emits stateChange event from ${CredentialState.OfferReceived} to ${CredentialState.Declined}`, async () => { - const credentialRecord = mockCredentialRecord({ - state: CredentialState.OfferReceived, - }) - - const eventListenerMock = jest.fn() - eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) - - // given - mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) - - // when - await credentialProtocol.declineOffer(agentContext, credentialRecord) - - // then - expect(eventListenerMock).toHaveBeenCalledTimes(1) - const [[event]] = eventListenerMock.mock.calls - expect(event).toMatchObject({ - type: 'CredentialStateChanged', - metadata: { - contextCorrelationId: 'mock', - }, - payload: { - previousState: CredentialState.OfferReceived, - credentialRecord: expect.objectContaining({ - state: CredentialState.Declined, - }), - }, - }) - }) - - const validState = CredentialState.OfferReceived - const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) - test(`throws an error when state transition is invalid`, async () => { - await Promise.all( - invalidCredentialStates.map(async (state) => { - await expect( - credentialProtocol.declineOffer(agentContext, mockCredentialRecord({ state })) - ).rejects.toThrowError(`Credential record is in invalid state ${state}. Valid states are: ${validState}.`) - }) - ) - }) - }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts index 7b76103178..5ae8e56632 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts @@ -1,5 +1,5 @@ import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { CreateOfferOptions } from '../../../CredentialProtocolOptions' +import type { CreateCredentialOfferOptions } from '../../CredentialProtocolOptions' import { Subject } from 'rxjs' @@ -78,7 +78,7 @@ const agentContext = getAgentContext({ agentConfig, }) -const connection = getMockConnection({ +const connectionRecord = getMockConnection({ id: '123', state: DidExchangeState.Completed, }) @@ -88,7 +88,7 @@ const credentialPreview = V1CredentialPreview.fromRecord({ age: '99', }) const offerFormat = new CredentialFormatSpec({ - attachId: 'offer-attachment-id', + attachmentId: 'offer-attachment-id', format: 'hlindy/cred-abstract@v2.0', }) @@ -106,7 +106,7 @@ describe('V2CredentialProtocolOffer', () => { beforeEach(async () => { // mock function implementations - mockFunction(connectionService.getById).mockResolvedValue(connection) + mockFunction(connectionService.getById).mockResolvedValue(connectionRecord) mockFunction(indyLedgerService.getCredentialDefinition).mockResolvedValue(credDef) mockFunction(indyLedgerService.getSchema).mockResolvedValue(schema) @@ -120,9 +120,9 @@ describe('V2CredentialProtocolOffer', () => { }) describe('createOffer', () => { - const offerOptions: CreateOfferOptions<[IndyCredentialFormatService]> = { + const offerOptions: CreateCredentialOfferOptions<[IndyCredentialFormatService]> = { comment: 'some comment', - connection, + connectionRecord, credentialFormats: { indy: { attributes: credentialPreview.attributes, @@ -150,7 +150,7 @@ describe('V2CredentialProtocolOffer', () => { id: expect.any(String), createdAt: expect.any(Date), state: CredentialState.OfferSent, - connectionId: connection.id, + connectionId: connectionRecord.id, }) ) }) @@ -224,7 +224,10 @@ describe('V2CredentialProtocolOffer', () => { offerAttachments: [offerAttachment], }) - const messageContext = new InboundMessageContext(credentialOfferMessage, { agentContext, connection }) + const messageContext = new InboundMessageContext(credentialOfferMessage, { + agentContext, + connection: connectionRecord, + }) test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) @@ -241,7 +244,7 @@ describe('V2CredentialProtocolOffer', () => { id: expect.any(String), createdAt: expect.any(Date), threadId: credentialOfferMessage.id, - connectionId: connection.id, + connectionId: connectionRecord.id, state: CredentialState.OfferReceived, }) ) diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index f23f53d2c0..8320314f0a 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -32,8 +32,7 @@ export class V2OfferCredentialHandler implements MessageHandler { private async acceptOffer( credentialRecord: CredentialExchangeRecord, - messageContext: MessageHandlerInboundMessage, - offerMessage?: V2OfferCredentialMessage + messageContext: MessageHandlerInboundMessage ) { messageContext.agentContext.config.logger.info(`Automatically sending request with autoAccept`) @@ -46,7 +45,7 @@ export class V2OfferCredentialHandler implements MessageHandler { connection: messageContext.connection, associatedRecord: credentialRecord, }) - } else if (offerMessage?.service) { + } else if (messageContext.message?.service) { const routingService = messageContext.agentContext.dependencyManager.resolve(RoutingService) const routing = await routingService.getRouting(messageContext.agentContext) const ourService = new ServiceDecorator({ @@ -54,7 +53,7 @@ export class V2OfferCredentialHandler implements MessageHandler { recipientKeys: [routing.recipientKey.publicKeyBase58], routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) - const recipientService = offerMessage.service + const recipientService = messageContext.message.service const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { credentialRecord, diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts index 95bbc29663..d375e09748 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage.ts @@ -6,7 +6,7 @@ import { Attachment } from '../../../../../decorators/attachment/Attachment' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { CredentialFormatSpec } from '../../../models' -export interface V2IssueCredentialMessageProps { +export interface V2IssueCredentialMessageOptions { id?: string comment?: string formats: CredentialFormatSpec[] @@ -14,7 +14,7 @@ export interface V2IssueCredentialMessageProps { } export class V2IssueCredentialMessage extends AgentMessage { - public constructor(options: V2IssueCredentialMessageProps) { + public constructor(options: V2IssueCredentialMessageOptions) { super() if (options) { @@ -48,6 +48,6 @@ export class V2IssueCredentialMessage extends AgentMessage { public credentialAttachments!: Attachment[] public getCredentialAttachmentById(id: string): Attachment | undefined { - return this.credentialAttachments.find((attachment) => attachment.id == id) + return this.credentialAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts index d0ebf68614..85c4052c54 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage.ts @@ -64,6 +64,6 @@ export class V2OfferCredentialMessage extends AgentMessage { public replacementId?: string public getOfferAttachmentById(id: string): Attachment | undefined { - return this.offerAttachments.find((attachment) => attachment.id == id) + return this.offerAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts index b4cbbc5b01..cc7873d505 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts @@ -8,7 +8,7 @@ import { CredentialFormatSpec } from '../../../models' import { V2CredentialPreview } from './V2CredentialPreview' -export interface V2ProposeCredentialMessageProps { +export interface V2ProposeCredentialMessageOptions { id?: string formats: CredentialFormatSpec[] proposalAttachments: Attachment[] @@ -18,21 +18,22 @@ export interface V2ProposeCredentialMessageProps { } export class V2ProposeCredentialMessage extends AgentMessage { - public constructor(props: V2ProposeCredentialMessageProps) { + public constructor(options: V2ProposeCredentialMessageOptions) { super() - if (props) { - this.id = props.id ?? this.generateId() - this.comment = props.comment - this.credentialPreview = props.credentialPreview - this.formats = props.formats - this.proposalAttachments = props.proposalAttachments - this.appendedAttachments = props.attachments + if (options) { + this.id = options.id ?? this.generateId() + this.comment = options.comment + this.credentialPreview = options.credentialPreview + this.formats = options.formats + this.proposalAttachments = options.proposalAttachments + this.appendedAttachments = options.attachments } } @Type(() => CredentialFormatSpec) - @ValidateNested() + @ValidateNested({ each: true }) @IsArray() + @IsInstance(CredentialFormatSpec, { each: true }) public formats!: CredentialFormatSpec[] @IsValidMessageType(V2ProposeCredentialMessage.type) @@ -64,6 +65,6 @@ export class V2ProposeCredentialMessage extends AgentMessage { public comment?: string public getProposalAttachmentById(id: string): Attachment | undefined { - return this.proposalAttachments.find((attachment) => attachment.id == id) + return this.proposalAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts index a8881c5222..ed7e08f228 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2RequestCredentialMessage.ts @@ -52,6 +52,6 @@ export class V2RequestCredentialMessage extends AgentMessage { public comment?: string public getRequestAttachmentById(id: string): Attachment | undefined { - return this.requestAttachments.find((attachment) => attachment.id == id) + return this.requestAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/credentials/util/composeAutoAccept.ts b/packages/core/src/modules/credentials/util/composeAutoAccept.ts index 55b3e70362..ace6fdf80c 100644 --- a/packages/core/src/modules/credentials/util/composeAutoAccept.ts +++ b/packages/core/src/modules/credentials/util/composeAutoAccept.ts @@ -6,7 +6,6 @@ import { AutoAcceptCredential } from '../models/CredentialAutoAcceptType' * - Otherwise the agent config * - Otherwise {@link AutoAcceptCredential.Never} is returned */ - export function composeAutoAccept( recordConfig: AutoAcceptCredential | undefined, agentConfig: AutoAcceptCredential | undefined diff --git a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts b/packages/core/src/modules/proofs/ProofResponseCoordinator.ts deleted file mode 100644 index db625f71e0..0000000000 --- a/packages/core/src/modules/proofs/ProofResponseCoordinator.ts +++ /dev/null @@ -1,93 +0,0 @@ -import type { ProofExchangeRecord } from './repository' -import type { AgentContext } from '../../agent/context/AgentContext' - -import { injectable } from '../../plugins' - -import { ProofService } from './ProofService' -import { AutoAcceptProof } from './models/ProofAutoAcceptType' - -/** - * This class handles all the automation with all the messages in the present proof protocol - * Every function returns `true` if it should automate the flow and `false` if not - */ -@injectable() -export class ProofResponseCoordinator { - private proofService: ProofService - - public constructor(proofService: ProofService) { - this.proofService = proofService - } - - /** - * Returns the proof auto accept config based on priority: - * - The record config takes first priority - * - Otherwise the agent config - * - Otherwise {@link AutoAcceptProof.Never} is returned - */ - private static composeAutoAccept( - recordConfig: AutoAcceptProof | undefined, - agentConfig: AutoAcceptProof | undefined - ) { - return recordConfig ?? agentConfig ?? AutoAcceptProof.Never - } - - /** - * Checks whether it should automatically respond to a proposal - */ - public async shouldAutoRespondToProposal(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { - const autoAccept = ProofResponseCoordinator.composeAutoAccept( - proofRecord.autoAcceptProof, - agentContext.config.autoAcceptProofs - ) - - if (autoAccept === AutoAcceptProof.Always) { - return true - } - - if (autoAccept === AutoAcceptProof.ContentApproved) { - return this.proofService.shouldAutoRespondToProposal(agentContext, proofRecord) - } - - return false - } - - /** - * Checks whether it should automatically respond to a request - */ - public async shouldAutoRespondToRequest(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { - const autoAccept = ProofResponseCoordinator.composeAutoAccept( - proofRecord.autoAcceptProof, - agentContext.config.autoAcceptProofs - ) - - if (autoAccept === AutoAcceptProof.Always) { - return true - } - - if (autoAccept === AutoAcceptProof.ContentApproved) { - return this.proofService.shouldAutoRespondToRequest(agentContext, proofRecord) - } - - return false - } - - /** - * Checks whether it should automatically respond to a presentation of proof - */ - public async shouldAutoRespondToPresentation(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { - const autoAccept = ProofResponseCoordinator.composeAutoAccept( - proofRecord.autoAcceptProof, - agentContext.config.autoAcceptProofs - ) - - if (autoAccept === AutoAcceptProof.Always) { - return true - } - - if (autoAccept === AutoAcceptProof.ContentApproved) { - return this.proofService.shouldAutoRespondToPresentation(agentContext, proofRecord) - } - - return false - } -} diff --git a/packages/core/src/modules/proofs/ProofService.ts b/packages/core/src/modules/proofs/ProofService.ts deleted file mode 100644 index 2fcbe509b1..0000000000 --- a/packages/core/src/modules/proofs/ProofService.ts +++ /dev/null @@ -1,261 +0,0 @@ -import type { ProofStateChangedEvent } from './ProofEvents' -import type { ProofResponseCoordinator } from './ProofResponseCoordinator' -import type { ProofFormat } from './formats/ProofFormat' -import type { CreateProblemReportOptions } from './formats/models/ProofFormatServiceOptions' -import type { - CreateAckOptions, - CreatePresentationOptions, - CreateProofRequestFromProposalOptions, - CreateProposalAsResponseOptions, - CreateProposalOptions, - CreateRequestAsResponseOptions, - CreateRequestOptions, - DeleteProofOptions, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, - GetFormatDataReturn, - GetRequestedCredentialsForProofRequestOptions, - ProofRequestFromProposalOptions, -} from './models/ProofServiceOptions' -import type { ProofState } from './models/ProofState' -import type { ProofExchangeRecord, ProofRepository } from './repository' -import type { AgentConfig } from '../../agent/AgentConfig' -import type { AgentMessage } from '../../agent/AgentMessage' -import type { Dispatcher } from '../../agent/Dispatcher' -import type { EventEmitter } from '../../agent/EventEmitter' -import type { AgentContext } from '../../agent/context/AgentContext' -import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' -import type { Logger } from '../../logger' -import type { DidCommMessageRepository, DidCommMessageRole } from '../../storage' -import type { Wallet } from '../../wallet/Wallet' -import type { ConnectionService } from '../connections/services' -import type { MediationRecipientService, RoutingService } from '../routing' - -import { JsonTransformer } from '../../utils/JsonTransformer' - -import { ProofEventTypes } from './ProofEvents' - -export abstract class ProofService { - protected proofRepository: ProofRepository - protected didCommMessageRepository: DidCommMessageRepository - protected eventEmitter: EventEmitter - protected connectionService: ConnectionService - protected wallet: Wallet - protected logger: Logger - - public constructor( - agentConfig: AgentConfig, - proofRepository: ProofRepository, - connectionService: ConnectionService, - didCommMessageRepository: DidCommMessageRepository, - wallet: Wallet, - eventEmitter: EventEmitter - ) { - this.proofRepository = proofRepository - this.connectionService = connectionService - this.didCommMessageRepository = didCommMessageRepository - this.eventEmitter = eventEmitter - this.wallet = wallet - this.logger = agentConfig.logger - } - public abstract readonly version: string - - public emitStateChangedEvent( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord, - previousState: ProofState | null - ) { - const clonedProof = JsonTransformer.clone(proofRecord) - - this.eventEmitter.emit(agentContext, { - type: ProofEventTypes.ProofStateChanged, - payload: { - proofRecord: clonedProof, - previousState: previousState, - }, - }) - } - - /** - * Update the record to a new state and emit an state changed event. Also updates the record - * in storage. - * - * @param proofRecord The proof record to update the state for - * @param newState The state to update to - * - */ - public async updateState(agentContext: AgentContext, proofRecord: ProofExchangeRecord, newState: ProofState) { - const previousState = proofRecord.state - proofRecord.state = newState - await this.proofRepository.update(agentContext, proofRecord) - - this.emitStateChangedEvent(agentContext, proofRecord, previousState) - } - - public update(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { - return this.proofRepository.update(agentContext, proofRecord) - } - - /** - * 1. Assert (connection ready, record state) - * 2. Create proposal message - * 3. loop through all formats from ProposeProofOptions and call format service - * 4. Create and store proof record - * 5. Store proposal message - * 6. Return proposal message + proof record - */ - public abstract createProposal( - agentContext: AgentContext, - options: CreateProposalOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - /** - * Create a proposal message in response to a received proof request message - * - * 1. assert record state - * 2. Create proposal message - * 3. loop through all formats from ProposeProofOptions and call format service - * 4. Update proof record - * 5. Create or update proposal message - * 6. Return proposal message + proof record - */ - public abstract createProposalAsResponse( - agentContext: AgentContext, - options: CreateProposalAsResponseOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - /** - * Process a received proposal message (does not accept yet) - * - * 1. Find proof record by thread and connection id - * - * Two flows possible: - * - Proof record already exist - * 2. Assert state - * 3. Save or update proposal message in storage (didcomm message record) - * 4. Loop through all format services to process proposal message - * 5. Update & return record - * - * - Proof record does not exist yet - * 2. Create record - * 3. Save proposal message - * 4. Loop through all format services to process proposal message - * 5. Save & return record - */ - public abstract processProposal(messageContext: InboundMessageContext): Promise - - public abstract createRequest( - agentContext: AgentContext, - options: CreateRequestOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - public abstract createRequestAsResponse( - agentContext: AgentContext, - options: CreateRequestAsResponseOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - public abstract processRequest(messageContext: InboundMessageContext): Promise - - public abstract createPresentation( - agentContext: AgentContext, - options: CreatePresentationOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - public abstract processPresentation(messageContext: InboundMessageContext): Promise - - public abstract createAck( - agentContext: AgentContext, - options: CreateAckOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - public abstract processAck(messageContext: InboundMessageContext): Promise - - public abstract createProblemReport( - agentContext: AgentContext, - options: CreateProblemReportOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> - - public abstract processProblemReport( - messageContext: InboundMessageContext - ): Promise - - public abstract shouldAutoRespondToProposal( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise - - public abstract shouldAutoRespondToRequest( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise - - public abstract shouldAutoRespondToPresentation( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise - - public abstract registerMessageHandlers( - dispatcher: Dispatcher, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - mediationRecipientService: MediationRecipientService, - routingService: RoutingService - ): void - - public abstract findProposalMessage(agentContext: AgentContext, proofRecordId: string): Promise - public abstract findRequestMessage(agentContext: AgentContext, proofRecordId: string): Promise - public abstract findPresentationMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise - - public async saveOrUpdatePresentationMessage( - agentContext: AgentContext, - options: { - proofRecord: ProofExchangeRecord - message: AgentMessage - role: DidCommMessageRole - } - ): Promise { - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - agentMessage: options.message, - role: options.role, - }) - } - - public async delete( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord, - options?: DeleteProofOptions - ): Promise { - await this.proofRepository.delete(agentContext, proofRecord) - - const deleteAssociatedDidCommMessages = options?.deleteAssociatedDidCommMessages ?? true - - if (deleteAssociatedDidCommMessages) { - const didCommMessages = await this.didCommMessageRepository.findByQuery(agentContext, { - associatedRecordId: proofRecord.id, - }) - for (const didCommMessage of didCommMessages) { - await this.didCommMessageRepository.delete(agentContext, didCommMessage) - } - } - } - - public abstract getRequestedCredentialsForProofRequest( - agentContext: AgentContext, - options: GetRequestedCredentialsForProofRequestOptions - ): Promise> - - public abstract autoSelectCredentialsForProofRequest( - options: FormatRetrievedCredentialOptions - ): Promise> - - public abstract createProofRequestFromProposal( - agentContext: AgentContext, - options: CreateProofRequestFromProposalOptions - ): Promise> - - public abstract getFormatData(agentContext: AgentContext, proofRecordId: string): Promise> -} diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 38f5e642bd..52b45d5733 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -1,93 +1,77 @@ -import type { ProofService } from './ProofService' import type { - AcceptProofPresentationOptions, + AcceptProofOptions, AcceptProofProposalOptions, + AcceptProofRequestOptions, CreateProofRequestOptions, + DeleteProofOptions, FindProofPresentationMessageReturn, FindProofProposalMessageReturn, FindProofRequestMessageReturn, + GetCredentialsForProofRequestOptions, + GetCredentialsForProofRequestReturn, + GetProofFormatDataReturn, + NegotiateProofProposalOptions, + NegotiateProofRequestOptions, ProposeProofOptions, RequestProofOptions, - ProofServiceMap, - NegotiateRequestOptions, - NegotiateProposalOptions, + SelectCredentialsForProofRequestOptions, + SelectCredentialsForProofRequestReturn, + SendProofProblemReportOptions, } from './ProofsApiOptions' -import type { ProofFormat } from './formats/ProofFormat' -import type { IndyProofFormat } from './formats/indy/IndyProofFormat' -import type { - AutoSelectCredentialsForProofRequestOptions, - GetRequestedCredentialsForProofRequest, -} from './models/ModuleOptions' -import type { - CreatePresentationOptions, - CreateProposalOptions, - CreateRequestOptions, - CreateRequestAsResponseOptions, - CreateProofRequestFromProposalOptions, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, - DeleteProofOptions, - GetFormatDataReturn, - CreateProposalAsResponseOptions, -} from './models/ProofServiceOptions' +import type { ProofProtocol } from './protocol/ProofProtocol' +import type { ProofFormatsFromProtocols } from './protocol/ProofProtocolOptions' import type { ProofExchangeRecord } from './repository/ProofExchangeRecord' import type { AgentMessage } from '../../agent/AgentMessage' import type { Query } from '../../storage/StorageService' -import { inject, injectable } from 'tsyringe' +import { injectable } from 'tsyringe' -import { AgentConfig } from '../../agent/AgentConfig' -import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { AgentContext } from '../../agent/context/AgentContext' import { OutboundMessageContext } from '../../agent/models' -import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' -import { Logger } from '../../logger' +import { DidCommMessageRepository } from '../../storage' import { DidCommMessageRole } from '../../storage/didcomm/DidCommMessageRole' import { ConnectionService } from '../connections/services/ConnectionService' -import { MediationRecipientService } from '../routing/services/MediationRecipientService' import { RoutingService } from '../routing/services/RoutingService' -import { ProofResponseCoordinator } from './ProofResponseCoordinator' +import { ProofsModuleConfig } from './ProofsModuleConfig' import { ProofState } from './models/ProofState' -import { V1ProofService } from './protocol/v1/V1ProofService' -import { V2ProofService } from './protocol/v2/V2ProofService' import { ProofRepository } from './repository/ProofRepository' -export interface ProofsApi[]> { +export interface ProofsApi { // Proposal methods - proposeProof(options: ProposeProofOptions): Promise - acceptProposal(options: AcceptProofProposalOptions): Promise - negotiateProposal(options: NegotiateProposalOptions): Promise + proposeProof(options: ProposeProofOptions): Promise + acceptProposal(options: AcceptProofProposalOptions): Promise + negotiateProposal(options: NegotiateProofProposalOptions): Promise // Request methods - requestProof(options: RequestProofOptions): Promise - acceptRequest(options: AcceptProofPresentationOptions): Promise + requestProof(options: RequestProofOptions): Promise + acceptRequest(options: AcceptProofRequestOptions): Promise declineRequest(proofRecordId: string): Promise - negotiateRequest(options: NegotiateRequestOptions): Promise + negotiateRequest(options: NegotiateProofRequestOptions): Promise // Present - acceptPresentation(proofRecordId: string): Promise + acceptPresentation(options: AcceptProofOptions): Promise // out of band - createRequest(options: CreateProofRequestOptions): Promise<{ + createRequest(options: CreateProofRequestOptions): Promise<{ message: AgentMessage proofRecord: ProofExchangeRecord }> // Auto Select - autoSelectCredentialsForProofRequest( - options: AutoSelectCredentialsForProofRequestOptions - ): Promise> + selectCredentialsForRequest( + options: SelectCredentialsForProofRequestOptions + ): Promise> - // Get Requested Credentials - getRequestedCredentialsForProofRequest( - options: AutoSelectCredentialsForProofRequestOptions - ): Promise> + // Get credentials for request + getCredentialsForRequest( + options: GetCredentialsForProofRequestOptions + ): Promise> - sendProblemReport(proofRecordId: string, message: string): Promise + sendProblemReport(options: SendProofProblemReportOptions): Promise // Record Methods getAll(): Promise @@ -96,107 +80,87 @@ export interface ProofsApi deleteById(proofId: string, options?: DeleteProofOptions): Promise update(proofRecord: ProofExchangeRecord): Promise - getFormatData(proofRecordId: string): Promise> + getFormatData(proofRecordId: string): Promise>> // DidComm Message Records - findProposalMessage(proofRecordId: string): Promise> - findRequestMessage(proofRecordId: string): Promise> - findPresentationMessage(proofRecordId: string): Promise> + findProposalMessage(proofRecordId: string): Promise> + findRequestMessage(proofRecordId: string): Promise> + findPresentationMessage(proofRecordId: string): Promise> } @injectable() -export class ProofsApi< - PFs extends ProofFormat[] = [IndyProofFormat], - PSs extends ProofService[] = [V1ProofService, V2ProofService] -> implements ProofsApi -{ +export class ProofsApi implements ProofsApi { + /** + * Configuration for the proofs module + */ + public readonly config: ProofsModuleConfig + private connectionService: ConnectionService private messageSender: MessageSender private routingService: RoutingService private proofRepository: ProofRepository + private didCommMessageRepository: DidCommMessageRepository private agentContext: AgentContext - private agentConfig: AgentConfig - private logger: Logger - private serviceMap: ProofServiceMap public constructor( - dispatcher: Dispatcher, - mediationRecipientService: MediationRecipientService, messageSender: MessageSender, connectionService: ConnectionService, agentContext: AgentContext, - agentConfig: AgentConfig, - routingService: RoutingService, - @inject(InjectionSymbols.Logger) logger: Logger, proofRepository: ProofRepository, - v1Service: V1ProofService, - v2Service: V2ProofService + routingService: RoutingService, + didCommMessageRepository: DidCommMessageRepository, + config: ProofsModuleConfig ) { this.messageSender = messageSender this.connectionService = connectionService this.proofRepository = proofRepository this.agentContext = agentContext - this.agentConfig = agentConfig this.routingService = routingService - this.logger = logger - // Dynamically build service map. This will be extracted once services are registered dynamically - this.serviceMap = [v1Service, v2Service].reduce( - (serviceMap, service) => ({ - ...serviceMap, - [service.version]: service, - }), - {} - ) as ProofServiceMap - - this.logger.debug(`Initializing Proofs Module for agent ${this.agentContext.config.label}`) - - this.registerMessageHandlers(dispatcher, mediationRecipientService) + this.didCommMessageRepository = didCommMessageRepository + this.config = config } - public getService(protocolVersion: PVT): ProofService { - if (!this.serviceMap[protocolVersion]) { - throw new AriesFrameworkError(`No proof service registered for protocol version ${protocolVersion}`) + private getProtocol(protocolVersion: PVT): ProofProtocol { + const proofProtocol = this.config.proofProtocols.find((protocol) => protocol.version === protocolVersion) + + if (!proofProtocol) { + throw new AriesFrameworkError(`No proof protocol registered for protocol version ${protocolVersion}`) } - return this.serviceMap[protocolVersion] as ProofService + return proofProtocol } /** * Initiate a new presentation exchange as prover by sending a presentation proposal message * to the connection with the specified connection id. * - * @param options multiple properties like protocol version, connection id, proof format (indy/ presentation exchange) - * to include in the message - * @returns Proof record associated with the sent proposal message + * @param options configuration to use for the proposal + * @returns Proof exchange record associated with the sent proposal message */ - public async proposeProof(options: ProposeProofOptions): Promise { - const service = this.getService(options.protocolVersion) + public async proposeProof(options: ProposeProofOptions): Promise { + const protocol = this.getProtocol(options.protocolVersion) - const { connectionId } = options - - const connection = await this.connectionService.getById(this.agentContext, connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() - const proposalOptions: CreateProposalOptions = { - connectionRecord: connection, + const { message, proofRecord } = await protocol.createProposal(this.agentContext, { + connectionRecord, proofFormats: options.proofFormats, autoAcceptProof: options.autoAcceptProof, goalCode: options.goalCode, comment: options.comment, parentThreadId: options.parentThreadId, - } - - const { message, proofRecord } = await service.createProposal(this.agentContext, proposalOptions) + }) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) - await this.messageSender.sendMessage(outboundMessageContext) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -204,53 +168,42 @@ export class ProofsApi< * Accept a presentation proposal as verifier (by sending a presentation request message) to the connection * associated with the proof record. * - * @param options multiple properties like proof record id, additional configuration for creating the request - * @returns Proof record associated with the presentation request + * @param options config object for accepting the proposal + * @returns Proof exchange record associated with the presentation request */ - public async acceptProposal(options: AcceptProofProposalOptions): Promise { - const { proofRecordId } = options - - const proofRecord = await this.getById(proofRecordId) - - const service = this.getService(proofRecord.protocolVersion) + public async acceptProposal(options: AcceptProofProposalOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) if (!proofRecord.connectionId) { throw new AriesFrameworkError( - `No connectionId found for proof record '${proofRecord.id}'. Connection-less issuance does not support presentation proposal or negotiation.` + `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support presentation proposal or negotiation.` ) } - const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + // with version we can get the protocol + const protocol = this.getProtocol(proofRecord.protocolVersion) + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() - const proofRequestFromProposalOptions: CreateProofRequestFromProposalOptions = { + const { message } = await protocol.acceptProposal(this.agentContext, { proofRecord, - } - - const proofRequest = await service.createProofRequestFromProposal( - this.agentContext, - proofRequestFromProposalOptions - ) - - const requestOptions: CreateRequestAsResponseOptions = { - proofRecord: proofRecord, - proofFormats: proofRequest.proofFormats, + proofFormats: options.proofFormats, goalCode: options.goalCode, - willConfirm: options.willConfirm ?? true, + willConfirm: options.willConfirm, comment: options.comment, - } - - const { message } = await service.createRequestAsResponse(this.agentContext, requestOptions) + autoAcceptProof: options.autoAcceptProof, + }) + // send the message const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) - await this.messageSender.sendMessage(outboundMessageContext) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -262,35 +215,33 @@ export class ProofsApi< * specifying which credentials to use for the proof * @returns Proof record associated with the sent request message */ - public async negotiateProposal(options: NegotiateProposalOptions): Promise { - const { proofRecordId } = options - - const proofRecord = await this.getById(proofRecordId) - - const service = this.getService(proofRecord.protocolVersion) + public async negotiateProposal(options: NegotiateProofProposalOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) if (!proofRecord.connectionId) { throw new AriesFrameworkError( - `No connectionId found for proof record '${proofRecord.id}'. Connection-less issuance does not support negotiation.` + `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support negotiation.` ) } - const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + const protocol = this.getProtocol(proofRecord.protocolVersion) + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() - const requestOptions: CreateRequestAsResponseOptions = { + const { message } = await protocol.negotiateProposal(this.agentContext, { proofRecord, proofFormats: options.proofFormats, autoAcceptProof: options.autoAcceptProof, comment: options.comment, - } - const { message } = await service.createRequestAsResponse(this.agentContext, requestOptions) + goalCode: options.goalCode, + willConfirm: options.willConfirm, + }) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -305,30 +256,30 @@ export class ProofsApi< * @param options multiple properties like connection id, protocol version, proof Formats to build the proof request * @returns Proof record associated with the sent request message */ - public async requestProof(options: RequestProofOptions): Promise { - const service = this.getService(options.protocolVersion) - - const connection = await this.connectionService.getById(this.agentContext, options.connectionId) + public async requestProof(options: RequestProofOptions): Promise { + const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) + const protocol = this.getProtocol(options.protocolVersion) // Assert - connection.assertReady() + connectionRecord.assertReady() - const createProofRequest: CreateRequestOptions = { - connectionRecord: connection, + const { message, proofRecord } = await protocol.createRequest(this.agentContext, { + connectionRecord, proofFormats: options.proofFormats, autoAcceptProof: options.autoAcceptProof, parentThreadId: options.parentThreadId, comment: options.comment, - } - const { message, proofRecord } = await service.createRequest(this.agentContext, createProofRequest) + goalCode: options.goalCode, + willConfirm: options.willConfirm, + }) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) - await this.messageSender.sendMessage(outboundMessageContext) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -340,32 +291,31 @@ export class ProofsApi< * specifying which credentials to use for the proof * @returns Proof record associated with the sent presentation message */ - public async acceptRequest(options: AcceptProofPresentationOptions): Promise { - const { proofRecordId, proofFormats, comment } = options - - const record = await this.getById(proofRecordId) - - const service = this.getService(record.protocolVersion) + public async acceptRequest(options: AcceptProofRequestOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) - const presentationOptions: CreatePresentationOptions = { - proofFormats, - proofRecord: record, - comment, - } - const { message, proofRecord } = await service.createPresentation(this.agentContext, presentationOptions) + const protocol = this.getProtocol(proofRecord.protocolVersion) - const requestMessage = await service.findRequestMessage(this.agentContext, proofRecord.id) + const requestMessage = await protocol.findRequestMessage(this.agentContext, proofRecord.id) // Use connection if present if (proofRecord.connectionId) { - const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() + + const { message } = await protocol.acceptRequest(this.agentContext, { + proofFormats: options.proofFormats, + proofRecord, + comment: options.comment, + autoAcceptProof: options.autoAcceptProof, + goalCode: options.goalCode, + }) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) await this.messageSender.sendMessage(outboundMessageContext) @@ -377,20 +327,27 @@ export class ProofsApi< else if (requestMessage?.service) { // Create ~service decorator const routing = await this.routingService.getRouting(this.agentContext) - message.service = new ServiceDecorator({ + const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) - const recipientService = requestMessage.service - // Set and save ~service decorator to record (to remember our verkey) + const { message } = await protocol.acceptRequest(this.agentContext, { + proofFormats: options.proofFormats, + proofRecord, + comment: options.comment, + autoAcceptProof: options.autoAcceptProof, + goalCode: options.goalCode, + }) - await service.saveOrUpdatePresentationMessage(this.agentContext, { - proofRecord: proofRecord, - message: message, + // Set and save ~service decorator to record (to remember our verkey) + message.service = ourService + await this.didCommMessageRepository.saveOrUpdateAgentMessage(this.agentContext, { + agentMessage: message, role: DidCommMessageRole.Sender, + associatedRecordId: proofRecord.id, }) await this.messageSender.sendMessageToService( @@ -398,7 +355,7 @@ export class ProofsApi< agentContext: this.agentContext, serviceParams: { service: recipientService.resolvedDidCommService, - senderKey: message.service.resolvedDidCommService.recipientKeys[0], + senderKey: ourService.resolvedDidCommService.recipientKeys[0], returnRoute: true, }, }) @@ -414,36 +371,12 @@ export class ProofsApi< } } - /** - * Initiate a new presentation exchange as verifier by sending an out of band presentation - * request message - * - * @param options multiple properties like protocol version, proof Formats to build the proof request - * @returns the message itself and the proof record associated with the sent request message - */ - public async createRequest(options: CreateProofRequestOptions): Promise<{ - message: AgentMessage - proofRecord: ProofExchangeRecord - }> { - const service = this.getService(options.protocolVersion) - - const createProofRequest: CreateRequestOptions = { - proofFormats: options.proofFormats, - autoAcceptProof: options.autoAcceptProof, - comment: options.comment, - parentThreadId: options.parentThreadId, - } - - return await service.createRequest(this.agentContext, createProofRequest) - } - public async declineRequest(proofRecordId: string): Promise { const proofRecord = await this.getById(proofRecordId) - const service = this.getService(proofRecord.protocolVersion) - proofRecord.assertState(ProofState.RequestReceived) - await service.updateState(this.agentContext, proofRecord, ProofState.Declined) + const protocol = this.getProtocol(proofRecord.protocolVersion) + await protocol.updateState(this.agentContext, proofRecord, ProofState.Declined) return proofRecord } @@ -456,43 +389,62 @@ export class ProofsApi< * to include in the message * @returns Proof record associated with the sent proposal message */ - public async negotiateRequest(options: NegotiateRequestOptions): Promise { - const { proofRecordId } = options - const proofRecord = await this.getById(proofRecordId) - - const service = this.getService(proofRecord.protocolVersion) + public async negotiateRequest(options: NegotiateProofRequestOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) if (!proofRecord.connectionId) { throw new AriesFrameworkError( - `No connectionId found for proof record '${proofRecord.id}'. Connection-less issuance does not support presentation proposal or negotiation.` + `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support presentation proposal or negotiation.` ) } - const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() - const proposalOptions: CreateProposalAsResponseOptions = { + const protocol = this.getProtocol(proofRecord.protocolVersion) + const { message } = await protocol.negotiateRequest(this.agentContext, { proofRecord, proofFormats: options.proofFormats, autoAcceptProof: options.autoAcceptProof, goalCode: options.goalCode, comment: options.comment, - } - - const { message } = await service.createProposalAsResponse(this.agentContext, proposalOptions) + }) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) - await this.messageSender.sendMessage(outboundMessageContext) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } + /** + * Initiate a new presentation exchange as verifier by sending an out of band presentation + * request message + * + * @param options multiple properties like protocol version, proof Formats to build the proof request + * @returns the message itself and the proof record associated with the sent request message + */ + public async createRequest(options: CreateProofRequestOptions): Promise<{ + message: AgentMessage + proofRecord: ProofExchangeRecord + }> { + const protocol = this.getProtocol(options.protocolVersion) + + return await protocol.createRequest(this.agentContext, { + proofFormats: options.proofFormats, + autoAcceptProof: options.autoAcceptProof, + comment: options.comment, + parentThreadId: options.parentThreadId, + goalCode: options.goalCode, + willConfirm: options.willConfirm, + }) + } + /** * Accept a presentation as prover (by sending a presentation acknowledgement message) to the connection * associated with the proof record. @@ -501,37 +453,42 @@ export class ProofsApi< * @returns Proof record associated with the sent presentation acknowledgement message * */ - public async acceptPresentation(proofRecordId: string): Promise { - const record = await this.getById(proofRecordId) - const service = this.getService(record.protocolVersion) - - const { message, proofRecord } = await service.createAck(this.agentContext, { - proofRecord: record, - }) - - const requestMessage = await service.findRequestMessage(this.agentContext, record.id) + public async acceptPresentation(options: AcceptProofOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) + const protocol = this.getProtocol(proofRecord.protocolVersion) - const presentationMessage = await service.findPresentationMessage(this.agentContext, record.id) + const requestMessage = await protocol.findRequestMessage(this.agentContext, proofRecord.id) + const presentationMessage = await protocol.findPresentationMessage(this.agentContext, proofRecord.id) // Use connection if present if (proofRecord.connectionId) { - const connection = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() + + const { message } = await protocol.acceptPresentation(this.agentContext, { + proofRecord, + }) const outboundMessageContext = new OutboundMessageContext(message, { agentContext: this.agentContext, - connection, + connection: connectionRecord, associatedRecord: proofRecord, }) await this.messageSender.sendMessage(outboundMessageContext) + + return proofRecord } // Use ~service decorator otherwise else if (requestMessage?.service && presentationMessage?.service) { - const recipientService = presentationMessage?.service + const recipientService = presentationMessage.service const ourService = requestMessage.service + const { message } = await protocol.acceptPresentation(this.agentContext, { + proofRecord, + }) + await this.messageSender.sendMessageToService( new OutboundMessageContext(message, { agentContext: this.agentContext, @@ -542,6 +499,8 @@ export class ProofsApi< }, }) ) + + return proofRecord } // Cannot send message without credentialId or ~service decorator else { @@ -549,8 +508,6 @@ export class ProofsApi< `Cannot accept presentation without connectionId or ~service decorator on presentation message.` ) } - - return record } /** @@ -561,39 +518,34 @@ export class ProofsApi< * @param options multiple properties like proof record id and optional configuration * @returns RequestedCredentials */ - public async autoSelectCredentialsForProofRequest( - options: AutoSelectCredentialsForProofRequestOptions - ): Promise> { + public async selectCredentialsForRequest( + options: SelectCredentialsForProofRequestOptions + ): Promise> { const proofRecord = await this.getById(options.proofRecordId) - const service = this.getService(proofRecord.protocolVersion) + const protocol = this.getProtocol(proofRecord.protocolVersion) - const retrievedCredentials: FormatRetrievedCredentialOptions = - await service.getRequestedCredentialsForProofRequest(this.agentContext, { - proofRecord: proofRecord, - config: options.config, - }) - return await service.autoSelectCredentialsForProofRequest(retrievedCredentials) + return protocol.selectCredentialsForRequest(this.agentContext, { + proofFormats: options.proofFormats, + proofRecord, + }) } /** - * Create a {@link RetrievedCredentials} object. Given input proof request and presentation proposal, - * use credentials in the wallet to build indy requested credentials object for input to proof creation. - * - * If restrictions allow, self attested attributes will be used. + * Get credentials in the wallet for a received proof request. * * @param options multiple properties like proof record id and optional configuration - * @returns RetrievedCredentials object */ - public async getRequestedCredentialsForProofRequest( - options: GetRequestedCredentialsForProofRequest - ): Promise> { - const record = await this.getById(options.proofRecordId) - const service = this.getService(record.protocolVersion) - - return await service.getRequestedCredentialsForProofRequest(this.agentContext, { - proofRecord: record, - config: options.config, + public async getCredentialsForRequest( + options: GetCredentialsForProofRequestOptions + ): Promise> { + const proofRecord = await this.getById(options.proofRecordId) + + const protocol = this.getProtocol(proofRecord.protocolVersion) + + return protocol.getCredentialsForRequest(this.agentContext, { + proofRecord, + proofFormats: options.proofFormats, }) } @@ -604,37 +556,38 @@ export class ProofsApi< * @param message message to send * @returns proof record associated with the proof problem report message */ - public async sendProblemReport(proofRecordId: string, message: string): Promise { - const record = await this.getById(proofRecordId) - const service = this.getService(record.protocolVersion) - if (!record.connectionId) { - throw new AriesFrameworkError(`No connectionId found for proof record '${record.id}'.`) + public async sendProblemReport(options: SendProofProblemReportOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) + if (!proofRecord.connectionId) { + throw new AriesFrameworkError(`No connectionId found for proof record '${proofRecord.id}'.`) } - const connection = await this.connectionService.getById(this.agentContext, record.connectionId) + + const protocol = this.getProtocol(proofRecord.protocolVersion) + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) // Assert - connection.assertReady() + connectionRecord.assertReady() - const { message: problemReport } = await service.createProblemReport(this.agentContext, { - proofRecord: record, - description: message, + const { message: problemReport } = await protocol.createProblemReport(this.agentContext, { + proofRecord, + description: options.description, }) const outboundMessageContext = new OutboundMessageContext(problemReport, { agentContext: this.agentContext, - connection, - associatedRecord: record, + connection: connectionRecord, + associatedRecord: proofRecord, }) - await this.messageSender.sendMessage(outboundMessageContext) - return record + await this.messageSender.sendMessage(outboundMessageContext) + return proofRecord } - public async getFormatData(proofRecordId: string): Promise> { + public async getFormatData(proofRecordId: string): Promise>> { const proofRecord = await this.getById(proofRecordId) - const service = this.getService(proofRecord.protocolVersion) + const protocol = this.getProtocol(proofRecord.protocolVersion) - return service.getFormatData(this.agentContext, proofRecordId) + return protocol.getFormatData(this.agentContext, proofRecordId) } /** @@ -685,8 +638,8 @@ export class ProofsApi< */ public async deleteById(proofId: string, options?: DeleteProofOptions) { const proofRecord = await this.getById(proofId) - const service = this.getService(proofRecord.protocolVersion) - return service.delete(this.agentContext, proofRecord, options) + const protocol = this.getProtocol(proofRecord.protocolVersion) + return protocol.delete(this.agentContext, proofRecord, options) } /** @@ -725,34 +678,21 @@ export class ProofsApi< await this.proofRepository.update(this.agentContext, proofRecord) } - public async findProposalMessage(proofRecordId: string): Promise> { + public async findProposalMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) - const service = this.getService(record.protocolVersion) - return service.findProposalMessage(this.agentContext, proofRecordId) as FindProofProposalMessageReturn + const protocol = this.getProtocol(record.protocolVersion) + return protocol.findProposalMessage(this.agentContext, proofRecordId) as FindProofProposalMessageReturn } - public async findRequestMessage(proofRecordId: string): Promise> { + public async findRequestMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) - const service = this.getService(record.protocolVersion) - return service.findRequestMessage(this.agentContext, proofRecordId) as FindProofRequestMessageReturn + const protocol = this.getProtocol(record.protocolVersion) + return protocol.findRequestMessage(this.agentContext, proofRecordId) as FindProofRequestMessageReturn } - public async findPresentationMessage(proofRecordId: string): Promise> { + public async findPresentationMessage(proofRecordId: string): Promise> { const record = await this.getById(proofRecordId) - const service = this.getService(record.protocolVersion) - return service.findPresentationMessage(this.agentContext, proofRecordId) as FindProofPresentationMessageReturn - } - - private registerMessageHandlers(dispatcher: Dispatcher, mediationRecipientService: MediationRecipientService) { - for (const service of Object.values(this.serviceMap)) { - const proofService = service as ProofService - proofService.registerMessageHandlers( - dispatcher, - this.agentConfig, - new ProofResponseCoordinator(proofService), - mediationRecipientService, - this.routingService - ) - } + const protocol = this.getProtocol(record.protocolVersion) + return protocol.findPresentationMessage(this.agentContext, proofRecordId) as FindProofPresentationMessageReturn } } diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts index 5d23a7b131..97bfca04eb 100644 --- a/packages/core/src/modules/proofs/ProofsApiOptions.ts +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -1,97 +1,169 @@ -import type { ProofService } from './ProofService' -import type { ProofFormat, ProofFormatPayload } from './formats/ProofFormat' +import type { ProofFormatCredentialForRequestPayload, ProofFormatPayload } from './formats' import type { AutoAcceptProof } from './models' -import type { ProofConfig } from './models/ModuleOptions' +import type { ProofProtocol } from './protocol/ProofProtocol' +import type { + DeleteProofOptions, + GetProofFormatDataReturn, + ProofFormatsFromProtocols, +} from './protocol/ProofProtocolOptions' -/** - * Get the supported protocol versions based on the provided proof services. - */ -export type ProofsProtocolVersionType = PSs[number]['version'] -export type FindProofProposalMessageReturn = ReturnType -export type FindProofRequestMessageReturn = ReturnType -export type FindProofPresentationMessageReturn = ReturnType< - PSs[number]['findPresentationMessage'] +// re-export GetFormatDataReturn type from protocol, as it is also used in the api +export type { GetProofFormatDataReturn, DeleteProofOptions } + +export type FindProofProposalMessageReturn = ReturnType +export type FindProofRequestMessageReturn = ReturnType +export type FindProofPresentationMessageReturn = ReturnType< + PPs[number]['findPresentationMessage'] > /** - * Get the service map for usage in the proofs module. Will return a type mapping of protocol version to service. - * - * @example - * ``` - * type ServiceMap = ProofServiceMap<[IndyProofFormat], [V1ProofService]> - * - * // equal to - * type ServiceMap = { - * v1: V1ProofService - * } - * ``` + * Get the supported protocol versions based on the provided proof protocols. */ -export type ProofServiceMap[]> = { - [PS in PSs[number] as PS['version']]: ProofService +export type ProofsProtocolVersionType = PPs[number]['version'] + +interface BaseOptions { + autoAcceptProof?: AutoAcceptProof + comment?: string } -export interface ProposeProofOptions< - PFs extends ProofFormat[] = ProofFormat[], - PSs extends ProofService[] = ProofService[] -> { +/** + * Interface for ProofsApi.proposeProof. Will send a proposal. + */ +export interface ProposeProofOptions extends BaseOptions { connectionId: string - protocolVersion: ProofsProtocolVersionType - proofFormats: ProofFormatPayload - comment?: string + protocolVersion: ProofsProtocolVersionType + proofFormats: ProofFormatPayload, 'createProposal'> + goalCode?: string - autoAcceptProof?: AutoAcceptProof parentThreadId?: string } -export interface NegotiateRequestOptions { +/** + * Interface for ProofsApi.acceptProposal. Will send a request + * + * proofFormats is optional because this is an accept method + */ +export interface AcceptProofProposalOptions extends BaseOptions { proofRecordId: string - proofFormats: ProofFormatPayload - comment?: string + proofFormats?: ProofFormatPayload, 'acceptProposal'> + goalCode?: string - autoAcceptProof?: AutoAcceptProof + + /** @default true */ + willConfirm?: boolean } -export interface AcceptProofPresentationOptions { +/** + * Interface for ProofsApi.negotiateProposal. Will send a request + */ +export interface NegotiateProofProposalOptions extends BaseOptions { proofRecordId: string - comment?: string - proofFormats: ProofFormatPayload + proofFormats: ProofFormatPayload, 'createRequest'> + + goalCode?: string + + /** @default true */ + willConfirm?: boolean } -export interface AcceptProofProposalOptions { - proofRecordId: string - config?: ProofConfig +/** + * Interface for ProofsApi.createRequest. Will create an out of band request + */ +export interface CreateProofRequestOptions extends BaseOptions { + protocolVersion: ProofsProtocolVersionType + proofFormats: ProofFormatPayload, 'createRequest'> + goalCode?: string + parentThreadId?: string + + /** @default true */ willConfirm?: boolean - comment?: string } -export interface RequestProofOptions< - PFs extends ProofFormat[] = ProofFormat[], - PSs extends ProofService[] = ProofService[] -> { - protocolVersion: ProofsProtocolVersionType +/** + * Interface for ProofsApi.requestCredential. Extends CreateProofRequestOptions, will send a request + */ +export interface RequestProofOptions + extends BaseOptions, + CreateProofRequestOptions { connectionId: string - proofFormats: ProofFormatPayload - comment?: string - autoAcceptProof?: AutoAcceptProof - parentThreadId?: string } -export interface NegotiateProposalOptions { +/** + * Interface for ProofsApi.acceptRequest. Will send a presentation + */ +export interface AcceptProofRequestOptions extends BaseOptions { proofRecordId: string - proofFormats: ProofFormatPayload - comment?: string - autoAcceptProof?: AutoAcceptProof - parentThreadId?: string + proofFormats?: ProofFormatPayload, 'acceptRequest'> + + goalCode?: string + + /** @default true */ + willConfirm?: boolean } -export interface CreateProofRequestOptions< - PFs extends ProofFormat[] = ProofFormat[], - PSs extends ProofService[] = ProofService[] -> { - protocolVersion: ProofsProtocolVersionType - proofFormats: ProofFormatPayload - comment?: string - autoAcceptProof?: AutoAcceptProof - parentThreadId?: string +/** + * Interface for ProofsApi.negotiateRequest. Will send a proposal + */ +export interface NegotiateProofRequestOptions extends BaseOptions { + proofRecordId: string + proofFormats: ProofFormatPayload, 'createProposal'> + + goalCode?: string +} + +/** + * Interface for ProofsApi.acceptPresentation. Will send an ack message + */ +export interface AcceptProofOptions { + proofRecordId: string +} + +/** + * Interface for ProofsApi.getCredentialsForRequest. Will return the credentials that match the proof request + */ +export interface GetCredentialsForProofRequestOptions { + proofRecordId: string + proofFormats?: ProofFormatCredentialForRequestPayload< + ProofFormatsFromProtocols, + 'getCredentialsForRequest', + 'input' + > +} + +export interface GetCredentialsForProofRequestReturn { + proofFormats: ProofFormatCredentialForRequestPayload< + ProofFormatsFromProtocols, + 'getCredentialsForRequest', + 'output' + > +} + +/** + * Interface for ProofsApi.selectCredentialsForRequest. Will automatically select return the first/best + * credentials that match the proof request + */ +export interface SelectCredentialsForProofRequestOptions { + proofRecordId: string + proofFormats?: ProofFormatCredentialForRequestPayload< + ProofFormatsFromProtocols, + 'getCredentialsForRequest', + 'input' + > +} + +export interface SelectCredentialsForProofRequestReturn { + proofFormats: ProofFormatCredentialForRequestPayload< + ProofFormatsFromProtocols, + 'selectCredentialsForRequest', + 'output' + > +} + +/** + * Interface for ProofsApi.sendProblemReport. Will send a problem-report message + */ +export interface SendProofProblemReportOptions { + proofRecordId: string + description: string } diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index 339ecd0c41..a3d3cf3b4d 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -1,22 +1,55 @@ import type { ProofsModuleConfigOptions } from './ProofsModuleConfig' +import type { ProofProtocol } from './protocol/ProofProtocol' import type { FeatureRegistry } from '../../agent/FeatureRegistry' -import type { DependencyManager, Module } from '../../plugins' - -import { Protocol } from '../../agent/models' +import type { ApiModule, DependencyManager } from '../../plugins' +import type { Optional } from '../../utils' +import type { Constructor } from '../../utils/mixins' import { ProofsApi } from './ProofsApi' import { ProofsModuleConfig } from './ProofsModuleConfig' import { IndyProofFormatService } from './formats/indy/IndyProofFormatService' -import { V1ProofService } from './protocol/v1' -import { V2ProofService } from './protocol/v2' +import { V1ProofProtocol, V2ProofProtocol } from './protocol' import { ProofRepository } from './repository' -export class ProofsModule implements Module { - public readonly config: ProofsModuleConfig - public readonly api = ProofsApi +/** + * Default proofProtocols that will be registered if the `proofProtocols` property is not configured. + */ +export type DefaultProofProtocols = [V1ProofProtocol, V2ProofProtocol] + +// ProofsModuleOptions makes the proofProtocols property optional from the config, as it will set it when not provided. +export type ProofsModuleOptions = Optional< + ProofsModuleConfigOptions, + 'proofProtocols' +> + +export class ProofsModule implements ApiModule { + public readonly config: ProofsModuleConfig + + public readonly api: Constructor> = ProofsApi + + public constructor(config?: ProofsModuleOptions) { + this.config = new ProofsModuleConfig({ + ...config, + // NOTE: the proofProtocols defaults are set in the ProofsModule rather than the ProofsModuleConfig to + // avoid dependency cycles. + proofProtocols: config?.proofProtocols ?? this.getDefaultProofProtocols(), + }) as ProofsModuleConfig + } + + /** + * Get the default proof protocols that will be registered if the `proofProtocols` property is not configured. + */ + private getDefaultProofProtocols(): DefaultProofProtocols { + // Instantiate proof formats + const indyProofFormat = new IndyProofFormatService() + + // Instantiate proof protocols + const v1ProofProtocol = new V1ProofProtocol({ indyProofFormat }) + const v2ProofProtocol = new V2ProofProtocol({ + proofFormats: [indyProofFormat], + }) - public constructor(config?: ProofsModuleConfigOptions) { - this.config = new ProofsModuleConfig(config) + return [v1ProofProtocol, v2ProofProtocol] } /** @@ -29,22 +62,11 @@ export class ProofsModule implements Module { // Config dependencyManager.registerInstance(ProofsModuleConfig, this.config) - // Services - dependencyManager.registerSingleton(V1ProofService) - dependencyManager.registerSingleton(V2ProofService) - // Repositories dependencyManager.registerSingleton(ProofRepository) - // Proof Formats - dependencyManager.registerSingleton(IndyProofFormatService) - - // Features - featureRegistry.register( - new Protocol({ - id: 'https://didcomm.org/present-proof/1.0', - roles: ['verifier', 'prover'], - }) - ) + for (const proofProtocol of this.config.proofProtocols) { + proofProtocol.register(dependencyManager, featureRegistry) + } } } diff --git a/packages/core/src/modules/proofs/ProofsModuleConfig.ts b/packages/core/src/modules/proofs/ProofsModuleConfig.ts index e0b12449e2..3526e4eb7d 100644 --- a/packages/core/src/modules/proofs/ProofsModuleConfig.ts +++ b/packages/core/src/modules/proofs/ProofsModuleConfig.ts @@ -1,27 +1,47 @@ +import type { ProofProtocol } from './protocol/ProofProtocol' + import { AutoAcceptProof } from './models/ProofAutoAcceptType' /** * ProofsModuleConfigOptions defines the interface for the options of the ProofsModuleConfig class. * This can contain optional parameters that have default values in the config class itself. */ -export interface ProofsModuleConfigOptions { +export interface ProofsModuleConfigOptions { /** * Whether to automatically accept proof messages. Applies to all present proof protocol versions. * * @default {@link AutoAcceptProof.Never} */ autoAcceptProofs?: AutoAcceptProof + + /** + * Proof protocols to make available to the proofs module. Only one proof protocol should be registered for each proof + * protocol version. + * + * When not provided, the `V1ProofProtocol` and `V2ProofProtocol` are registered by default. + * + * @default + * ``` + * [V1ProofProtocol, V2ProofProtocol] + * ``` + */ + proofProtocols: ProofProtocols } -export class ProofsModuleConfig { - private options: ProofsModuleConfigOptions +export class ProofsModuleConfig { + private options: ProofsModuleConfigOptions - public constructor(options?: ProofsModuleConfigOptions) { - this.options = options ?? {} + public constructor(options: ProofsModuleConfigOptions) { + this.options = options } /** See {@link ProofsModuleConfigOptions.autoAcceptProofs} */ public get autoAcceptProofs() { return this.options.autoAcceptProofs ?? AutoAcceptProof.Never } + + /** See {@link CredentialsModuleConfigOptions.proofProtocols} */ + public get proofProtocols() { + return this.options.proofProtocols + } } diff --git a/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts index 8af9a5b2c2..c2012ed566 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts @@ -1,33 +1,60 @@ +import type { ProofProtocol } from '../protocol/ProofProtocol' + import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' import { ProofsApi } from '../ProofsApi' import { ProofsModule } from '../ProofsModule' -import { IndyProofFormatService } from '../formats/indy/IndyProofFormatService' -import { V1ProofService } from '../protocol/v1/V1ProofService' -import { V2ProofService } from '../protocol/v2/V2ProofService' +import { ProofsModuleConfig } from '../ProofsModuleConfig' +import { V1ProofProtocol } from '../protocol/v1/V1ProofProtocol' +import { V2ProofProtocol } from '../protocol/v2/V2ProofProtocol' import { ProofRepository } from '../repository' jest.mock('../../../plugins/DependencyManager') -const DependencyManagerMock = DependencyManager as jest.Mock +jest.mock('../../../agent/FeatureRegistry') +const DependencyManagerMock = DependencyManager as jest.Mock const dependencyManager = new DependencyManagerMock() - -jest.mock('../../../agent/FeatureRegistry') const FeatureRegistryMock = FeatureRegistry as jest.Mock - const featureRegistry = new FeatureRegistryMock() describe('ProofsModule', () => { test('registers dependencies on the dependency manager', () => { - new ProofsModule().register(dependencyManager, featureRegistry) + const proofsModule = new ProofsModule({ + proofProtocols: [], + }) + proofsModule.register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(ProofsApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(4) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V1ProofService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V2ProofService) + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(ProofsModuleConfig, proofsModule.config) + + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ProofRepository) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyProofFormatService) + }) + + test('registers V1ProofProtocol and V2ProofProtocol if no proofProtocols are configured', () => { + const proofsModule = new ProofsModule() + + expect(proofsModule.config.proofProtocols).toEqual([expect.any(V1ProofProtocol), expect.any(V2ProofProtocol)]) + }) + + test('calls register on the provided ProofProtocols', () => { + const registerMock = jest.fn() + const proofProtocol = { + register: registerMock, + } as unknown as ProofProtocol + + const proofsModule = new ProofsModule({ + proofProtocols: [proofProtocol], + }) + + expect(proofsModule.config.proofProtocols).toEqual([proofProtocol]) + + proofsModule.register(dependencyManager, featureRegistry) + + expect(registerMock).toHaveBeenCalledTimes(1) + expect(registerMock).toHaveBeenCalledWith(dependencyManager, featureRegistry) }) }) diff --git a/packages/core/src/modules/proofs/__tests__/ProofsModuleConfig.test.ts b/packages/core/src/modules/proofs/__tests__/ProofsModuleConfig.test.ts new file mode 100644 index 0000000000..35920a4f48 --- /dev/null +++ b/packages/core/src/modules/proofs/__tests__/ProofsModuleConfig.test.ts @@ -0,0 +1,26 @@ +import type { ProofProtocol } from '../protocol/ProofProtocol' + +import { ProofsModuleConfig } from '../ProofsModuleConfig' +import { AutoAcceptProof } from '../models' + +describe('ProofsModuleConfig', () => { + test('sets default values', () => { + const config = new ProofsModuleConfig({ + proofProtocols: [], + }) + + expect(config.autoAcceptProofs).toBe(AutoAcceptProof.Never) + expect(config.proofProtocols).toEqual([]) + }) + + test('sets values', () => { + const proofProtocol = jest.fn() as unknown as ProofProtocol + const config = new ProofsModuleConfig({ + autoAcceptProofs: AutoAcceptProof.Always, + proofProtocols: [proofProtocol], + }) + + expect(config.autoAcceptProofs).toBe(AutoAcceptProof.Always) + expect(config.proofProtocols).toEqual([proofProtocol]) + }) +}) diff --git a/packages/core/src/modules/proofs/__tests__/fixtures.ts b/packages/core/src/modules/proofs/__tests__/fixtures.ts index 10606073b8..2045f6f0f8 100644 --- a/packages/core/src/modules/proofs/__tests__/fixtures.ts +++ b/packages/core/src/modules/proofs/__tests__/fixtures.ts @@ -15,3 +15,33 @@ export const credDef = { }, }, } + +export const TEST_INPUT_DESCRIPTORS_CITIZENSHIP = { + constraints: { + fields: [ + { + path: ['$.credentialSubject.familyName'], + purpose: 'The claim must be from one of the specified issuers', + id: '1f44d55f-f161-4938-a659-f8026467f126', + }, + { + path: ['$.credentialSubject.givenName'], + purpose: 'The claim must be from one of the specified issuers', + }, + ], + }, + schema: [ + { + uri: 'https://www.w3.org/2018/credentials#VerifiableCredential', + }, + { + uri: 'https://w3id.org/citizenship#PermanentResident', + }, + { + uri: 'https://w3id.org/citizenship/v1', + }, + ], + name: "EU Driver's License", + group: ['A'], + id: 'citizenship_input_1', +} diff --git a/packages/core/src/modules/proofs/formats/ProofFormat.ts b/packages/core/src/modules/proofs/formats/ProofFormat.ts index 18fbba278d..c573c12579 100644 --- a/packages/core/src/modules/proofs/formats/ProofFormat.ts +++ b/packages/core/src/modules/proofs/formats/ProofFormat.ts @@ -21,21 +21,50 @@ export type ProofFormatPayload + * + * // equal to + * type SelectedCredentialsForRequest = { + * indy: { + * // ... return value for indy selected credentials ... + * }, + * presentationExchange: { + * // ... return value for presentation exchange selected credentials ... + * } + * } + * ``` + */ +export type ProofFormatCredentialForRequestPayload< + PFs extends ProofFormat[], + M extends 'selectCredentialsForRequest' | 'getCredentialsForRequest', + IO extends 'input' | 'output' +> = { + [ProofFormat in PFs[number] as ProofFormat['formatKey']]?: ProofFormat['proofFormats'][M][IO] +} + export interface ProofFormat { - formatKey: string // e.g. 'ProofManifest', cannot be shared between different formats + formatKey: string // e.g. 'presentationExchange', cannot be shared between different formats + proofFormats: { createProposal: unknown acceptProposal: unknown createRequest: unknown acceptRequest: unknown - createPresentation: unknown - acceptPresentation: unknown - createProposalAsResponse: unknown - createOutOfBandRequest: unknown - createRequestAsResponse: unknown - createProofRequestFromProposal: unknown - requestCredentials: unknown - retrieveCredentials: unknown + + getCredentialsForRequest: { + input: unknown + output: unknown + } + selectCredentialsForRequest: { + input: unknown + output: unknown + } } formatData: { proposal: unknown diff --git a/packages/core/src/modules/proofs/formats/ProofFormatConstants.ts b/packages/core/src/modules/proofs/formats/ProofFormatConstants.ts deleted file mode 100644 index 35e1ce33ab..0000000000 --- a/packages/core/src/modules/proofs/formats/ProofFormatConstants.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const INDY_ATTACH_ID = 'indy' -export const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' -export const V2_INDY_PRESENTATION_REQUEST = 'hlindy/proof-req@v2.0' -export const V2_INDY_PRESENTATION = 'hlindy/proof@v2.0' diff --git a/packages/core/src/modules/proofs/formats/ProofFormatService.ts b/packages/core/src/modules/proofs/formats/ProofFormatService.ts index 931d4c886f..f48db80624 100644 --- a/packages/core/src/modules/proofs/formats/ProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/ProofFormatService.ts @@ -1,81 +1,70 @@ import type { ProofFormat } from './ProofFormat' -import type { IndyProofFormat } from './indy/IndyProofFormat' -import type { GetRequestedCredentialsFormat } from './indy/IndyProofFormatsServiceOptions' -import type { ProofAttachmentFormat } from './models/ProofAttachmentFormat' import type { - CreatePresentationFormatsOptions, - FormatCreateProofProposalOptions, - CreateRequestOptions, - FormatCreatePresentationOptions, - ProcessPresentationOptions, - ProcessProposalOptions, - ProcessRequestOptions, -} from './models/ProofFormatServiceOptions' + ProofFormatAcceptProposalOptions, + ProofFormatAcceptRequestOptions, + ProofFormatCreateProposalOptions, + FormatCreateRequestOptions, + ProofFormatProcessPresentationOptions, + ProofFormatCreateReturn, + ProofFormatProcessOptions, + ProofFormatGetCredentialsForRequestOptions, + ProofFormatGetCredentialsForRequestReturn, + ProofFormatSelectCredentialsForRequestOptions, + ProofFormatSelectCredentialsForRequestReturn, + ProofFormatAutoRespondProposalOptions, + ProofFormatAutoRespondRequestOptions, + ProofFormatAutoRespondPresentationOptions, +} from './ProofFormatServiceOptions' import type { AgentContext } from '../../../agent' -import type { AgentConfig } from '../../../agent/AgentConfig' -import type { DidCommMessageRepository } from '../../../storage' -import type { - CreateRequestAsResponseOptions, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, -} from '../models/ProofServiceOptions' -import type { ProofRequestFormats } from '../models/SharedOptions' - -/** - * This abstract class is the base class for any proof format - * specific service. - * - * @export - * @abstract - * @class ProofFormatService - */ -export abstract class ProofFormatService { - protected didCommMessageRepository: DidCommMessageRepository - protected agentConfig: AgentConfig - - public abstract readonly formatKey: PF['formatKey'] - - public constructor(didCommMessageRepository: DidCommMessageRepository, agentConfig: AgentConfig) { - this.didCommMessageRepository = didCommMessageRepository - this.agentConfig = agentConfig - } - public abstract createProposal(options: FormatCreateProofProposalOptions): Promise +export interface ProofFormatService { + formatKey: PF['formatKey'] - public abstract processProposal(options: ProcessProposalOptions): Promise - - public abstract createRequest(options: CreateRequestOptions): Promise - - public abstract processRequest(options: ProcessRequestOptions): Promise - - public abstract createPresentation( + // proposal methods + createProposal( agentContext: AgentContext, - options: FormatCreatePresentationOptions - ): Promise - - public abstract processPresentation(agentContext: AgentContext, options: ProcessPresentationOptions): Promise - - public abstract createProofRequestFromProposal( - options: CreatePresentationFormatsOptions - ): Promise + options: ProofFormatCreateProposalOptions + ): Promise + processProposal(agentContext: AgentContext, options: ProofFormatProcessOptions): Promise + acceptProposal( + agentContext: AgentContext, + options: ProofFormatAcceptProposalOptions + ): Promise - public abstract getRequestedCredentialsForProofRequest( + // request methods + createRequest(agentContext: AgentContext, options: FormatCreateRequestOptions): Promise + processRequest(agentContext: AgentContext, options: ProofFormatProcessOptions): Promise + acceptRequest( agentContext: AgentContext, - options: GetRequestedCredentialsFormat - ): Promise> + options: ProofFormatAcceptRequestOptions + ): Promise - public abstract autoSelectCredentialsForProofRequest( - options: FormatRetrievedCredentialOptions<[PF]> - ): Promise> + // presentation methods + processPresentation(agentContext: AgentContext, options: ProofFormatProcessPresentationOptions): Promise - public abstract proposalAndRequestAreEqual( - proposalAttachments: ProofAttachmentFormat[], - requestAttachments: ProofAttachmentFormat[] - ): boolean + // credentials for request + getCredentialsForRequest( + agentContext: AgentContext, + options: ProofFormatGetCredentialsForRequestOptions + ): Promise> + selectCredentialsForRequest( + agentContext: AgentContext, + options: ProofFormatSelectCredentialsForRequestOptions + ): Promise> - public abstract supportsFormat(formatIdentifier: string): boolean + // auto accept methods + shouldAutoRespondToProposal( + agentContext: AgentContext, + options: ProofFormatAutoRespondProposalOptions + ): Promise + shouldAutoRespondToRequest( + agentContext: AgentContext, + options: ProofFormatAutoRespondRequestOptions + ): Promise + shouldAutoRespondToPresentation( + agentContext: AgentContext, + options: ProofFormatAutoRespondPresentationOptions + ): Promise - public abstract createRequestAsResponse( - options: CreateRequestAsResponseOptions<[IndyProofFormat]> - ): Promise + supportsFormat(formatIdentifier: string): boolean } diff --git a/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts index 25731e43c8..db7923bafc 100644 --- a/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts @@ -1,23 +1,35 @@ -import type { ProofFormat } from './ProofFormat' +import type { ProofFormat, ProofFormatCredentialForRequestPayload, ProofFormatPayload } from './ProofFormat' import type { ProofFormatService } from './ProofFormatService' import type { Attachment } from '../../../decorators/attachment/Attachment' import type { ProofFormatSpec } from '../models/ProofFormatSpec' +import type { ProofExchangeRecord } from '../repository/ProofExchangeRecord' /** - * Get the service map for usage in the proofs module. Will return a type mapping of protocol version to service. + * Infer the {@link ProofFormat} based on a {@link ProofFormatService}. + * + * It does this by extracting the `ProofFormat` generic from the `ProofFormatService`. * * @example * ``` - * type FormatServiceMap = ProofFormatServiceMap<[IndyProofFormat]> + * // TheProofFormat is now equal to IndyProofFormat + * type TheProofFormat = ExtractProofFormat + * ``` * - * // equal to - * type FormatServiceMap = { - * indy: ProofFormatServiceMap + * Because the `IndyProofFormatService` is defined as follows: + * ``` + * class IndyProofFormatService implements ProofFormatService { * } * ``` */ -export type ProofFormatServiceMap = { - [PF in PFs[number] as PF['formatKey']]: ProofFormatService +export type ExtractProofFormat = Type extends ProofFormatService ? ProofFormat : never + +/** + * Infer an array of {@link ProofFormat} types based on an array of {@link ProofFormatService} types. + * + * This is based on {@link ExtractProofFormat}, but allows to handle arrays. + */ +export type ExtractProofFormats = { + [PF in keyof PFs]: ExtractProofFormat } /** @@ -29,3 +41,85 @@ export interface ProofFormatCreateReturn { format: ProofFormatSpec attachment: Attachment } + +/** + * Base type for all proof process methods. + */ +export interface ProofFormatProcessOptions { + attachment: Attachment + proofRecord: ProofExchangeRecord +} + +export interface ProofFormatProcessPresentationOptions extends ProofFormatProcessOptions { + requestAttachment: Attachment +} + +export interface ProofFormatCreateProposalOptions { + proofRecord: ProofExchangeRecord + proofFormats: ProofFormatPayload<[PF], 'createProposal'> + attachmentId?: string +} + +export interface ProofFormatAcceptProposalOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatPayload<[PF], 'acceptProposal'> + attachmentId?: string + + proposalAttachment: Attachment +} + +export interface FormatCreateRequestOptions { + proofRecord: ProofExchangeRecord + proofFormats: ProofFormatPayload<[PF], 'createRequest'> + attachmentId?: string +} + +export interface ProofFormatAcceptRequestOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatPayload<[PF], 'acceptRequest'> + attachmentId?: string + + requestAttachment: Attachment + proposalAttachment?: Attachment +} + +export interface ProofFormatGetCredentialsForRequestOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatCredentialForRequestPayload<[PF], 'getCredentialsForRequest', 'input'> + + requestAttachment: Attachment + proposalAttachment?: Attachment +} + +export type ProofFormatGetCredentialsForRequestReturn = + PF['proofFormats']['getCredentialsForRequest']['output'] + +export interface ProofFormatSelectCredentialsForRequestOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatCredentialForRequestPayload<[PF], 'selectCredentialsForRequest', 'input'> + + requestAttachment: Attachment + proposalAttachment?: Attachment +} + +export type ProofFormatSelectCredentialsForRequestReturn = + PF['proofFormats']['selectCredentialsForRequest']['output'] + +export interface ProofFormatAutoRespondProposalOptions { + proofRecord: ProofExchangeRecord + proposalAttachment: Attachment + requestAttachment: Attachment +} + +export interface ProofFormatAutoRespondRequestOptions { + proofRecord: ProofExchangeRecord + requestAttachment: Attachment + proposalAttachment: Attachment +} + +export interface ProofFormatAutoRespondPresentationOptions { + proofRecord: ProofExchangeRecord + proposalAttachment?: Attachment + requestAttachment: Attachment + presentationAttachment: Attachment +} diff --git a/packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts b/packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts new file mode 100644 index 0000000000..a81e4e5553 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts @@ -0,0 +1,3 @@ +import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' + +export class InvalidEncodedValueError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts b/packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts new file mode 100644 index 0000000000..a00abc40cb --- /dev/null +++ b/packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts @@ -0,0 +1,3 @@ +import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' + +export class MissingIndyProofMessageError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/formats/index.ts b/packages/core/src/modules/proofs/formats/index.ts index efb4e8a6ab..08ece3aa21 100644 --- a/packages/core/src/modules/proofs/formats/index.ts +++ b/packages/core/src/modules/proofs/formats/index.ts @@ -1,6 +1,9 @@ -export * from './indy' -export * from './models' export * from './ProofFormat' -export * from './ProofFormatConstants' export * from './ProofFormatService' export * from './ProofFormatServiceOptions' + +export * from './indy' + +import * as ProofFormatServiceOptions from './ProofFormatServiceOptions' + +export { ProofFormatServiceOptions } diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts index ae58b2db75..a6afce160a 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts @@ -1,46 +1,75 @@ -import type { RequestedAttribute } from './models/RequestedAttribute' -import type { IndyRequestedCredentialsOptions } from './models/RequestedCredentials' -import type { RequestedPredicate } from './models/RequestedPredicate' -import type { PresentationPreviewAttribute, PresentationPreviewPredicate } from '../../protocol/v1' +import type { ProofAttributeInfoOptions, ProofPredicateInfoOptions } from './models' +import type { RequestedAttributeOptions } from './models/RequestedAttribute' +import type { RequestedPredicateOptions } from './models/RequestedPredicate' +import type { V1PresentationPreviewAttributeOptions, V1PresentationPreviewPredicateOptions } from '../../protocol/v1' import type { ProofFormat } from '../ProofFormat' -import type { IndyRequestProofFormat } from '../indy/IndyProofFormatsServiceOptions' import type { IndyProof, IndyProofRequest } from 'indy-sdk' +/** + * Interface for creating an indy proof proposal. + */ export interface IndyProposeProofFormat { - attributes?: PresentationPreviewAttribute[] - predicates?: PresentationPreviewPredicate[] - nonce?: string name?: string version?: string + attributes?: V1PresentationPreviewAttributeOptions[] + predicates?: V1PresentationPreviewPredicateOptions[] } -export interface IndyRequestedCredentialsFormat { - requestedAttributes: Record - requestedPredicates: Record +/** + * Interface for creating an indy proof request. + */ +export interface IndyRequestProofFormat { + name: string + version: string + // TODO: update to AnonCredsNonRevokedInterval when moving to AnonCreds package + nonRevoked?: { from?: number; to?: number } + requestedAttributes?: Record + requestedPredicates?: Record +} + +/** + * Interface for accepting an indy proof request. + */ +export type IndyAcceptProofRequestFormat = Partial + +export interface IndySelectedCredentialsForProofRequest { + requestedAttributes: Record + requestedPredicates: Record selfAttestedAttributes: Record } -export interface IndyRetrievedCredentialsFormat { - requestedAttributes: Record - requestedPredicates: Record +/** + * Interface for getting credentials for an indy proof request. + */ +export interface IndyCredentialsForProofRequest { + attributes: Record + predicates: Record +} + +export interface IndyGetCredentialsForProofRequestOptions { + filterByNonRevocationRequirements?: boolean } export interface IndyProofFormat extends ProofFormat { formatKey: 'indy' - proofRecordType: 'indy' + proofFormats: { createProposal: IndyProposeProofFormat - acceptProposal: unknown + acceptProposal: { + name?: string + version?: string + } createRequest: IndyRequestProofFormat - acceptRequest: unknown - createPresentation: IndyRequestedCredentialsOptions - acceptPresentation: unknown - createProposalAsResponse: IndyProposeProofFormat - createOutOfBandRequest: unknown - createRequestAsResponse: IndyRequestProofFormat - createProofRequestFromProposal: IndyRequestProofFormat - requestCredentials: IndyRequestedCredentialsFormat - retrieveCredentials: IndyRetrievedCredentialsFormat + acceptRequest: IndyAcceptProofRequestFormat + + getCredentialsForRequest: { + input: IndyGetCredentialsForProofRequestOptions + output: IndyCredentialsForProofRequest + } + selectCredentialsForRequest: { + input: IndyGetCredentialsForProofRequestOptions + output: IndySelectedCredentialsForProofRequest + } } formatData: { diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts index 9730e6aa8d..924f9dcb62 100644 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts +++ b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts @@ -1,293 +1,211 @@ -import type { IndyProofFormat, IndyProposeProofFormat } from './IndyProofFormat' -import type { GetRequestedCredentialsFormat } from './IndyProofFormatsServiceOptions' -import type { AgentContext } from '../../../../agent' -import type { Logger } from '../../../../logger' import type { - CreateRequestAsResponseOptions, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, -} from '../../models/ProofServiceOptions' -import type { ProofRequestFormats } from '../../models/SharedOptions' -import type { PresentationPreviewAttribute } from '../../protocol/v1/models' -import type { ProofAttachmentFormat } from '../models/ProofAttachmentFormat' + IndyCredentialsForProofRequest, + IndyGetCredentialsForProofRequestOptions, + IndyProofFormat, + IndySelectedCredentialsForProofRequest, +} from './IndyProofFormat' +import type { ProofAttributeInfo, ProofPredicateInfo } from './models' +import type { AgentContext } from '../../../../agent' +import type { ProofFormatService } from '../ProofFormatService' import type { - CreatePresentationFormatsOptions, - CreateProofAttachmentOptions, - FormatCreateProofProposalOptions, - CreateRequestAttachmentOptions, - CreateRequestOptions, - FormatCreatePresentationOptions, - ProcessPresentationOptions, - ProcessProposalOptions, - ProcessRequestOptions, - VerifyProofOptions, -} from '../models/ProofFormatServiceOptions' -import type { CredDef, IndyProof, Schema } from 'indy-sdk' - -import { Lifecycle, scoped } from 'tsyringe' - -import { AgentConfig } from '../../../../agent/AgentConfig' + ProofFormatCreateProposalOptions, + ProofFormatCreateReturn, + ProofFormatAcceptProposalOptions, + ProofFormatAcceptRequestOptions, + ProofFormatAutoRespondProposalOptions, + ProofFormatAutoRespondRequestOptions, + ProofFormatGetCredentialsForRequestOptions, + ProofFormatGetCredentialsForRequestReturn, + ProofFormatSelectCredentialsForRequestOptions, + ProofFormatSelectCredentialsForRequestReturn, + ProofFormatProcessOptions, + FormatCreateRequestOptions, + ProofFormatProcessPresentationOptions, +} from '../ProofFormatServiceOptions' +import type { CredDef, IndyProof, IndyProofRequest, Schema } from 'indy-sdk' + import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { ConsoleLogger, LogLevel } from '../../../../logger' -import { DidCommMessageRepository } from '../../../../storage/didcomm/DidCommMessageRepository' -import { checkProofRequestForDuplicates, deepEquality } from '../../../../utils' import { JsonEncoder } from '../../../../utils/JsonEncoder' import { JsonTransformer } from '../../../../utils/JsonTransformer' import { MessageValidator } from '../../../../utils/MessageValidator' -import { uuid } from '../../../../utils/uuid' -import { IndyWallet } from '../../../../wallet/IndyWallet' import { IndyCredential, IndyCredentialInfo } from '../../../credentials' import { IndyCredentialUtils } from '../../../credentials/formats/indy/IndyCredentialUtils' -import { IndyHolderService, IndyVerifierService, IndyRevocationService } from '../../../indy' +import { IndyVerifierService, IndyHolderService, IndyRevocationService } from '../../../indy' import { IndyLedgerService } from '../../../ledger' import { ProofFormatSpec } from '../../models/ProofFormatSpec' -import { PartialProof, PresentationPreview } from '../../protocol/v1/models' -import { - V2_INDY_PRESENTATION_REQUEST, - V2_INDY_PRESENTATION_PROPOSAL, - V2_INDY_PRESENTATION, -} from '../ProofFormatConstants' -import { ProofFormatService } from '../ProofFormatService' import { InvalidEncodedValueError } from './errors/InvalidEncodedValueError' -import { MissingIndyProofMessageError } from './errors/MissingIndyProofMessageError' -import { - AttributeFilter, - ProofAttributeInfo, - ProofPredicateInfo, - RequestedAttribute, - RequestedPredicate, -} from './models' +import { RequestedAttribute, RequestedPredicate } from './models' +import { PartialProof } from './models/PartialProof' import { ProofRequest } from './models/ProofRequest' import { RequestedCredentials } from './models/RequestedCredentials' -import { RetrievedCredentials } from './models/RetrievedCredentials' +import { areIndyProofRequestsEqual, assertNoDuplicateGroupsNamesInProofRequest, createRequestFromPreview } from './util' import { sortRequestedCredentials } from './util/sortRequestedCredentials' -@scoped(Lifecycle.ContainerScoped) -export class IndyProofFormatService extends ProofFormatService { - private indyHolderService: IndyHolderService - private indyVerifierService: IndyVerifierService - private indyRevocationService: IndyRevocationService - private ledgerService: IndyLedgerService - private logger: Logger - private wallet: IndyWallet - - public constructor( - agentConfig: AgentConfig, - indyHolderService: IndyHolderService, - indyVerifierService: IndyVerifierService, - indyRevocationService: IndyRevocationService, - ledgerService: IndyLedgerService, - didCommMessageRepository: DidCommMessageRepository, - wallet: IndyWallet - ) { - super(didCommMessageRepository, agentConfig) - this.indyHolderService = indyHolderService - this.indyVerifierService = indyVerifierService - this.indyRevocationService = indyRevocationService - this.ledgerService = ledgerService - this.wallet = wallet - this.logger = new ConsoleLogger(LogLevel.off) - } +const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' +const V2_INDY_PRESENTATION_REQUEST = 'hlindy/proof-req@v2.0' +const V2_INDY_PRESENTATION = 'hlindy/proof@v2.0' + +export class IndyProofFormatService implements ProofFormatService { public readonly formatKey = 'indy' as const - public readonly proofRecordType = 'indy' as const - private createRequestAttachment(options: CreateRequestAttachmentOptions): ProofAttachmentFormat { + public async createProposal( + agentContext: AgentContext, + { attachmentId, proofFormats }: ProofFormatCreateProposalOptions + ): Promise { const format = new ProofFormatSpec({ - attachmentId: options.id, - format: V2_INDY_PRESENTATION_REQUEST, + format: V2_INDY_PRESENTATION_PROPOSAL, + attachmentId, }) - const request = new ProofRequest(options.proofRequestOptions) - - // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(request) + const indyFormat = proofFormats.indy + if (!indyFormat) { + throw Error('Missing indy format to create proposal attachment format') + } - const attachment = new Attachment({ - id: options.id, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(request), - }), + const proofRequest = createRequestFromPreview({ + attributes: indyFormat.attributes ?? [], + predicates: indyFormat.predicates ?? [], + name: indyFormat.name ?? 'Proof request', + version: indyFormat.version ?? '1.0', + nonce: await agentContext.wallet.generateNonce(), }) - return { format, attachment } + const attachment = this.getFormatData(proofRequest.toJSON(), format.attachmentId) + + return { attachment, format } } - private async createProofAttachment(options: CreateProofAttachmentOptions): Promise { - const format = new ProofFormatSpec({ - attachmentId: options.id, - format: V2_INDY_PRESENTATION_PROPOSAL, - }) + public async processProposal(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const proposalJson = attachment.getDataAsJson() - const request = new ProofRequest(options.proofProposalOptions) - MessageValidator.validateSync(request) + // fromJSON also validates + const proposal = JsonTransformer.fromJSON(proposalJson, ProofRequest) - const attachment = new Attachment({ - id: options.id, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(JsonTransformer.toJSON(request)), - }), - }) - return { format, attachment } + // Assert attribute and predicate (group) names do not match + assertNoDuplicateGroupsNamesInProofRequest(proposal) } - public async createProposal(options: FormatCreateProofProposalOptions): Promise { - if (!options.formats.indy) { - throw Error('Missing indy format to create proposal attachment format') - } - const proofRequest = await this.createRequestFromPreview(options.formats.indy) - - return await this.createProofAttachment({ - id: options.id ?? uuid(), - proofProposalOptions: proofRequest, + public async acceptProposal( + agentContext: AgentContext, + { proposalAttachment, attachmentId }: ProofFormatAcceptProposalOptions + ): Promise { + const format = new ProofFormatSpec({ + format: V2_INDY_PRESENTATION_REQUEST, + attachmentId, }) - } - public async processProposal(options: ProcessProposalOptions): Promise { - const proofProposalJson = options.proposal.attachment.getDataAsJson() + const proposalJson = proposalAttachment.getDataAsJson() - // Assert attachment - if (!proofProposalJson) { - throw new AriesFrameworkError( - `Missing required base64 or json encoded attachment data for presentation proposal with thread id ${options.record?.threadId}` - ) - } + // The proposal and request formats are the same, so we can just use the proposal + const request = JsonTransformer.fromJSON(proposalJson, ProofRequest) - const proposalMessage = JsonTransformer.fromJSON(proofProposalJson, ProofRequest) + // We never want to reuse the nonce from the proposal, as this will allow replay attacks + request.nonce = await agentContext.wallet.generateNonce() - MessageValidator.validateSync(proposalMessage) - } - - public async createRequestAsResponse( - options: CreateRequestAsResponseOptions<[IndyProofFormat]> - ): Promise { - if (!options.proofFormats.indy) { - throw Error('Missing indy format to create proposal attachment format') - } + const attachment = this.getFormatData(request.toJSON(), format.attachmentId) - const id = options.id ?? uuid() + return { attachment, format } + } + public async createRequest( + agentContext: AgentContext, + { attachmentId, proofFormats }: FormatCreateRequestOptions + ): Promise { const format = new ProofFormatSpec({ - attachmentId: id, format: V2_INDY_PRESENTATION_REQUEST, + attachmentId, }) - const attachment = new Attachment({ - id: id, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(options.proofFormats.indy), - }), + const indyFormat = proofFormats.indy + if (!indyFormat) { + throw Error('Missing indy format in create request attachment format') + } + + const request = new ProofRequest({ + name: indyFormat.name, + version: indyFormat.version, + nonce: await agentContext.wallet.generateNonce(), + requestedAttributes: indyFormat.requestedAttributes, + requestedPredicates: indyFormat.requestedPredicates, + nonRevoked: indyFormat.nonRevoked, }) - return { format, attachment } - } - public async createRequest(options: CreateRequestOptions): Promise { - if (!options.formats.indy) { - throw new AriesFrameworkError('Missing indy format to create proof request attachment format.') - } + // Validate to make sure user provided correct input + MessageValidator.validateSync(request) + assertNoDuplicateGroupsNamesInProofRequest(request) - const indyFormat = options.formats.indy + const attachment = this.getFormatData(request.toJSON(), format.attachmentId) - return this.createRequestAttachment({ - id: options.id ?? uuid(), - proofRequestOptions: { - ...indyFormat, - name: indyFormat.name ?? 'proof-request', - version: indyFormat.version ?? '1.0', - nonce: indyFormat.nonce ?? (await this.wallet.generateNonce()), - }, - }) + return { attachment, format } } - public async processRequest(options: ProcessRequestOptions): Promise { - const proofRequestJson = options.requestAttachment.attachment.getDataAsJson() + public async processRequest(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const requestJson = attachment.getDataAsJson() - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - // Assert attachment - if (!proofRequest) { - throw new AriesFrameworkError( - `Missing required base64 or json encoded attachment data for presentation request with thread id ${options.record?.threadId}` - ) - } - MessageValidator.validateSync(proofRequest) + // fromJSON also validates + const proofRequest = JsonTransformer.fromJSON(requestJson, ProofRequest) // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(proofRequest) + assertNoDuplicateGroupsNamesInProofRequest(proofRequest) } - public async createPresentation( + public async acceptRequest( agentContext: AgentContext, - options: FormatCreatePresentationOptions - ): Promise { - // Extract proof request from attachment - const proofRequestJson = options.attachment.getDataAsJson() ?? null - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - // verify everything is there - if (!options.proofFormats.indy) { - throw new AriesFrameworkError('Missing indy format to create proof presentation attachment format.') - } - - const requestedCredentials = new RequestedCredentials({ - requestedAttributes: options.proofFormats.indy.requestedAttributes, - requestedPredicates: options.proofFormats.indy.requestedPredicates, - selfAttestedAttributes: options.proofFormats.indy.selfAttestedAttributes, + { proofFormats, requestAttachment, attachmentId }: ProofFormatAcceptRequestOptions + ): Promise { + const format = new ProofFormatSpec({ + format: V2_INDY_PRESENTATION, + attachmentId, }) - const proof = await this.createProof(agentContext, proofRequest, requestedCredentials) + const indyFormat = proofFormats?.indy - const attachmentId = options.id ?? uuid() + const requestJson = requestAttachment.getDataAsJson() + const proofRequest = JsonTransformer.fromJSON(requestJson, ProofRequest) - const format = new ProofFormatSpec({ - attachmentId, - format: V2_INDY_PRESENTATION, - }) + let requestedCredentials: RequestedCredentials - const attachment = new Attachment({ - id: attachmentId, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(proof), - }), - }) - return { format, attachment } - } + if (indyFormat) { + requestedCredentials = new RequestedCredentials({ + requestedAttributes: indyFormat.requestedAttributes, + requestedPredicates: indyFormat.requestedPredicates, + selfAttestedAttributes: indyFormat.selfAttestedAttributes, + }) - public async processPresentation(agentContext: AgentContext, options: ProcessPresentationOptions): Promise { - const requestFormat = options.formatAttachments.request.find( - (x) => x.format.format === V2_INDY_PRESENTATION_REQUEST - ) + // Validate to make sure user provided correct input + MessageValidator.validateSync(requestedCredentials) + } else { + const selectedCredentials = await this._selectCredentialsForRequest(agentContext, proofRequest, { + filterByNonRevocationRequirements: true, + }) - if (!requestFormat) { - throw new MissingIndyProofMessageError( - 'Missing Indy Proof Request format while trying to process an Indy proof presentation.' - ) + requestedCredentials = new RequestedCredentials({ + requestedAttributes: selectedCredentials.requestedAttributes, + requestedPredicates: selectedCredentials.requestedPredicates, + selfAttestedAttributes: selectedCredentials.selfAttestedAttributes, + }) } - const proofFormat = options.formatAttachments.presentation.find((x) => x.format.format === V2_INDY_PRESENTATION) + const proof = await this.createProof(agentContext, proofRequest, requestedCredentials) + const attachment = this.getFormatData(proof, format.attachmentId) - if (!proofFormat) { - throw new MissingIndyProofMessageError( - 'Missing Indy Proof Presentation format while trying to process an Indy proof presentation.' - ) + return { + attachment, + format, } - - return await this.verifyProof(agentContext, { request: requestFormat.attachment, proof: proofFormat.attachment }) } - public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise { - if (!options) { - throw new AriesFrameworkError('No Indy proof was provided.') - } - const proofRequestJson = options.request.getDataAsJson() ?? null - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + public async processPresentation( + agentContext: AgentContext, + { requestAttachment, attachment }: ProofFormatProcessPresentationOptions + ): Promise { + const indyVerifierService = agentContext.dependencyManager.resolve(IndyVerifierService) - const proofJson = options.proof.getDataAsJson() ?? null + const proofRequestJson = requestAttachment.getDataAsJson() + const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + const proofJson = attachment.getDataAsJson() const proof = JsonTransformer.fromJSON(proofJson, PartialProof) for (const [referent, attribute] of proof.requestedProof.revealedAttributes.entries()) { @@ -310,7 +228,7 @@ export class IndyProofFormatService extends ProofFormatService { new Set(proof.identifiers.map((i) => i.credentialDefinitionId)) ) - return await this.indyVerifierService.verifyProof(agentContext, { + return await indyVerifierService.verifyProof(agentContext, { proofRequest: proofRequest.toJSON(), proof: proofJson, schemas, @@ -318,116 +236,95 @@ export class IndyProofFormatService extends ProofFormatService { }) } - public supportsFormat(formatIdentifier: string): boolean { - const supportedFormats = [V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST, V2_INDY_PRESENTATION] - return supportedFormats.includes(formatIdentifier) + public async getCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment, proofFormats }: ProofFormatGetCredentialsForRequestOptions + ): Promise> { + const proofRequestJson = requestAttachment.getDataAsJson() + const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + + // Set default values + const { filterByNonRevocationRequirements = true } = proofFormats?.indy ?? {} + + const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, { + filterByNonRevocationRequirements, + }) + + return credentialsForRequest } - /** - * Compare presentation attrs with request/proposal attrs (auto-accept) - * - * @param proposalAttachments attachment data from the proposal - * @param requestAttachments attachment data from the request - * @returns boolean value - */ - public proposalAndRequestAreEqual( - proposalAttachments: ProofAttachmentFormat[], - requestAttachments: ProofAttachmentFormat[] - ) { - const proposalAttachment = proposalAttachments.find( - (x) => x.format.format === V2_INDY_PRESENTATION_PROPOSAL - )?.attachment - const requestAttachment = requestAttachments.find( - (x) => x.format.format === V2_INDY_PRESENTATION_REQUEST - )?.attachment - - if (!proposalAttachment) { - throw new AriesFrameworkError('Proposal message has no attachment linked to it') - } + public async selectCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment, proofFormats }: ProofFormatSelectCredentialsForRequestOptions + ): Promise> { + const proofRequestJson = requestAttachment.getDataAsJson() + const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - if (!requestAttachment) { - throw new AriesFrameworkError('Request message has no attachment linked to it') - } + // Set default values + const { filterByNonRevocationRequirements = true } = proofFormats?.indy ?? {} - const proposalAttachmentJson = proposalAttachment.getDataAsJson() - const proposalAttachmentData = JsonTransformer.fromJSON(proposalAttachmentJson, ProofRequest) + const selectedCredentials = this._selectCredentialsForRequest(agentContext, proofRequest, { + filterByNonRevocationRequirements, + }) - const requestAttachmentJson = requestAttachment.getDataAsJson() - const requestAttachmentData = JsonTransformer.fromJSON(requestAttachmentJson, ProofRequest) + return selectedCredentials + } - if ( - deepEquality(proposalAttachmentData.requestedAttributes, requestAttachmentData.requestedAttributes) && - deepEquality(proposalAttachmentData.requestedPredicates, requestAttachmentData.requestedPredicates) - ) { - return true - } + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + { proposalAttachment, requestAttachment }: ProofFormatAutoRespondProposalOptions + ): Promise { + const proposalJson = proposalAttachment.getDataAsJson() + const requestJson = requestAttachment.getDataAsJson() + + const areRequestsEqual = areIndyProofRequestsEqual(proposalJson, requestJson) + agentContext.config.logger.debug(`Indy request and proposal are are equal: ${areRequestsEqual}`, { + proposalJson, + requestJson, + }) - return false + return areRequestsEqual } - /** - * Build credential definitions object needed to create and verify proof objects. - * - * Creates object with `{ credentialDefinitionId: CredentialDefinition }` mapping - * - * @param credentialDefinitionIds List of credential definition ids - * @returns Object containing credential definitions for specified credential definition ids - * - */ - private async getCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { - const credentialDefinitions: { [key: string]: CredDef } = {} + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + { proposalAttachment, requestAttachment }: ProofFormatAutoRespondRequestOptions + ): Promise { + const proposalJson = proposalAttachment.getDataAsJson() + const requestJson = requestAttachment.getDataAsJson() - for (const credDefId of credentialDefinitionIds) { - const credDef = await this.ledgerService.getCredentialDefinition(agentContext, credDefId) - credentialDefinitions[credDefId] = credDef - } + return areIndyProofRequestsEqual(proposalJson, requestJson) + } - return credentialDefinitions + public async shouldAutoRespondToPresentation(): Promise { + // The presentation is already verified in processPresentation, so we can just return true here. + // It's only an ack, so it's just that we received the presentation. + return true } - public async getRequestedCredentialsForProofRequest( + public supportsFormat(formatIdentifier: string): boolean { + const supportedFormats = [V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST, V2_INDY_PRESENTATION] + return supportedFormats.includes(formatIdentifier) + } + + private async _getCredentialsForRequest( agentContext: AgentContext, - options: GetRequestedCredentialsFormat - ): Promise> { - const retrievedCredentials = new RetrievedCredentials({}) - const { attachment, presentationProposal } = options - const filterByNonRevocationRequirements = options.config?.filterByNonRevocationRequirements + proofRequest: ProofRequest, + options: IndyGetCredentialsForProofRequestOptions + ): Promise { + const credentialsForProofRequest: IndyCredentialsForProofRequest = { + attributes: {}, + predicates: {}, + } - const proofRequestJson = attachment.getDataAsJson() ?? null - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) + const proofRequestJson = proofRequest.toJSON() for (const [referent, requestedAttribute] of proofRequest.requestedAttributes.entries()) { - let credentialMatch: IndyCredential[] = [] - const credentials = await this.getCredentialsForProofRequest(agentContext, proofRequest, referent) + const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequestJson, referent) - // If we have exactly one credential, or no proposal to pick preferences - // on the credentials to use, we will use the first one - if (credentials.length === 1 || !presentationProposal) { - credentialMatch = credentials - } - // If we have a proposal we will use that to determine the credentials to use - else { - const names = requestedAttribute.names ?? [requestedAttribute.name] - - // Find credentials that matches all parameters from the proposal - credentialMatch = credentials.filter((credential) => { - const { attributes, credentialDefinitionId } = credential.credentialInfo - - // Check if credentials matches all parameters from proposal - return names.every((name) => - presentationProposal.attributes.find( - (a) => - a.name === name && - a.credentialDefinitionId === credentialDefinitionId && - (!a.value || a.value === attributes[name]) - ) - ) - }) - } - - retrievedCredentials.requestedAttributes[referent] = sortRequestedCredentials( + credentialsForProofRequest.attributes[referent] = sortRequestedCredentials( await Promise.all( - credentialMatch.map(async (credential: IndyCredential) => { + credentials.map(async (credential: IndyCredential) => { const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { proofRequest, requestedItem: requestedAttribute, @@ -447,17 +344,17 @@ export class IndyProofFormatService extends ProofFormatService { // We only attach revoked state if non-revocation is requested. So if revoked is true it means // the credential is not applicable to the proof request - if (filterByNonRevocationRequirements) { - retrievedCredentials.requestedAttributes[referent] = retrievedCredentials.requestedAttributes[referent].filter( + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter( (r) => !r.revoked ) } } for (const [referent, requestedPredicate] of proofRequest.requestedPredicates.entries()) { - const credentials = await this.getCredentialsForProofRequest(agentContext, proofRequest, referent) + const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequestJson, referent) - retrievedCredentials.requestedPredicates[referent] = sortRequestedCredentials( + credentialsForProofRequest.predicates[referent] = sortRequestedCredentials( await Promise.all( credentials.map(async (credential) => { const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { @@ -478,68 +375,64 @@ export class IndyProofFormatService extends ProofFormatService { // We only attach revoked state if non-revocation is requested. So if revoked is true it means // the credential is not applicable to the proof request - if (filterByNonRevocationRequirements) { - retrievedCredentials.requestedPredicates[referent] = retrievedCredentials.requestedPredicates[referent].filter( + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter( (r) => !r.revoked ) } } - return { - proofFormats: { - indy: retrievedCredentials, - }, - } + return credentialsForProofRequest } - private async getCredentialsForProofRequest( + private async _selectCredentialsForRequest( agentContext: AgentContext, proofRequest: ProofRequest, - attributeReferent: string - ): Promise { - const credentialsJson = await this.indyHolderService.getCredentialsForProofRequest(agentContext, { - proofRequest: proofRequest.toJSON(), - attributeReferent, - }) - - return JsonTransformer.fromJSON(credentialsJson, IndyCredential) as unknown as IndyCredential[] - } - - public async autoSelectCredentialsForProofRequest( - options: FormatRetrievedCredentialOptions<[IndyProofFormat]> - ): Promise> { - const { proofFormats } = options - const indy = proofFormats.indy - - if (!indy) { - throw new AriesFrameworkError('No indy options provided') + options: IndyGetCredentialsForProofRequestOptions + ): Promise { + const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, options) + + const selectedCredentials: IndySelectedCredentialsForProofRequest = { + requestedAttributes: {}, + requestedPredicates: {}, + selfAttestedAttributes: {}, } - const requestedCredentials = new RequestedCredentials({}) - - Object.keys(indy.requestedAttributes).forEach((attributeName) => { - const attributeArray = indy.requestedAttributes[attributeName] + Object.keys(credentialsForRequest.attributes).forEach((attributeName) => { + const attributeArray = credentialsForRequest.attributes[attributeName] if (attributeArray.length === 0) { throw new AriesFrameworkError('Unable to automatically select requested attributes.') - } else { - requestedCredentials.requestedAttributes[attributeName] = attributeArray[0] } + + selectedCredentials.requestedAttributes[attributeName] = attributeArray[0] }) - Object.keys(indy.requestedPredicates).forEach((attributeName) => { - if (indy.requestedPredicates[attributeName].length === 0) { + Object.keys(credentialsForRequest.predicates).forEach((attributeName) => { + if (credentialsForRequest.predicates[attributeName].length === 0) { throw new AriesFrameworkError('Unable to automatically select requested predicates.') } else { - requestedCredentials.requestedPredicates[attributeName] = indy.requestedPredicates[attributeName][0] + selectedCredentials.requestedPredicates[attributeName] = credentialsForRequest.predicates[attributeName][0] } }) - return { - proofFormats: { - indy: requestedCredentials, - }, - } + return selectedCredentials + } + + private async getCredentialsForProofRequestReferent( + agentContext: AgentContext, + // pass as json to prevent having to transform to json on every call + proofRequestJson: IndyProofRequest, + attributeReferent: string + ): Promise { + const holderService = agentContext.dependencyManager.resolve(IndyHolderService) + + const credentialsJson = await holderService.getCredentialsForProofRequest(agentContext, { + proofRequest: proofRequestJson, + attributeReferent, + }) + + return JsonTransformer.fromJSON(credentialsJson, IndyCredential) as unknown as IndyCredential[] } /** @@ -552,16 +445,40 @@ export class IndyProofFormatService extends ProofFormatService { * */ private async getSchemas(agentContext: AgentContext, schemaIds: Set) { + const ledgerService = agentContext.dependencyManager.resolve(IndyLedgerService) + const schemas: { [key: string]: Schema } = {} for (const schemaId of schemaIds) { - const schema = await this.ledgerService.getSchema(agentContext, schemaId) + const schema = await ledgerService.getSchema(agentContext, schemaId) schemas[schemaId] = schema } return schemas } + /** + * Build credential definitions object needed to create and verify proof objects. + * + * Creates object with `{ credentialDefinitionId: CredentialDefinition }` mapping + * + * @param credentialDefinitionIds List of credential definition ids + * @returns Object containing credential definitions for specified credential definition ids + * + */ + private async getCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { + const ledgerService = agentContext.dependencyManager.resolve(IndyLedgerService) + + const credentialDefinitions: { [key: string]: CredDef } = {} + + for (const credDefId of credentialDefinitionIds) { + const credDef = await ledgerService.getCredentialDefinition(agentContext, credDefId) + credentialDefinitions[credDefId] = credDef + } + + return credentialDefinitions + } + /** * Create indy proof from a given proof request and requested credential object. * @@ -574,6 +491,8 @@ export class IndyProofFormatService extends ProofFormatService { proofRequest: ProofRequest, requestedCredentials: RequestedCredentials ): Promise { + const indyHolderService = agentContext.dependencyManager.resolve(IndyHolderService) + const credentialObjects = await Promise.all( [ ...Object.values(requestedCredentials.requestedAttributes), @@ -582,7 +501,7 @@ export class IndyProofFormatService extends ProofFormatService { if (c.credentialInfo) { return c.credentialInfo } - const credentialInfo = await this.indyHolderService.getCredential(agentContext, c.credentialId) + const credentialInfo = await indyHolderService.getCredential(agentContext, c.credentialId) return JsonTransformer.fromJSON(credentialInfo, IndyCredentialInfo) }) ) @@ -593,7 +512,7 @@ export class IndyProofFormatService extends ProofFormatService { new Set(credentialObjects.map((c) => c.credentialDefinitionId)) ) - return await this.indyHolderService.createProof(agentContext, { + return await indyHolderService.createProof(agentContext, { proofRequest: proofRequest.toJSON(), requestedCredentials: requestedCredentials, schemas, @@ -601,25 +520,6 @@ export class IndyProofFormatService extends ProofFormatService { }) } - public async createProofRequestFromProposal(options: CreatePresentationFormatsOptions): Promise { - const proofRequestJson = options.presentationAttachment.getDataAsJson() - - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - // Assert attachment - if (!proofRequest) { - throw new AriesFrameworkError(`Missing required base64 or json encoded attachment data for presentation request.`) - } - MessageValidator.validateSync(proofRequest) - - // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(proofRequest) - - return { - indy: proofRequest, - } - } - private async getRevocationStatusForRequestedItem( agentContext: AgentContext, { @@ -632,13 +532,15 @@ export class IndyProofFormatService extends ProofFormatService { credential: IndyCredential } ) { + const indyRevocationService = agentContext.dependencyManager.resolve(IndyRevocationService) + const requestNonRevoked = requestedItem.nonRevoked ?? proofRequest.nonRevoked const credentialRevocationId = credential.credentialInfo.credentialRevocationId const revocationRegistryId = credential.credentialInfo.revocationRegistryId // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { - this.logger.trace( + agentContext.config.logger.trace( `Presentation is requesting proof of non revocation, getting revocation status for credential`, { requestNonRevoked, @@ -648,7 +550,7 @@ export class IndyProofFormatService extends ProofFormatService { ) // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals - const status = await this.indyRevocationService.getRevocationStatus( + const status = await indyRevocationService.getRevocationStatus( agentContext, credentialRevocationId, revocationRegistryId, @@ -661,89 +563,22 @@ export class IndyProofFormatService extends ProofFormatService { return { revoked: undefined, deltaTimestamp: undefined } } - public async createRequestFromPreview(indyFormat: IndyProposeProofFormat): Promise { - const preview = new PresentationPreview({ - attributes: indyFormat.attributes, - predicates: indyFormat.predicates, - }) - - const proofRequest = await this.createReferentForProofRequest(indyFormat, preview) - - return proofRequest - } - - public async createReferentForProofRequest( - indyFormat: IndyProposeProofFormat, - preview: PresentationPreview - ): Promise { - const proofRequest = new ProofRequest({ - name: indyFormat.name ?? 'proof-request', - version: indyFormat.version ?? '1.0', - nonce: indyFormat.nonce ?? (await this.wallet.generateNonce()), + /** + * Returns an object of type {@link Attachment} for use in credential exchange messages. + * It looks up the correct format identifier and encodes the data as a base64 attachment. + * + * @param data The data to include in the attach object + * @param id the attach id from the formats component of the message + */ + private getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ + id, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(data), + }), }) - /** - * Create mapping of attributes by referent. This required the - * attributes to come from the same credential. - * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#referent - * - * { - * "referent1": [Attribute1, Attribute2], - * "referent2": [Attribute3] - * } - */ - const attributesByReferent: Record = {} - for (const proposedAttributes of preview.attributes) { - if (!proposedAttributes.referent) proposedAttributes.referent = uuid() - - const referentAttributes = attributesByReferent[proposedAttributes.referent] - - // Referent key already exist, add to list - if (referentAttributes) { - referentAttributes.push(proposedAttributes) - } - - // Referent key does not exist yet, create new entry - else { - attributesByReferent[proposedAttributes.referent] = [proposedAttributes] - } - } - - // Transform attributes by referent to requested attributes - for (const [referent, proposedAttributes] of Object.entries(attributesByReferent)) { - // Either attributeName or attributeNames will be undefined - const attributeName = proposedAttributes.length == 1 ? proposedAttributes[0].name : undefined - const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map((a) => a.name) : undefined - - const requestedAttribute = new ProofAttributeInfo({ - name: attributeName, - names: attributeNames, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: proposedAttributes[0].credentialDefinitionId, - }), - ], - }) - - proofRequest.requestedAttributes.set(referent, requestedAttribute) - } - - // Transform proposed predicates to requested predicates - for (const proposedPredicate of preview.predicates) { - const requestedPredicate = new ProofPredicateInfo({ - name: proposedPredicate.name, - predicateType: proposedPredicate.predicate, - predicateValue: proposedPredicate.threshold, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: proposedPredicate.credentialDefinitionId, - }), - ], - }) - - proofRequest.requestedPredicates.set(uuid(), requestedPredicate) - } - - return proofRequest + return attachment } } diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts deleted file mode 100644 index bb78139bb7..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatsServiceOptions.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { IndyRequestedCredentialsFormat } from './IndyProofFormat' -import type { ProofAttributeInfo } from '.././indy/models/ProofAttributeInfo' -import type { ProofPredicateInfo } from '.././indy/models/ProofPredicateInfo' -import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { IndyRevocationInterval } from '../../../credentials' -import type { GetRequestedCredentialsConfig } from '../../models/GetRequestedCredentialsConfig' -import type { PresentationPreview } from '../../protocol/v1/models/V1PresentationPreview' -import type { ProofExchangeRecord } from '../../repository/ProofExchangeRecord' - -export type IndyPresentationProofFormat = IndyRequestedCredentialsFormat - -export interface IndyRequestProofFormat { - name?: string - version?: string - nonce?: string - nonRevoked?: IndyRevocationInterval - ver?: '1.0' | '2.0' - requestedAttributes?: Record | Map - requestedPredicates?: Record | Map -} - -export interface IndyVerifyProofFormat { - proofJson: Attachment - proofRequest: Attachment -} - -export interface GetRequestedCredentialsFormat { - attachment: Attachment - presentationProposal?: PresentationPreview - config?: GetRequestedCredentialsConfig -} - -export interface IndyProofRequestFromProposalOptions { - proofRecord: ProofExchangeRecord - name?: string - version?: string - nonce?: string -} diff --git a/packages/core/src/modules/proofs/__tests__/groupKeys.ts b/packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts similarity index 79% rename from packages/core/src/modules/proofs/__tests__/groupKeys.ts rename to packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts index e20144792f..3d62914aca 100644 --- a/packages/core/src/modules/proofs/__tests__/groupKeys.ts +++ b/packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts @@ -1,9 +1,9 @@ -import type { IndyProofFormat } from '../formats/indy/IndyProofFormat' -import type { GetFormatDataReturn } from '../models/ProofServiceOptions' +import type { GetProofFormatDataReturn } from '../../../protocol/ProofProtocolOptions' +import type { IndyProofFormat } from '../IndyProofFormat' -import { AriesFrameworkError } from '../../../error' +import { AriesFrameworkError } from '../../../../../error' -export function getGroupKeysFromIndyProofFormatData(formatData: GetFormatDataReturn<[IndyProofFormat]>): { +export function getGroupKeysFromIndyProofFormatData(formatData: GetProofFormatDataReturn<[IndyProofFormat]>): { proposeKey1: string proposeKey2: string requestKey1: string diff --git a/packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts b/packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts new file mode 100644 index 0000000000..db6435670c --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts @@ -0,0 +1,541 @@ +import type { default as Indy } from 'indy-sdk' + +import { AriesFrameworkError } from '../../../../../error' +import { AttributeFilter, PredicateType, ProofAttributeInfo, ProofPredicateInfo, ProofRequest } from '../models' +import { areIndyProofRequestsEqual, assertNoDuplicateGroupsNamesInProofRequest } from '../util' + +const proofRequest = { + name: 'Proof Request', + version: '1.0.0', + nonce: 'nonce', + ver: '1.0', + non_revoked: {}, + requested_attributes: { + a: { + names: ['name1', 'name2'], + restrictions: [ + { + cred_def_id: 'cred_def_id1', + }, + { + schema_id: 'schema_id', + }, + ], + }, + }, + requested_predicates: { + p: { + name: 'Hello', + p_type: '<', + p_value: 10, + restrictions: [ + { + cred_def_id: 'string2', + }, + { + cred_def_id: 'string', + }, + ], + }, + }, +} satisfies Indy.IndyProofRequest + +const credDefId = '9vPXgSpQJPkJEALbLXueBp:3:CL:57753:tag1' +const nonce = 'testtesttest12345' + +describe('IndyProofFormat | util', () => { + describe('assertNoDuplicateGroupsNamesInProofRequest', () => { + test('attribute names match', () => { + const attributes = { + age1: new ProofAttributeInfo({ + name: 'age', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + age2: new ProofAttributeInfo({ + name: 'age', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const proofRequest = new ProofRequest({ + name: 'proof-request', + version: '1.0', + nonce, + requestedAttributes: attributes, + }) + + expect(() => assertNoDuplicateGroupsNamesInProofRequest(proofRequest)).not.toThrow() + }) + + test('attribute names match with predicates name', () => { + const attributes = { + attrib: new ProofAttributeInfo({ + name: 'age', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const predicates = { + predicate: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: credDefId, + }), + ], + }), + } + + const proofRequest = new ProofRequest({ + name: 'proof-request', + version: '1.0', + nonce, + requestedAttributes: attributes, + requestedPredicates: predicates, + }) + + expect(() => assertNoDuplicateGroupsNamesInProofRequest(proofRequest)).toThrowError(AriesFrameworkError) + }) + }) + describe('areIndyProofRequestsEqual', () => { + test('does not compare name, ver, version and nonce', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + name: 'Proof Request 2', + version: '2.0.0', + nonce: 'nonce2', + ver: '2.0', + }) + ).toBe(true) + }) + + test('check top level non_revocation interval', () => { + // empty object is semantically equal to undefined + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + non_revoked: {}, + }) + ).toBe(true) + + // properties inside object are different + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + non_revoked: { + to: 5, + }, + }, + { + ...proofRequest, + non_revoked: { + from: 5, + }, + } + ) + ).toBe(false) + + // One has non_revoked, other doesn't + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + non_revoked: { + from: 5, + }, + }) + ).toBe(false) + }) + + test('ignores attribute group name differences', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + b: proofRequest.requested_attributes.a, + }, + }) + ).toBe(true) + }) + + test('ignores attribute restriction order', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: [...proofRequest.requested_attributes.a.restrictions].reverse(), + }, + }, + }) + ).toBe(true) + }) + + test('ignores attribute restriction undefined vs empty array', () => { + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: undefined, + }, + }, + }, + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: [], + }, + }, + } + ) + ).toBe(true) + }) + + test('ignores attribute names order', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + names: ['name2', 'name1'], + }, + }, + }) + ).toBe(true) + }) + + test('checks attribute non_revocation interval', () => { + // empty object is semantically equal to undefined + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: {}, + }, + }, + }) + ).toBe(true) + + // properties inside object are different + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: { + to: 5, + }, + }, + }, + }, + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: { + from: 5, + }, + }, + }, + } + ) + ).toBe(false) + + // One has non_revoked, other doesn't + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: { + from: 5, + }, + }, + }, + }) + ).toBe(false) + }) + + test('checks attribute restriction differences', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: [ + { + cred_def_id: 'cred_def_id1', + }, + { + cred_def_id: 'cred_def_id2', + }, + ], + }, + }, + }) + ).toBe(false) + }) + + test('checks attribute name differences', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + names: ['name3'], + }, + }, + }) + ).toBe(false) + + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + name: 'name3', + names: undefined, + }, + }, + }) + ).toBe(false) + }) + + test('allows names with one value to be same as name property', () => { + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + name: 'name1', + }, + }, + }, + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + names: ['name1'], + }, + }, + } + ) + ).toBe(true) + + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + name: 'name1', + }, + }, + }, + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + names: ['name2'], + }, + }, + } + ) + ).toBe(false) + }) + + test('ignores predicate group name differences', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + a: proofRequest.requested_predicates.p, + }, + }) + ).toBe(true) + }) + + test('ignores predicate restriction order', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: [...proofRequest.requested_predicates.p.restrictions].reverse(), + }, + }, + }) + ).toBe(true) + }) + + test('ignores predicate restriction undefined vs empty array', () => { + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: undefined, + }, + }, + }, + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: [], + }, + }, + } + ) + ).toBe(true) + }) + + test('checks predicate restriction differences', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: [ + { + cred_def_id: 'cred_def_id1', + }, + { + cred_def_id: 'cred_def_id2', + }, + ], + }, + }, + }) + ).toBe(false) + }) + + test('checks predicate name differences', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + name: 'name3', + }, + }, + }) + ).toBe(false) + }) + + test('checks predicate non_revocation interval', () => { + // empty object is semantically equal to undefined + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: {}, + }, + }, + }) + ).toBe(true) + + // properties inside object are different + expect( + areIndyProofRequestsEqual( + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: { + to: 5, + }, + }, + }, + }, + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: { + from: 5, + }, + }, + }, + } + ) + ).toBe(false) + + // One has non_revoked, other doesn't + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: { + from: 5, + }, + }, + }, + }) + ).toBe(false) + }) + + test('checks predicate p_type and p_value', () => { + expect( + areIndyProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + p_type: '<', + p_value: 134134, + }, + }, + }) + ).toBe(false) + }) + }) +}) diff --git a/packages/core/src/modules/proofs/formats/indy/errors/MissingIndyProofMessageError.ts b/packages/core/src/modules/proofs/formats/indy/errors/MissingIndyProofMessageError.ts deleted file mode 100644 index 2ab9c3f15e..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/errors/MissingIndyProofMessageError.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' - -export class MissingIndyProofMessageError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/formats/indy/errors/index.ts b/packages/core/src/modules/proofs/formats/indy/errors/index.ts index 0f0b302726..7b2373bb66 100644 --- a/packages/core/src/modules/proofs/formats/indy/errors/index.ts +++ b/packages/core/src/modules/proofs/formats/indy/errors/index.ts @@ -1,2 +1 @@ export * from './InvalidEncodedValueError' -export * from './MissingIndyProofMessageError' diff --git a/packages/core/src/modules/proofs/formats/indy/index.ts b/packages/core/src/modules/proofs/formats/indy/index.ts index c94afb8629..185c2f8afc 100644 --- a/packages/core/src/modules/proofs/formats/indy/index.ts +++ b/packages/core/src/modules/proofs/formats/indy/index.ts @@ -1,4 +1,2 @@ -export * from './errors' -export * from './models' export * from './IndyProofFormat' -export * from './IndyProofFormatsServiceOptions' +export * from './IndyProofFormatService' diff --git a/packages/core/src/modules/proofs/protocol/v1/models/PartialProof.ts b/packages/core/src/modules/proofs/formats/indy/models/PartialProof.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/models/PartialProof.ts rename to packages/core/src/modules/proofs/formats/indy/models/PartialProof.ts diff --git a/packages/core/src/modules/proofs/protocol/v1/models/ProofAttribute.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofAttribute.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/models/ProofAttribute.ts rename to packages/core/src/modules/proofs/formats/indy/models/ProofAttribute.ts diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts index 4bf1f136b0..a67c8425ae 100644 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts @@ -5,13 +5,15 @@ import { IndyRevocationInterval } from '../../../../credentials' import { AttributeFilter } from './AttributeFilter' +export type ProofAttributeInfoOptions = ProofAttributeInfo + export class ProofAttributeInfo { - public constructor(options: ProofAttributeInfo) { + public constructor(options: ProofAttributeInfoOptions) { if (options) { this.name = options.name this.names = options.names - this.nonRevoked = options.nonRevoked - this.restrictions = options.restrictions + this.nonRevoked = options.nonRevoked ? new IndyRevocationInterval(options.nonRevoked) : undefined + this.restrictions = options.restrictions?.map((r) => new AttributeFilter(r)) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/models/ProofIdentifier.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofIdentifier.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/models/ProofIdentifier.ts rename to packages/core/src/modules/proofs/formats/indy/models/ProofIdentifier.ts diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts index 8f246746bf..48083fc54d 100644 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts @@ -6,13 +6,22 @@ import { IndyRevocationInterval } from '../../../../credentials' import { AttributeFilter } from './AttributeFilter' import { PredicateType } from './PredicateType' +export interface ProofPredicateInfoOptions { + name: string + // Also allow string value of the enum as input, to make it easier to use in the API + predicateType: PredicateType | `${PredicateType}` + predicateValue: number + nonRevoked?: IndyRevocationInterval + restrictions?: AttributeFilter[] +} + export class ProofPredicateInfo { - public constructor(options: ProofPredicateInfo) { + public constructor(options: ProofPredicateInfoOptions) { if (options) { this.name = options.name - this.nonRevoked = options.nonRevoked - this.restrictions = options.restrictions - this.predicateType = options.predicateType + this.nonRevoked = options.nonRevoked ? new IndyRevocationInterval(options.nonRevoked) : undefined + this.restrictions = options.restrictions?.map((r) => new AttributeFilter(r)) + this.predicateType = options.predicateType as PredicateType this.predicateValue = options.predicateValue } } diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts index 224169c864..b2d5cf83cc 100644 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts @@ -1,3 +1,5 @@ +import type { ProofAttributeInfoOptions } from './ProofAttributeInfo' +import type { ProofPredicateInfoOptions } from './ProofPredicateInfo' import type { IndyProofRequest } from 'indy-sdk' import { Expose, Type } from 'class-transformer' @@ -16,8 +18,8 @@ export interface ProofRequestOptions { nonce: string nonRevoked?: IndyRevocationInterval ver?: '1.0' | '2.0' - requestedAttributes?: Record | Map - requestedPredicates?: Record | Map + requestedAttributes?: Record + requestedPredicates?: Record } /** @@ -31,17 +33,22 @@ export class ProofRequest { this.name = options.name this.version = options.version this.nonce = options.nonce - this.requestedAttributes = options.requestedAttributes - ? options.requestedAttributes instanceof Map - ? options.requestedAttributes - : new Map(Object.entries(options.requestedAttributes)) - : new Map() - this.requestedPredicates = options.requestedPredicates - ? options.requestedPredicates instanceof Map - ? options.requestedPredicates - : new Map(Object.entries(options.requestedPredicates)) - : new Map() - this.nonRevoked = options.nonRevoked + + this.requestedAttributes = new Map( + Object.entries(options.requestedAttributes ?? {}).map(([key, attribute]) => [ + key, + new ProofAttributeInfo(attribute), + ]) + ) + + this.requestedPredicates = new Map( + Object.entries(options.requestedPredicates ?? {}).map(([key, predicate]) => [ + key, + new ProofPredicateInfo(predicate), + ]) + ) + + this.nonRevoked = options.nonRevoked ? new IndyRevocationInterval(options.nonRevoked) : undefined this.ver = options.ver } } diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts index 048a89cf82..21a1e9a1c3 100644 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts @@ -1,18 +1,28 @@ +import type { IndyCredentialInfoOptions } from '../../../../credentials/formats/indy/models/IndyCredentialInfo' + import { Exclude, Expose } from 'class-transformer' import { IsBoolean, IsInt, IsOptional, IsString } from 'class-validator' import { IndyCredentialInfo } from '../../../../credentials/formats/indy/models/IndyCredentialInfo' +export interface RequestedAttributeOptions { + credentialId: string + timestamp?: number + revealed: boolean + credentialInfo?: IndyCredentialInfoOptions + revoked?: boolean +} + /** * Requested Attribute for Indy proof creation */ export class RequestedAttribute { - public constructor(options: RequestedAttribute) { + public constructor(options: RequestedAttributeOptions) { if (options) { this.credentialId = options.credentialId this.timestamp = options.timestamp this.revealed = options.revealed - this.credentialInfo = options.credentialInfo + this.credentialInfo = options.credentialInfo ? new IndyCredentialInfo(options.credentialInfo) : undefined this.revoked = options.revoked } } diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts index b2824bf7bd..f515a82dee 100644 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts @@ -1,3 +1,5 @@ +import type { RequestedAttributeOptions } from './RequestedAttribute' +import type { RequestedPredicateOptions } from './RequestedPredicate' import type { IndyRequestedCredentials } from 'indy-sdk' import { Expose } from 'class-transformer' @@ -10,8 +12,8 @@ import { RequestedAttribute } from './RequestedAttribute' import { RequestedPredicate } from './RequestedPredicate' export interface IndyRequestedCredentialsOptions { - requestedAttributes?: Record - requestedPredicates?: Record + requestedAttributes?: Record + requestedPredicates?: Record selfAttestedAttributes?: Record } @@ -23,8 +25,24 @@ export interface IndyRequestedCredentialsOptions { export class RequestedCredentials { public constructor(options: IndyRequestedCredentialsOptions = {}) { if (options) { - this.requestedAttributes = options.requestedAttributes ?? {} - this.requestedPredicates = options.requestedPredicates ?? {} + const { requestedAttributes, requestedPredicates } = options + + // Create RequestedAttribute objects from options + this.requestedAttributes = {} + if (requestedAttributes) { + Object.keys(requestedAttributes).forEach((key) => { + this.requestedAttributes[key] = new RequestedAttribute(requestedAttributes[key]) + }) + } + + // Create RequestedPredicate objects from options + this.requestedPredicates = {} + if (requestedPredicates) { + Object.keys(requestedPredicates).forEach((key) => { + this.requestedPredicates[key] = new RequestedPredicate(requestedPredicates[key]) + }) + } + this.selfAttestedAttributes = options.selfAttestedAttributes ?? {} } } diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts index 9109b51a4d..d8f5e2d9d2 100644 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts @@ -1,17 +1,26 @@ +import type { IndyCredentialInfoOptions } from '../../../../credentials' + import { Exclude, Expose } from 'class-transformer' import { IsInt, IsOptional, IsString } from 'class-validator' import { IndyCredentialInfo } from '../../../../credentials' +export interface RequestedPredicateOptions { + credentialId: string + timestamp?: number + credentialInfo?: IndyCredentialInfoOptions + revoked?: boolean +} + /** * Requested Predicate for Indy proof creation */ export class RequestedPredicate { - public constructor(options: RequestedPredicate) { + public constructor(options: RequestedPredicateOptions) { if (options) { this.credentialId = options.credentialId this.timestamp = options.timestamp - this.credentialInfo = options.credentialInfo + this.credentialInfo = options.credentialInfo ? new IndyCredentialInfo(options.credentialInfo) : undefined this.revoked = options.revoked } } diff --git a/packages/core/src/modules/proofs/protocol/v1/models/RequestedProof.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedProof.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/models/RequestedProof.ts rename to packages/core/src/modules/proofs/formats/indy/models/RequestedProof.ts diff --git a/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts b/packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts similarity index 87% rename from packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts rename to packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts index fbfab93e5f..9d52625ece 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofRequest.test.ts +++ b/packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts @@ -1,7 +1,7 @@ -import { ClassValidationError } from '../../../error/ClassValidationError' -import { JsonTransformer } from '../../../utils/JsonTransformer' -import { MessageValidator } from '../../../utils/MessageValidator' -import { ProofRequest } from '../formats/indy/models/ProofRequest' +import { ClassValidationError } from '../../../../../../error/ClassValidationError' +import { JsonTransformer } from '../../../../../../utils/JsonTransformer' +import { MessageValidator } from '../../../../../../utils/MessageValidator' +import { ProofRequest } from '../ProofRequest' describe('ProofRequest', () => { it('should successfully validate if the proof request JSON contains a valid structure', async () => { diff --git a/packages/core/src/modules/proofs/formats/indy/util.ts b/packages/core/src/modules/proofs/formats/indy/util.ts new file mode 100644 index 0000000000..f1c3df2a16 --- /dev/null +++ b/packages/core/src/modules/proofs/formats/indy/util.ts @@ -0,0 +1,266 @@ +import type { V1PresentationPreviewAttributeOptions, V1PresentationPreviewPredicateOptions } from '../../protocol' +import type { default as Indy } from 'indy-sdk' + +import { AriesFrameworkError } from '../../../../error' +import { areObjectsEqual } from '../../../../utils' +import { uuid } from '../../../../utils/uuid' + +import { ProofAttributeInfo, ProofPredicateInfo, ProofRequest } from './models' + +export function createRequestFromPreview({ + name, + version, + nonce, + attributes, + predicates, +}: { + name: string + version: string + nonce: string + attributes: V1PresentationPreviewAttributeOptions[] + predicates: V1PresentationPreviewPredicateOptions[] +}): ProofRequest { + const proofRequest = new ProofRequest({ + name, + version, + nonce, + }) + + /** + * Create mapping of attributes by referent. This required the + * attributes to come from the same credential. + * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#referent + * + * { + * "referent1": [Attribute1, Attribute2], + * "referent2": [Attribute3] + * } + */ + const attributesByReferent: Record = {} + for (const proposedAttributes of attributes ?? []) { + if (!proposedAttributes.referent) proposedAttributes.referent = uuid() + + const referentAttributes = attributesByReferent[proposedAttributes.referent] + + // Referent key already exist, add to list + if (referentAttributes) { + referentAttributes.push(proposedAttributes) + } + + // Referent key does not exist yet, create new entry + else { + attributesByReferent[proposedAttributes.referent] = [proposedAttributes] + } + } + + // Transform attributes by referent to requested attributes + for (const [referent, proposedAttributes] of Object.entries(attributesByReferent)) { + // Either attributeName or attributeNames will be undefined + const attributeName = proposedAttributes.length === 1 ? proposedAttributes[0].name : undefined + const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map((a) => a.name) : undefined + + const requestedAttribute = new ProofAttributeInfo({ + name: attributeName, + names: attributeNames, + restrictions: [ + { + credentialDefinitionId: proposedAttributes[0].credentialDefinitionId, + }, + ], + }) + + proofRequest.requestedAttributes.set(referent, requestedAttribute) + } + + // Transform proposed predicates to requested predicates + for (const proposedPredicate of predicates ?? []) { + const requestedPredicate = new ProofPredicateInfo({ + name: proposedPredicate.name, + predicateType: proposedPredicate.predicate, + predicateValue: proposedPredicate.threshold, + restrictions: [ + { + credentialDefinitionId: proposedPredicate.credentialDefinitionId, + }, + ], + }) + + proofRequest.requestedPredicates.set(uuid(), requestedPredicate) + } + + return proofRequest +} + +/** + * Checks whether two `names` arrays are equal. The order of the names doesn't matter. + */ +function areNamesEqual({ + nameA, + namesA, + nameB, + namesB, +}: { + namesA?: string[] + nameA?: string + namesB?: string[] + nameB?: string +}) { + const namesACombined = nameA ? [nameA] : namesA + const namesBCombined = nameB ? [nameB] : namesB + + // Filter out case where both are not set (invalid) + if (!namesACombined || !namesBCombined) return false + + // Check if there are any duplicates + if (new Set(namesACombined).size !== namesACombined.length || new Set(namesBCombined).size !== namesBCombined.length) + return false + + // Check if the number of names is equal between A & B + if (namesACombined.length !== namesBCombined.length) return false + + return namesACombined.every((a) => namesBCombined.includes(a)) +} + +/** + * Checks whether two proof requests are semantically equal. The `name`, `version` and `nonce`, `ver` fields are ignored. + * In addition the group names don't have to be the same between the different requests. + */ +export function areIndyProofRequestsEqual(requestA: Indy.IndyProofRequest, requestB: Indy.IndyProofRequest): boolean { + // Check if the top-level non-revocation interval is equal + if (!isNonRevokedEqual(requestA.non_revoked, requestB.non_revoked)) return false + + const attributeAList = Object.values(requestA.requested_attributes) + const attributeBList = Object.values(requestB.requested_attributes) + + // Check if the number of attribute groups is equal in both requests + if (attributeAList.length !== attributeBList.length) return false + + const predicatesA = Object.values(requestA.requested_predicates) + const predicatesB = Object.values(requestB.requested_predicates) + + if (predicatesA.length !== predicatesB.length) return false + + // Check if all attribute groups in A are also in B + const attributesMatch = attributeAList.every((a) => { + // find an attribute in B that matches this attribute + const bIndex = attributeBList.findIndex((b) => { + return ( + // Check if name and names are equal + areNamesEqual({ + nameB: b.name, + namesB: b.names, + nameA: a.name, + namesA: a.names, + }) && + isNonRevokedEqual(a.non_revoked, b.non_revoked) && + areRestrictionsEqual(a.restrictions, b.restrictions) + ) + }) + + // Match found + if (bIndex !== -1) { + attributeBList.splice(bIndex, 1) + return true + } + + // Match not found + return false + }) + + if (!attributesMatch) return false + + const predicatesMatch = predicatesA.every((a) => { + // find a predicate in B that matches this predicate + const bIndex = predicatesB.findIndex((b) => { + return ( + a.name === b.name && + a.p_type === b.p_type && + a.p_value === b.p_value && + isNonRevokedEqual(a.non_revoked, b.non_revoked) && + areRestrictionsEqual(a.restrictions, b.restrictions) + ) + }) + + if (bIndex !== -1) { + predicatesB.splice(bIndex, 1) + return true + } + + return false + }) + + if (!predicatesMatch) return false + + return true +} + +/** + * Checks whether two non-revocation intervals are semantically equal. They are considered equal if: + * - Both are undefined + * - Both are empty objects + * - One if undefined and the other is an empty object + * - Both have the same from and to values + */ +function isNonRevokedEqual(nonRevokedA?: Indy.NonRevokedInterval, nonRevokedB?: Indy.NonRevokedInterval) { + // Having an empty non-revoked object is the same as not having one + if (nonRevokedA === undefined) + return nonRevokedB === undefined || (nonRevokedB.from === undefined && nonRevokedB.to === undefined) + if (nonRevokedB === undefined) return nonRevokedA.from === undefined && nonRevokedA.to === undefined + + return nonRevokedA.from === nonRevokedB.from && nonRevokedA.to === nonRevokedB.to +} + +/** + * Check if two restriction lists are equal. The order of the restrictions does not matter. + */ +function areRestrictionsEqual(restrictionsA?: Indy.WalletQuery[], restrictionsB?: Indy.WalletQuery[]) { + // Having an undefined restrictions property or an empty array is the same + if (restrictionsA === undefined) return restrictionsB === undefined || restrictionsB.length === 0 + if (restrictionsB === undefined) return restrictionsA.length === 0 + + // Clone array to not modify input object + const bList = [...restrictionsB] + + // Check if all restrictions in A are also in B + return restrictionsA.every((a) => { + const bIndex = restrictionsB.findIndex((b) => areObjectsEqual(a, b)) + + // Match found + if (bIndex !== -1) { + bList.splice(bIndex, 1) + return true + } + + // Match not found + return false + }) +} + +function attributeNamesToArray(proofRequest: ProofRequest) { + // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array + // containing all attribute names from the requested attributes. + return Array.from(proofRequest.requestedAttributes.values()).reduce( + (names, a) => [...names, ...(a.name ? [a.name] : a.names ? a.names : [])], + [] + ) +} + +function predicateNamesToArray(proofRequest: ProofRequest) { + return Array.from(new Set(Array.from(proofRequest.requestedPredicates.values()).map((a) => a.name))) +} + +function assertNoDuplicates(predicates: string[], attributeNames: string[]) { + const duplicates = predicates.filter((item) => attributeNames.indexOf(item) !== -1) + if (duplicates.length > 0) { + throw new AriesFrameworkError( + `The proof request contains duplicate predicates and attributes: ${duplicates.toString()}` + ) + } +} + +// TODO: This is still not ideal. The requested groups can specify different credentials using restrictions. +export function assertNoDuplicateGroupsNamesInProofRequest(proofRequest: ProofRequest) { + const attributes = attributeNamesToArray(proofRequest) + const predicates = predicateNamesToArray(proofRequest) + assertNoDuplicates(predicates, attributes) +} diff --git a/packages/core/src/modules/proofs/formats/models/ProofAttachmentFormat.ts b/packages/core/src/modules/proofs/formats/models/ProofAttachmentFormat.ts deleted file mode 100644 index 5bc2fc881b..0000000000 --- a/packages/core/src/modules/proofs/formats/models/ProofAttachmentFormat.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { ProofFormatSpec } from '../../models/ProofFormatSpec' - -export interface ProofAttachmentFormat { - format: ProofFormatSpec - attachment: Attachment -} diff --git a/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts deleted file mode 100644 index 8859c48b64..0000000000 --- a/packages/core/src/modules/proofs/formats/models/ProofFormatServiceOptions.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type { ProofAttachmentFormat } from './ProofAttachmentFormat' -import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { ProposeProofFormats } from '../../models/SharedOptions' -import type { ProofExchangeRecord } from '../../repository' -import type { ProofFormat, ProofFormatPayload } from '../ProofFormat' -import type { ProofRequestOptions } from '../indy/models/ProofRequest' - -export interface CreateRequestAttachmentOptions { - id?: string - proofRequestOptions: ProofRequestOptions -} - -export interface CreateProofAttachmentOptions { - id?: string - proofProposalOptions: ProofRequestOptions -} - -export interface FormatCreateProofProposalOptions { - id?: string - formats: ProposeProofFormats -} - -export interface ProcessProposalOptions { - proposal: ProofAttachmentFormat - record?: ProofExchangeRecord -} - -export interface CreateRequestOptions { - id?: string - formats: ProposeProofFormats -} - -export interface ProcessRequestOptions { - requestAttachment: ProofAttachmentFormat - record?: ProofExchangeRecord -} - -export interface FormatCreatePresentationOptions { - id?: string - attachment: Attachment - proofFormats: ProofFormatPayload<[PF], 'createPresentation'> -} - -export interface ProcessPresentationOptions { - record: ProofExchangeRecord - formatAttachments: { - request: ProofAttachmentFormat[] - presentation: ProofAttachmentFormat[] - } -} - -export interface VerifyProofOptions { - request: Attachment - proof: Attachment -} - -export interface CreateProblemReportOptions { - proofRecord: ProofExchangeRecord - description: string -} - -export interface CreatePresentationFormatsOptions { - presentationAttachment: Attachment -} diff --git a/packages/core/src/modules/proofs/formats/models/index.ts b/packages/core/src/modules/proofs/formats/models/index.ts deleted file mode 100644 index 968a6b53ee..0000000000 --- a/packages/core/src/modules/proofs/formats/models/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './ProofAttachmentFormat' -export * from './ProofFormatServiceOptions' diff --git a/packages/core/src/modules/proofs/index.ts b/packages/core/src/modules/proofs/index.ts index 5d4f5f16c7..30eb44ba0f 100644 --- a/packages/core/src/modules/proofs/index.ts +++ b/packages/core/src/modules/proofs/index.ts @@ -1,13 +1,14 @@ export * from './errors' export * from './formats' -export * from './messages' export * from './models' export * from './protocol' export * from './repository' export * from './ProofEvents' -export * from './ProofResponseCoordinator' + +// Api export * from './ProofsApi' export * from './ProofsApiOptions' -export * from './ProofService' + +// Module export * from './ProofsModule' export * from './ProofsModuleConfig' diff --git a/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts b/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts deleted file mode 100644 index 64e60f56b2..0000000000 --- a/packages/core/src/modules/proofs/messages/PresentationAckMessage.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { ProtocolVersion } from '../../../types' -import type { AckMessageOptions } from '../../common' - -export type PresentationAckMessageOptions = AckMessageOptions - -type PresentationAckMessageType = `https://didcomm.org/present-proof/${ProtocolVersion}/ack` - -export interface PresentationAckMessage { - type: PresentationAckMessageType -} diff --git a/packages/core/src/modules/proofs/messages/index.ts b/packages/core/src/modules/proofs/messages/index.ts deleted file mode 100644 index 1f395b2d57..0000000000 --- a/packages/core/src/modules/proofs/messages/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './PresentationAckMessage' diff --git a/packages/core/src/modules/proofs/models/GetRequestedCredentialsConfig.ts b/packages/core/src/modules/proofs/models/GetRequestedCredentialsConfig.ts deleted file mode 100644 index 9041bbabe3..0000000000 --- a/packages/core/src/modules/proofs/models/GetRequestedCredentialsConfig.ts +++ /dev/null @@ -1,19 +0,0 @@ -export interface GetRequestedCredentialsConfig { - /** - * Whether to filter the retrieved credentials using the presentation preview. - * This configuration will only have effect if a presentation proposal message is available - * containing a presentation preview. - * - * @default false - */ - filterByPresentationPreview?: boolean - - /** - * Whether to filter the retrieved credentials using the non-revocation request in the proof request. - * This configuration will only have effect if the proof request requires proof on non-revocation of any kind. - * Default to true - * - * @default true - */ - filterByNonRevocationRequirements?: boolean -} diff --git a/packages/core/src/modules/proofs/models/ModuleOptions.ts b/packages/core/src/modules/proofs/models/ModuleOptions.ts deleted file mode 100644 index e471a243db..0000000000 --- a/packages/core/src/modules/proofs/models/ModuleOptions.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' -import type { AutoAcceptProof } from './ProofAutoAcceptType' -import type { ProposeProofFormats } from './SharedOptions' - -export interface ProofConfig { - name: string - version: string -} - -export interface NegotiateRequestOptions { - proofRecordId: string - proofFormats: ProposeProofFormats - comment?: string - autoAcceptProof?: AutoAcceptProof -} - -export interface AutoSelectCredentialsForProofRequestOptions { - proofRecordId: string - config?: GetRequestedCredentialsConfig -} - -export type GetRequestedCredentialsForProofRequest = AutoSelectCredentialsForProofRequestOptions diff --git a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts b/packages/core/src/modules/proofs/models/ProofServiceOptions.ts deleted file mode 100644 index 1a978404eb..0000000000 --- a/packages/core/src/modules/proofs/models/ProofServiceOptions.ts +++ /dev/null @@ -1,85 +0,0 @@ -import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' -import type { AutoAcceptProof } from './ProofAutoAcceptType' -import type { ConnectionRecord } from '../../connections' -import type { ProofFormat, ProofFormatPayload } from '../formats/ProofFormat' -import type { ProofExchangeRecord } from '../repository' - -export type FormatDataMessagePayload< - CFs extends ProofFormat[] = ProofFormat[], - M extends keyof ProofFormat['formatData'] = keyof ProofFormat['formatData'] -> = { - [ProofFormat in CFs[number] as ProofFormat['formatKey']]?: ProofFormat['formatData'][M] -} - -interface BaseOptions { - willConfirm?: boolean - goalCode?: string - comment?: string - autoAcceptProof?: AutoAcceptProof -} - -export interface CreateProposalOptions extends BaseOptions { - connectionRecord: ConnectionRecord - proofFormats: ProofFormatPayload - parentThreadId?: string -} - -export interface CreateProposalAsResponseOptions extends BaseOptions { - proofRecord: ProofExchangeRecord - proofFormats: ProofFormatPayload -} - -export interface CreateRequestAsResponseOptions extends BaseOptions { - id?: string - proofRecord: ProofExchangeRecord - proofFormats: ProofFormatPayload -} - -export interface CreateRequestOptions extends BaseOptions { - connectionRecord?: ConnectionRecord - proofFormats: ProofFormatPayload - parentThreadId?: string -} - -export interface CreateProofRequestFromProposalOptions extends BaseOptions { - id?: string - proofRecord: ProofExchangeRecord -} - -export interface FormatRetrievedCredentialOptions { - proofFormats: ProofFormatPayload -} - -export interface FormatRequestedCredentialReturn { - proofFormats: ProofFormatPayload -} - -export interface CreatePresentationOptions extends BaseOptions { - proofRecord: ProofExchangeRecord - proofFormats: ProofFormatPayload // - lastPresentation?: boolean -} - -export interface CreateAckOptions { - proofRecord: ProofExchangeRecord -} - -export interface GetRequestedCredentialsForProofRequestOptions { - proofRecord: ProofExchangeRecord - config?: GetRequestedCredentialsConfig -} - -export interface ProofRequestFromProposalOptions { - proofRecord: ProofExchangeRecord - proofFormats: ProofFormatPayload -} - -export interface DeleteProofOptions { - deleteAssociatedDidCommMessages?: boolean -} - -export type GetFormatDataReturn = { - proposal?: FormatDataMessagePayload - request?: FormatDataMessagePayload - presentation?: FormatDataMessagePayload -} diff --git a/packages/core/src/modules/proofs/models/SharedOptions.ts b/packages/core/src/modules/proofs/models/SharedOptions.ts deleted file mode 100644 index 18fe5ef7f3..0000000000 --- a/packages/core/src/modules/proofs/models/SharedOptions.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { GetRequestedCredentialsConfig } from './GetRequestedCredentialsConfig' -import type { IndyProposeProofFormat } from '../formats/indy/IndyProofFormat' -import type { IndyRequestProofFormat, IndyVerifyProofFormat } from '../formats/indy/IndyProofFormatsServiceOptions' -import type { ProofRequest } from '../formats/indy/models/ProofRequest' -import type { IndyRequestedCredentialsOptions } from '../formats/indy/models/RequestedCredentials' -import type { RetrievedCredentials } from '../formats/indy/models/RetrievedCredentials' - -export interface ProposeProofFormats { - // If you want to propose an indy proof without attributes or - // any of the other properties you should pass an empty object - indy?: IndyProposeProofFormat - presentationExchange?: never -} - -export interface RequestProofFormats { - indy?: IndyRequestProofFormat - presentationExchange?: never -} - -export interface CreatePresentationFormats { - indy?: IndyRequestedCredentialsOptions - presentationExchange?: never -} - -export interface AcceptProposalFormats { - indy?: IndyAcceptProposalOptions - presentationExchange?: never -} - -export interface VerifyProofFormats { - indy?: IndyVerifyProofFormat - presentationExchange?: never -} - -export interface RequestedCredentialConfigOptions { - indy?: GetRequestedCredentialsConfig - presentationExchange?: never -} - -// export interface RetrievedCredentialOptions { -// indy?: RetrievedCredentials -// presentationExchange?: undefined -// } - -export interface ProofRequestFormats { - indy?: ProofRequest - presentationExchange?: undefined -} - -// export interface RequestedCredentialsFormats { -// indy?: RequestedCredentials -// presentationExchange?: undefined -// } - -interface IndyAcceptProposalOptions { - request: ProofRequest -} - -export interface AutoSelectCredentialOptions { - indy?: RetrievedCredentials - presentationExchange?: undefined -} diff --git a/packages/core/src/modules/proofs/__tests__/ProofState.test.ts b/packages/core/src/modules/proofs/models/__tests__/ProofState.test.ts similarity index 92% rename from packages/core/src/modules/proofs/__tests__/ProofState.test.ts rename to packages/core/src/modules/proofs/models/__tests__/ProofState.test.ts index 4b67ed11d0..9cabafd183 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofState.test.ts +++ b/packages/core/src/modules/proofs/models/__tests__/ProofState.test.ts @@ -1,4 +1,4 @@ -import { ProofState } from '../models/ProofState' +import { ProofState } from '../ProofState' describe('ProofState', () => { test('state matches Present Proof 1.0 (RFC 0037) state value', () => { diff --git a/packages/core/src/modules/proofs/models/index.ts b/packages/core/src/modules/proofs/models/index.ts index a092a0ae7e..9e20094e5e 100644 --- a/packages/core/src/modules/proofs/models/index.ts +++ b/packages/core/src/modules/proofs/models/index.ts @@ -1,3 +1,2 @@ -export * from './GetRequestedCredentialsConfig' export * from './ProofAutoAcceptType' export * from './ProofState' diff --git a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts new file mode 100644 index 0000000000..9e4e9e8b1c --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts @@ -0,0 +1,286 @@ +import type { ProofProtocol } from './ProofProtocol' +import type { + CreateProofProposalOptions, + CreateProofRequestOptions, + DeleteProofOptions, + GetProofFormatDataReturn, + CreateProofProblemReportOptions, + ProofProtocolMsgReturnType, + AcceptPresentationOptions, + AcceptProofProposalOptions, + AcceptProofRequestOptions, + GetCredentialsForRequestOptions, + GetCredentialsForRequestReturn, + NegotiateProofProposalOptions, + NegotiateProofRequestOptions, + SelectCredentialsForRequestOptions, + SelectCredentialsForRequestReturn, +} from './ProofProtocolOptions' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../agent/FeatureRegistry' +import type { AgentContext } from '../../../agent/context/AgentContext' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../plugins' +import type { Query } from '../../../storage/StorageService' +import type { ProblemReportMessage } from '../../problem-reports' +import type { ProofStateChangedEvent } from '../ProofEvents' +import type { ExtractProofFormats, ProofFormatService } from '../formats' +import type { ProofExchangeRecord } from '../repository' + +import { EventEmitter } from '../../../agent/EventEmitter' +import { DidCommMessageRepository } from '../../../storage' +import { JsonTransformer } from '../../../utils/JsonTransformer' +import { ProofEventTypes } from '../ProofEvents' +import { ProofState } from '../models/ProofState' +import { ProofRepository } from '../repository' + +export abstract class BaseProofProtocol + implements ProofProtocol +{ + public abstract readonly version: string + + public abstract register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void + + // methods for proposal + public abstract createProposal( + agentContext: AgentContext, + options: CreateProofProposalOptions + ): Promise> + public abstract processProposal(messageContext: InboundMessageContext): Promise + public abstract acceptProposal( + agentContext: AgentContext, + options: AcceptProofProposalOptions + ): Promise> + public abstract negotiateProposal( + agentContext: AgentContext, + options: NegotiateProofProposalOptions + ): Promise> + + // methods for request + public abstract createRequest( + agentContext: AgentContext, + options: CreateProofRequestOptions + ): Promise> + public abstract processRequest(messageContext: InboundMessageContext): Promise + public abstract acceptRequest( + agentContext: AgentContext, + options: AcceptProofRequestOptions + ): Promise> + public abstract negotiateRequest( + agentContext: AgentContext, + options: NegotiateProofRequestOptions + ): Promise> + + // retrieving credentials for request + public abstract getCredentialsForRequest( + agentContext: AgentContext, + options: GetCredentialsForRequestOptions + ): Promise> + public abstract selectCredentialsForRequest( + agentContext: AgentContext, + options: SelectCredentialsForRequestOptions + ): Promise> + + // methods for presentation + public abstract processPresentation(messageContext: InboundMessageContext): Promise + public abstract acceptPresentation( + agentContext: AgentContext, + options: AcceptPresentationOptions + ): Promise> + + // methods for ack + public abstract processAck(messageContext: InboundMessageContext): Promise + // method for problem report + public abstract createProblemReport( + agentContext: AgentContext, + options: CreateProofProblemReportOptions + ): Promise> + + public abstract findProposalMessage(agentContext: AgentContext, proofExchangeId: string): Promise + public abstract findRequestMessage(agentContext: AgentContext, proofExchangeId: string): Promise + public abstract findPresentationMessage( + agentContext: AgentContext, + proofExchangeId: string + ): Promise + public abstract getFormatData( + agentContext: AgentContext, + proofExchangeId: string + ): Promise>> + + public async processProblemReport( + messageContext: InboundMessageContext + ): Promise { + const { message: proofProblemReportMessage, agentContext } = messageContext + + const connection = messageContext.assertReadyConnection() + + agentContext.config.logger.debug(`Processing problem report with message id ${proofProblemReportMessage.id}`) + + const proofRecord = await this.getByThreadAndConnectionId( + agentContext, + proofProblemReportMessage.threadId, + connection.id + ) + + // Update record + proofRecord.errorMessage = `${proofProblemReportMessage.description.code}: ${proofProblemReportMessage.description.en}` + await this.updateState(agentContext, proofRecord, ProofState.Abandoned) + return proofRecord + } + + /** + * Update the record to a new state and emit an state changed event. Also updates the record + * in storage. + * + * @param proofRecord The proof record to update the state for + * @param newState The state to update to + * + */ + public async updateState(agentContext: AgentContext, proofRecord: ProofExchangeRecord, newState: ProofState) { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + agentContext.config.logger.debug( + `Updating proof record ${proofRecord.id} to state ${newState} (previous=${proofRecord.state})` + ) + + const previousState = proofRecord.state + proofRecord.state = newState + await proofRepository.update(agentContext, proofRecord) + + this.emitStateChangedEvent(agentContext, proofRecord, previousState) + } + + protected emitStateChangedEvent( + agentContext: AgentContext, + proofRecord: ProofExchangeRecord, + previousState: ProofState | null + ) { + const eventEmitter = agentContext.dependencyManager.resolve(EventEmitter) + + const clonedProof = JsonTransformer.clone(proofRecord) + + eventEmitter.emit(agentContext, { + type: ProofEventTypes.ProofStateChanged, + payload: { + proofRecord: clonedProof, + previousState: previousState, + }, + }) + } + + /** + * Retrieve a proof record by id + * + * @param proofRecordId The proof record id + * @throws {RecordNotFoundError} If no record is found + * @return The proof record + * + */ + public getById(agentContext: AgentContext, proofRecordId: string): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return proofRepository.getById(agentContext, proofRecordId) + } + + /** + * Retrieve all proof records + * + * @returns List containing all proof records + */ + public getAll(agentContext: AgentContext): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return proofRepository.getAll(agentContext) + } + + public async findAllByQuery( + agentContext: AgentContext, + query: Query + ): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return proofRepository.findByQuery(agentContext, query) + } + + /** + * Find a proof record by id + * + * @param proofRecordId the proof record id + * @returns The proof record or null if not found + */ + public findById(agentContext: AgentContext, proofRecordId: string): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return proofRepository.findById(agentContext, proofRecordId) + } + + public async delete( + agentContext: AgentContext, + proofRecord: ProofExchangeRecord, + options?: DeleteProofOptions + ): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + await proofRepository.delete(agentContext, proofRecord) + + const deleteAssociatedDidCommMessages = options?.deleteAssociatedDidCommMessages ?? true + + if (deleteAssociatedDidCommMessages) { + const didCommMessages = await didCommMessageRepository.findByQuery(agentContext, { + associatedRecordId: proofRecord.id, + }) + for (const didCommMessage of didCommMessages) { + await didCommMessageRepository.delete(agentContext, didCommMessage) + } + } + } + + /** + * Retrieve a proof record by connection id and thread id + * + * @param connectionId The connection id + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The proof record + */ + public getByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return proofRepository.getSingleByQuery(agentContext, { + connectionId, + threadId, + }) + } + + /** + * Find a proof record by connection id and thread id, returns null if not found + * + * @param connectionId The connection id + * @param threadId The thread id + * @returns The proof record + */ + public findByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return proofRepository.findSingleByQuery(agentContext, { + connectionId, + threadId, + }) + } + + public async update(agentContext: AgentContext, proofRecord: ProofExchangeRecord) { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + return await proofRepository.update(agentContext, proofRecord) + } +} diff --git a/packages/core/src/modules/proofs/protocol/ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/ProofProtocol.ts new file mode 100644 index 0000000000..2065d97b35 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/ProofProtocol.ts @@ -0,0 +1,117 @@ +import type { + CreateProofProposalOptions, + CreateProofRequestOptions, + DeleteProofOptions, + GetProofFormatDataReturn, + CreateProofProblemReportOptions, + ProofProtocolMsgReturnType, + AcceptProofProposalOptions, + NegotiateProofProposalOptions, + AcceptProofRequestOptions, + NegotiateProofRequestOptions, + AcceptPresentationOptions, + GetCredentialsForRequestOptions, + GetCredentialsForRequestReturn, + SelectCredentialsForRequestOptions, + SelectCredentialsForRequestReturn, +} from './ProofProtocolOptions' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../agent/FeatureRegistry' +import type { AgentContext } from '../../../agent/context/AgentContext' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../plugins' +import type { Query } from '../../../storage/StorageService' +import type { ProblemReportMessage } from '../../problem-reports' +import type { ExtractProofFormats, ProofFormatService } from '../formats' +import type { ProofState } from '../models/ProofState' +import type { ProofExchangeRecord } from '../repository' + +export interface ProofProtocol { + readonly version: string + + // methods for proposal + createProposal( + agentContext: AgentContext, + options: CreateProofProposalOptions + ): Promise> + processProposal(messageContext: InboundMessageContext): Promise + acceptProposal( + agentContext: AgentContext, + options: AcceptProofProposalOptions + ): Promise> + negotiateProposal( + agentContext: AgentContext, + options: NegotiateProofProposalOptions + ): Promise> + + // methods for request + createRequest( + agentContext: AgentContext, + options: CreateProofRequestOptions + ): Promise> + processRequest(messageContext: InboundMessageContext): Promise + acceptRequest( + agentContext: AgentContext, + options: AcceptProofRequestOptions + ): Promise> + negotiateRequest( + agentContext: AgentContext, + options: NegotiateProofRequestOptions + ): Promise> + + // retrieving credentials for request + getCredentialsForRequest( + agentContext: AgentContext, + options: GetCredentialsForRequestOptions + ): Promise> + selectCredentialsForRequest( + agentContext: AgentContext, + options: SelectCredentialsForRequestOptions + ): Promise> + + // methods for presentation + processPresentation(messageContext: InboundMessageContext): Promise + acceptPresentation( + agentContext: AgentContext, + options: AcceptPresentationOptions + ): Promise> + + // methods for ack + processAck(messageContext: InboundMessageContext): Promise + + // method for problem report + createProblemReport( + agentContext: AgentContext, + options: CreateProofProblemReportOptions + ): Promise> + processProblemReport(messageContext: InboundMessageContext): Promise + + findProposalMessage(agentContext: AgentContext, proofExchangeId: string): Promise + findRequestMessage(agentContext: AgentContext, proofExchangeId: string): Promise + findPresentationMessage(agentContext: AgentContext, proofExchangeId: string): Promise + getFormatData( + agentContext: AgentContext, + proofExchangeId: string + ): Promise>> + + // repository methods + updateState(agentContext: AgentContext, proofRecord: ProofExchangeRecord, newState: ProofState): Promise + getById(agentContext: AgentContext, proofExchangeId: string): Promise + getAll(agentContext: AgentContext): Promise + findAllByQuery(agentContext: AgentContext, query: Query): Promise + findById(agentContext: AgentContext, proofExchangeId: string): Promise + delete(agentContext: AgentContext, proofRecord: ProofExchangeRecord, options?: DeleteProofOptions): Promise + getByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise + findByThreadAndConnectionId( + agentContext: AgentContext, + threadId: string, + connectionId?: string + ): Promise + update(agentContext: AgentContext, proofRecord: ProofExchangeRecord): Promise + + register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void +} diff --git a/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts b/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts new file mode 100644 index 0000000000..ff752beb29 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts @@ -0,0 +1,165 @@ +import type { ProofProtocol } from './ProofProtocol' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { ConnectionRecord } from '../../connections' +import type { + ExtractProofFormats, + ProofFormat, + ProofFormatCredentialForRequestPayload, + ProofFormatPayload, + ProofFormatService, +} from '../formats' +import type { AutoAcceptProof } from '../models' +import type { ProofExchangeRecord } from '../repository' + +/** + * Get the format data payload for a specific message from a list of ProofFormat interfaces and a message + * + * For an indy offer, this resolves to the proof request format as defined here: + * https://github.com/hyperledger/aries-rfcs/tree/b3a3942ef052039e73cd23d847f42947f8287da2/features/0592-indy-attachments#proof-request-format + * + * @example + * ``` + * + * type RequestFormatData = ProofFormatDataMessagePayload<[IndyProofFormat, PresentationExchangeProofFormat], 'createRequest'> + * + * // equal to + * type RequestFormatData = { + * indy: { + * // ... payload for indy proof request attachment as defined in RFC 0592 ... + * }, + * presentationExchange: { + * // ... payload for presentation exchange request attachment as defined in RFC 0510 ... + * } + * } + * ``` + */ +export type ProofFormatDataMessagePayload< + CFs extends ProofFormat[] = ProofFormat[], + M extends keyof ProofFormat['formatData'] = keyof ProofFormat['formatData'] +> = { + [ProofFormat in CFs[number] as ProofFormat['formatKey']]?: ProofFormat['formatData'][M] +} + +/** + * Infer the {@link ProofFormat} types based on an array of {@link ProofProtocol} types. + * + * It does this by extracting the `ProofFormatServices` generic from the `ProofProtocol`, and + * then extracting the `ProofFormat` generic from each of the `ProofFormatService` types. + * + * @example + * ``` + * // TheProofFormatServices is now equal to [IndyProofFormatService] + * type TheProofFormatServices = ProofFormatsFromProtocols<[V1ProofProtocol]> + * ``` + * + * Because the `V1ProofProtocol` is defined as follows: + * ``` + * class V1ProofProtocol implements ProofProtocol<[IndyProofFormatService]> { + * } + * ``` + */ +export type ProofFormatsFromProtocols = Type[number] extends ProofProtocol< + infer ProofFormatServices +> + ? ProofFormatServices extends ProofFormatService[] + ? ExtractProofFormats + : never + : never + +export type GetProofFormatDataReturn = { + proposal?: ProofFormatDataMessagePayload + request?: ProofFormatDataMessagePayload + presentation?: ProofFormatDataMessagePayload +} + +interface BaseOptions { + goalCode?: string + comment?: string + autoAcceptProof?: AutoAcceptProof +} + +export interface CreateProofProposalOptions extends BaseOptions { + connectionRecord: ConnectionRecord + proofFormats: ProofFormatPayload, 'createProposal'> + parentThreadId?: string +} + +export interface AcceptProofProposalOptions extends BaseOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatPayload, 'acceptProposal'> + + /** @default true */ + willConfirm?: boolean +} + +export interface NegotiateProofProposalOptions extends BaseOptions { + proofRecord: ProofExchangeRecord + proofFormats: ProofFormatPayload, 'createRequest'> + + /** @default true */ + willConfirm?: boolean +} + +export interface CreateProofRequestOptions extends BaseOptions { + // Create request can also be used for connection-less, so connection is optional + connectionRecord?: ConnectionRecord + proofFormats: ProofFormatPayload, 'createRequest'> + parentThreadId?: string + + /** @default true */ + willConfirm?: boolean +} + +export interface AcceptProofRequestOptions extends BaseOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatPayload, 'acceptRequest'> +} + +export interface NegotiateProofRequestOptions extends BaseOptions { + proofRecord: ProofExchangeRecord + proofFormats: ProofFormatPayload, 'createProposal'> +} + +export interface GetCredentialsForRequestOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatCredentialForRequestPayload, 'getCredentialsForRequest', 'input'> +} + +export interface GetCredentialsForRequestReturn { + proofFormats: ProofFormatCredentialForRequestPayload, 'getCredentialsForRequest', 'output'> +} + +export interface SelectCredentialsForRequestOptions { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatCredentialForRequestPayload< + ExtractProofFormats, + 'selectCredentialsForRequest', + 'input' + > +} + +export interface SelectCredentialsForRequestReturn { + proofFormats: ProofFormatCredentialForRequestPayload< + ExtractProofFormats, + 'selectCredentialsForRequest', + 'output' + > +} + +export interface AcceptPresentationOptions { + proofRecord: ProofExchangeRecord +} + +export interface CreateProofProblemReportOptions { + proofRecord: ProofExchangeRecord + description: string +} + +export interface ProofProtocolMsgReturnType { + message: MessageType + proofRecord: ProofExchangeRecord +} + +export interface DeleteProofOptions { + deleteAssociatedDidCommMessages?: boolean +} diff --git a/packages/core/src/modules/proofs/protocol/index.ts b/packages/core/src/modules/proofs/protocol/index.ts index 4d9da63573..db72d7287c 100644 --- a/packages/core/src/modules/proofs/protocol/index.ts +++ b/packages/core/src/modules/proofs/protocol/index.ts @@ -1,2 +1,6 @@ export * from './v1' export * from './v2' +export { ProofProtocol } from './ProofProtocol' +import * as ProofProtocolOptions from './ProofProtocolOptions' + +export { ProofProtocolOptions } diff --git a/packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts new file mode 100644 index 0000000000..5461aa8ebc --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts @@ -0,0 +1,1111 @@ +import type { AgentContext } from '../../../../agent' +import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../../plugins' +import type { ProblemReportMessage } from '../../../problem-reports' +import type { ProofFormatService } from '../../formats' +import type { ProofFormat } from '../../formats/ProofFormat' +import type { IndyProofFormat } from '../../formats/indy/IndyProofFormat' +import type { ProofProtocol } from '../ProofProtocol' +import type { + AcceptPresentationOptions, + AcceptProofProposalOptions, + AcceptProofRequestOptions, + CreateProofProblemReportOptions, + CreateProofProposalOptions, + CreateProofRequestOptions, + GetCredentialsForRequestOptions, + GetCredentialsForRequestReturn, + GetProofFormatDataReturn, + NegotiateProofProposalOptions, + NegotiateProofRequestOptions, + ProofProtocolMsgReturnType, + SelectCredentialsForRequestOptions, + SelectCredentialsForRequestReturn, +} from '../ProofProtocolOptions' + +import { Protocol } from '../../../../agent/models' +import { Attachment } from '../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' +import { JsonEncoder } from '../../../../utils' +import { JsonTransformer } from '../../../../utils/JsonTransformer' +import { MessageValidator } from '../../../../utils/MessageValidator' +import { uuid } from '../../../../utils/uuid' +import { AckStatus } from '../../../common/messages/AckMessage' +import { ConnectionService } from '../../../connections' +import { ProofsModuleConfig } from '../../ProofsModuleConfig' +import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' +import { createRequestFromPreview } from '../../formats/indy/util' +import { AutoAcceptProof } from '../../models' +import { ProofState } from '../../models/ProofState' +import { ProofRepository } from '../../repository' +import { ProofExchangeRecord } from '../../repository/ProofExchangeRecord' +import { composeAutoAccept } from '../../utils/composeAutoAccept' +import { BaseProofProtocol } from '../BaseProofProtocol' + +import { V1PresentationProblemReportError } from './errors' +import { + V1PresentationAckHandler, + V1PresentationHandler, + V1PresentationProblemReportHandler, + V1ProposePresentationHandler, + V1RequestPresentationHandler, +} from './handlers' +import { + INDY_PROOF_ATTACHMENT_ID, + INDY_PROOF_REQUEST_ATTACHMENT_ID, + V1PresentationAckMessage, + V1PresentationMessage, + V1ProposePresentationMessage, + V1RequestPresentationMessage, +} from './messages' +import { V1PresentationProblemReportMessage } from './messages/V1PresentationProblemReportMessage' +import { V1PresentationPreview } from './models/V1PresentationPreview' + +type IndyProofFormatServiceLike = ProofFormatService + +export interface V1ProofProtocolConfig { + // indyCredentialFormat must be a service that implements the `IndyProofFormat` interface, however it doesn't + // have to be the IndyProofFormatService implementation per se. + indyProofFormat: ProofFormatService +} + +export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol<[IndyProofFormatServiceLike]> { + private indyProofFormat: ProofFormatService + + public constructor({ indyProofFormat }: V1ProofProtocolConfig) { + super() + + this.indyProofFormat = indyProofFormat + } + + /** + * The version of the present proof protocol this protocol supports + */ + public readonly version = 'v1' as const + + /** + * Registers the protocol implementation (handlers, feature registry) on the agent. + */ + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { + // Register message handlers for the Issue Credential V1 Protocol + dependencyManager.registerMessageHandlers([ + new V1ProposePresentationHandler(this), + new V1RequestPresentationHandler(this), + new V1PresentationHandler(this), + new V1PresentationAckHandler(this), + new V1PresentationProblemReportHandler(this), + ]) + + // Register Present Proof V1 in feature registry, with supported roles + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/present-proof/1.0', + roles: ['prover', 'verifier'], + }) + ) + } + + public async createProposal( + agentContext: AgentContext, + { + proofFormats, + connectionRecord, + comment, + parentThreadId, + autoAcceptProof, + }: CreateProofProposalOptions<[IndyProofFormatServiceLike]> + ): Promise> { + this.assertOnlyIndyFormat(proofFormats) + + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + if (!proofFormats.indy) { + throw new AriesFrameworkError('Missing indy proof format in v1 create proposal call.') + } + + const presentationProposal = new V1PresentationPreview({ + attributes: proofFormats.indy?.attributes, + predicates: proofFormats.indy?.predicates, + }) + + // validate input data from user + MessageValidator.validateSync(presentationProposal) + + // Create message + const message = new V1ProposePresentationMessage({ + presentationProposal, + comment, + }) + + if (parentThreadId) + message.setThread({ + parentThreadId, + }) + + // Create record + const proofRecord = new ProofExchangeRecord({ + connectionId: connectionRecord.id, + threadId: message.threadId, + parentThreadId: message.thread?.parentThreadId, + state: ProofState.ProposalSent, + autoAcceptProof, + protocolVersion: 'v1', + }) + + await didCommMessageRepository.saveAgentMessage(agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + await proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + + return { proofRecord, message } + } + + public async processProposal( + messageContext: InboundMessageContext + ): Promise { + const { message: proposalMessage, connection, agentContext } = messageContext + + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // TODO: with this method, we should update the credential protocol to use the ConnectionApi, so it + // only depends on the public api, rather than the internal API (this helps with breaking changes) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + agentContext.config.logger.debug(`Processing presentation proposal with message id ${proposalMessage.id}`) + + let proofRecord = await this.findByThreadAndConnectionId(agentContext, proposalMessage.threadId, connection?.id) + + // Proof record already exists, this is a response to an earlier message sent by us + if (proofRecord) { + agentContext.config.logger.debug('Proof record already exists for incoming proposal') + + // Assert + proofRecord.assertState(ProofState.RequestSent) + proofRecord.assertProtocolVersion('v1') + + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + const previousSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + // Update record + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: proposalMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + await this.updateState(agentContext, proofRecord, ProofState.ProposalReceived) + } else { + agentContext.config.logger.debug('Proof record does not exists yet for incoming proposal') + + // No proof record exists with thread id + proofRecord = new ProofExchangeRecord({ + connectionId: connection?.id, + threadId: proposalMessage.threadId, + parentThreadId: proposalMessage.thread?.parentThreadId, + state: ProofState.ProposalReceived, + protocolVersion: 'v1', + }) + + // Assert + connectionService.assertConnectionOrServiceDecorator(messageContext) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: proposalMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + // Save record + await proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + } + + return proofRecord + } + + public async acceptProposal( + agentContext: AgentContext, + { proofRecord, proofFormats, comment, autoAcceptProof }: AcceptProofProposalOptions<[IndyProofFormatServiceLike]> + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.ProposalReceived) + if (proofFormats) this.assertOnlyIndyFormat(proofFormats) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const proposalMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + const indyFormat = proofFormats?.indy + + // Create a proof request from the preview, so we can let the messages + // be handled using the indy proof format which supports RFC0592 + const requestFromPreview = createRequestFromPreview({ + attributes: proposalMessage.presentationProposal.attributes, + predicates: proposalMessage.presentationProposal.predicates, + name: indyFormat?.name ?? 'Proof Request', + version: indyFormat?.version ?? '1.0', + nonce: await agentContext.wallet.generateNonce(), + }) + + const proposalAttachment = new Attachment({ + data: { + json: JsonTransformer.toJSON(requestFromPreview), + }, + }) + + // Create message + const { attachment } = await this.indyProofFormat.acceptProposal(agentContext, { + attachmentId: INDY_PROOF_REQUEST_ATTACHMENT_ID, + proofRecord, + proposalAttachment, + }) + + const requestPresentationMessage = new V1RequestPresentationMessage({ + comment, + requestAttachments: [attachment], + }) + + requestPresentationMessage.setThread({ + threadId: proofRecord.threadId, + parentThreadId: proofRecord.parentThreadId, + }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: requestPresentationMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + // Update record + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.RequestSent) + + return { message: requestPresentationMessage, proofRecord } + } + + public async negotiateProposal( + agentContext: AgentContext, + { proofFormats, proofRecord, comment, autoAcceptProof }: NegotiateProofProposalOptions<[IndyProofFormatServiceLike]> + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.ProposalReceived) + this.assertOnlyIndyFormat(proofFormats) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // Create message + const { attachment } = await this.indyProofFormat.createRequest(agentContext, { + attachmentId: INDY_PROOF_REQUEST_ATTACHMENT_ID, + proofFormats, + proofRecord, + }) + + const requestPresentationMessage = new V1RequestPresentationMessage({ + comment, + requestAttachments: [attachment], + }) + requestPresentationMessage.setThread({ + threadId: proofRecord.threadId, + }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: requestPresentationMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.RequestSent) + + return { message: requestPresentationMessage, proofRecord } + } + + public async createRequest( + agentContext: AgentContext, + { + proofFormats, + connectionRecord, + comment, + parentThreadId, + autoAcceptProof, + }: CreateProofRequestOptions<[IndyProofFormatServiceLike]> + ): Promise> { + this.assertOnlyIndyFormat(proofFormats) + + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + if (!proofFormats.indy) { + throw new AriesFrameworkError('Missing indy proof request data for v1 create request') + } + + // Create record + const proofRecord = new ProofExchangeRecord({ + connectionId: connectionRecord?.id, + threadId: uuid(), + parentThreadId, + state: ProofState.RequestSent, + autoAcceptProof, + protocolVersion: 'v1', + }) + + // Create message + const { attachment } = await this.indyProofFormat.createRequest(agentContext, { + attachmentId: INDY_PROOF_REQUEST_ATTACHMENT_ID, + proofFormats, + proofRecord, + }) + + // Construct request message + const message = new V1RequestPresentationMessage({ + id: proofRecord.threadId, + comment, + requestAttachments: [attachment], + }) + + message.setThread({ + threadId: proofRecord.threadId, + parentThreadId: proofRecord.parentThreadId, + }) + + await didCommMessageRepository.saveAgentMessage(agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + await proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + + return { message, proofRecord } + } + + public async processRequest( + messageContext: InboundMessageContext + ): Promise { + const { message: proofRequestMessage, connection, agentContext } = messageContext + + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // TODO: with this method, we should update the credential protocol to use the ConnectionApi, so it + // only depends on the public api, rather than the internal API (this helps with breaking changes) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + agentContext.config.logger.debug(`Processing presentation request with id ${proofRequestMessage.id}`) + + let proofRecord = await this.findByThreadAndConnectionId(agentContext, proofRequestMessage.threadId, connection?.id) + + const requestAttachment = proofRequestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + if (!requestAttachment) { + throw new AriesFrameworkError( + `Indy attachment with id ${INDY_PROOF_REQUEST_ATTACHMENT_ID} not found in request message` + ) + } + + // proof record already exists, this means we are the message is sent as reply to a proposal we sent + if (proofRecord) { + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + const previousSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.ProposalSent) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + await this.indyProofFormat.processRequest(agentContext, { + attachment: requestAttachment, + proofRecord, + }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: proofRequestMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + await this.updateState(agentContext, proofRecord, ProofState.RequestReceived) + } else { + // No proof record exists with thread id + proofRecord = new ProofExchangeRecord({ + connectionId: connection?.id, + threadId: proofRequestMessage.threadId, + parentThreadId: proofRequestMessage.thread?.parentThreadId, + state: ProofState.RequestReceived, + protocolVersion: 'v1', + }) + + await this.indyProofFormat.processRequest(agentContext, { + attachment: requestAttachment, + proofRecord, + }) + + await didCommMessageRepository.saveAgentMessage(agentContext, { + agentMessage: proofRequestMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + + // Assert + connectionService.assertConnectionOrServiceDecorator(messageContext) + + // Save in repository + await proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + } + + return proofRecord + } + + public async negotiateRequest( + agentContext: AgentContext, + { proofFormats, proofRecord, comment, autoAcceptProof }: NegotiateProofRequestOptions<[IndyProofFormatServiceLike]> + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.RequestReceived) + this.assertOnlyIndyFormat(proofFormats) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + if (!proofRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support negotiation.` + ) + } + + if (!proofFormats.indy) { + throw new AriesFrameworkError('Missing indy proof format in v1 negotiate request call.') + } + + const presentationProposal = new V1PresentationPreview({ + attributes: proofFormats.indy?.attributes, + predicates: proofFormats.indy?.predicates, + }) + + // validate input data from user + MessageValidator.validateSync(presentationProposal) + + const message = new V1ProposePresentationMessage({ + comment, + presentationProposal, + }) + message.setThread({ threadId: proofRecord.threadId }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + // Update record + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.ProposalSent) + + return { proofRecord, message: message } + } + + public async acceptRequest( + agentContext: AgentContext, + { proofRecord, proofFormats, autoAcceptProof, comment }: AcceptProofRequestOptions<[IndyProofFormatServiceLike]> + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.RequestReceived) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + const indyProofRequest = requestMessage.indyProofRequest + + if (!requestAttachment || !indyProofRequest) { + throw new V1PresentationProblemReportError( + `Missing indy attachment in request message for presentation with thread id ${proofRecord.threadId}`, + { problemCode: PresentationProblemReportReason.Abandoned } + ) + } + + const proposalAttachment = proposalMessage + ? new Attachment({ + data: { + json: JsonTransformer.toJSON( + createRequestFromPreview({ + attributes: proposalMessage.presentationProposal?.attributes, + predicates: proposalMessage.presentationProposal?.predicates, + name: indyProofRequest.name, + nonce: indyProofRequest.nonce, + version: indyProofRequest.nonce, + }) + ), + }, + }) + : undefined + + const { attachment } = await this.indyProofFormat.acceptRequest(agentContext, { + attachmentId: INDY_PROOF_ATTACHMENT_ID, + requestAttachment, + proposalAttachment, + proofFormats, + proofRecord, + }) + + const message = new V1PresentationMessage({ + comment, + presentationAttachments: [attachment], + }) + message.setThread({ threadId: proofRecord.threadId }) + + await didCommMessageRepository.saveAgentMessage(agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + // Update record + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.PresentationSent) + + return { message, proofRecord } + } + + public async getCredentialsForRequest( + agentContext: AgentContext, + { proofRecord, proofFormats }: GetCredentialsForRequestOptions<[IndyProofFormatServiceLike]> + ): Promise> { + if (proofFormats) this.assertOnlyIndyFormat(proofFormats) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + const indyProofRequest = requestMessage.indyProofRequest + + if (!requestAttachment || !indyProofRequest) { + throw new AriesFrameworkError( + `Missing indy attachment in request message for presentation with thread id ${proofRecord.threadId}` + ) + } + + const proposalAttachment = proposalMessage + ? new Attachment({ + data: { + json: JsonTransformer.toJSON( + createRequestFromPreview({ + attributes: proposalMessage.presentationProposal?.attributes, + predicates: proposalMessage.presentationProposal?.predicates, + name: indyProofRequest.name, + nonce: indyProofRequest.nonce, + version: indyProofRequest.nonce, + }) + ), + }, + }) + : undefined + + const credentialForRequest = await this.indyProofFormat.getCredentialsForRequest(agentContext, { + proofRecord, + requestAttachment, + proofFormats, + proposalAttachment, + }) + + return { + proofFormats: { + indy: credentialForRequest, + }, + } + } + + public async selectCredentialsForRequest( + agentContext: AgentContext, + { proofRecord, proofFormats }: SelectCredentialsForRequestOptions<[IndyProofFormatServiceLike]> + ): Promise> { + if (proofFormats) this.assertOnlyIndyFormat(proofFormats) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + const indyProofRequest = requestMessage.indyProofRequest + + if (!requestAttachment || !indyProofRequest) { + throw new AriesFrameworkError( + `Missing indy attachment in request message for presentation with thread id ${proofRecord.threadId}` + ) + } + + const proposalAttachment = proposalMessage + ? new Attachment({ + data: { + json: JsonTransformer.toJSON( + createRequestFromPreview({ + attributes: proposalMessage.presentationProposal?.attributes, + predicates: proposalMessage.presentationProposal?.predicates, + name: indyProofRequest.name, + nonce: indyProofRequest.nonce, + version: indyProofRequest.nonce, + }) + ), + }, + }) + : undefined + + const selectedCredentials = await this.indyProofFormat.selectCredentialsForRequest(agentContext, { + proofFormats, + proofRecord, + requestAttachment, + proposalAttachment, + }) + + return { + proofFormats: { + indy: selectedCredentials, + }, + } + } + + public async processPresentation( + messageContext: InboundMessageContext + ): Promise { + const { message: presentationMessage, connection, agentContext } = messageContext + + agentContext.config.logger.debug(`Processing presentation with message id ${presentationMessage.id}`) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // TODO: with this method, we should update the credential protocol to use the ConnectionApi, so it + // only depends on the public api, rather than the internal API (this helps with breaking changes) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + const proofRecord = await this.getByThreadAndConnectionId( + agentContext, + presentationMessage.threadId, + connection?.id + ) + + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1ProposePresentationMessage, + }) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + // Assert + proofRecord.assertState(ProofState.RequestSent) + proofRecord.assertProtocolVersion('v1') + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage: proposalMessage, + previousSentMessage: requestMessage, + }) + + const presentationAttachment = presentationMessage.getPresentationAttachmentById(INDY_PROOF_ATTACHMENT_ID) + if (!presentationAttachment) { + throw new AriesFrameworkError('Missing indy proof attachment in processPresentation') + } + + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + if (!requestAttachment) { + throw new AriesFrameworkError('Missing indy proof request attachment in processPresentation') + } + + const isValid = await this.indyProofFormat.processPresentation(agentContext, { + proofRecord, + attachment: presentationAttachment, + requestAttachment, + }) + + await didCommMessageRepository.saveAgentMessage(agentContext, { + agentMessage: presentationMessage, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Receiver, + }) + + // Update record + proofRecord.isVerified = isValid + await this.updateState(agentContext, proofRecord, ProofState.PresentationReceived) + + return proofRecord + } + + public async acceptPresentation( + agentContext: AgentContext, + { proofRecord }: AcceptPresentationOptions + ): Promise> { + agentContext.config.logger.debug(`Creating presentation ack for proof record with id ${proofRecord.id}`) + + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.PresentationReceived) + + // Create message + const ackMessage = new V1PresentationAckMessage({ + status: AckStatus.OK, + threadId: proofRecord.threadId, + }) + + // Update record + await this.updateState(agentContext, proofRecord, ProofState.Done) + + return { message: ackMessage, proofRecord } + } + + public async processAck( + messageContext: InboundMessageContext + ): Promise { + const { message: presentationAckMessage, connection, agentContext } = messageContext + + agentContext.config.logger.debug(`Processing presentation ack with message id ${presentationAckMessage.id}`) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // TODO: with this method, we should update the credential protocol to use the ConnectionApi, so it + // only depends on the public api, rather than the internal API (this helps with breaking changes) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + const proofRecord = await this.getByThreadAndConnectionId( + agentContext, + presentationAckMessage.threadId, + connection?.id + ) + + const previousReceivedMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + const previousSentMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1PresentationMessage, + }) + + // Assert + proofRecord.assertProtocolVersion('v1') + proofRecord.assertState(ProofState.PresentationSent) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + // Update record + await this.updateState(agentContext, proofRecord, ProofState.Done) + + return proofRecord + } + + public async createProblemReport( + agentContext: AgentContext, + { proofRecord, description }: CreateProofProblemReportOptions + ): Promise> { + const message = new V1PresentationProblemReportMessage({ + description: { + code: PresentationProblemReportReason.Abandoned, + en: description, + }, + }) + + message.setThread({ + threadId: proofRecord.threadId, + parentThreadId: proofRecord.parentThreadId, + }) + + return { + proofRecord, + message, + } + } + + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + options: { + proofRecord: ProofExchangeRecord + proposalMessage: V1ProposePresentationMessage + } + ): Promise { + const { proofRecord, proposalMessage } = options + + const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) + + const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + + // Handle always / never cases + if (autoAccept === AutoAcceptProof.Always) return true + if (autoAccept === AutoAcceptProof.Never) return false + + // We are in the ContentApproved case. We need to make sure we've sent a request, and it matches the proposal + const requestMessage = await this.findRequestMessage(agentContext, proofRecord.id) + const requestAttachment = requestMessage?.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + if (!requestAttachment) return false + + const rfc0592Proposal = JsonTransformer.toJSON( + createRequestFromPreview({ + name: 'Proof Request', + nonce: await agentContext.wallet.generateNonce(), + version: '1.0', + attributes: proposalMessage.presentationProposal.attributes, + predicates: proposalMessage.presentationProposal.predicates, + }) + ) + + return this.indyProofFormat.shouldAutoRespondToProposal(agentContext, { + proofRecord, + proposalAttachment: new Attachment({ + data: { + json: rfc0592Proposal, + }, + }), + requestAttachment, + }) + } + + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + options: { + proofRecord: ProofExchangeRecord + requestMessage: V1RequestPresentationMessage + } + ): Promise { + const { proofRecord, requestMessage } = options + + const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) + + const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + + // Handle always / never cases + if (autoAccept === AutoAcceptProof.Always) return true + if (autoAccept === AutoAcceptProof.Never) return false + + const requestAttachment = requestMessage.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + if (!requestAttachment) return false + + // We are in the ContentApproved case. We need to make sure we've sent a proposal, and it matches the request + const proposalMessage = await this.findProposalMessage(agentContext, proofRecord.id) + if (!proposalMessage) return false + + const rfc0592Proposal = createRequestFromPreview({ + name: 'Proof Request', + nonce: await agentContext.wallet.generateNonce(), + version: '1.0', + attributes: proposalMessage.presentationProposal.attributes, + predicates: proposalMessage.presentationProposal.predicates, + }).toJSON() + + return this.indyProofFormat.shouldAutoRespondToRequest(agentContext, { + proofRecord, + proposalAttachment: new Attachment({ + data: { + base64: JsonEncoder.toBase64(rfc0592Proposal), + }, + }), + requestAttachment, + }) + } + + public async shouldAutoRespondToPresentation( + agentContext: AgentContext, + options: { + proofRecord: ProofExchangeRecord + presentationMessage: V1PresentationMessage + } + ): Promise { + const { proofRecord, presentationMessage } = options + + const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) + + const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + + // Handle always / never cases + if (autoAccept === AutoAcceptProof.Always) return true + if (autoAccept === AutoAcceptProof.Never) return false + + const presentationAttachment = presentationMessage.getPresentationAttachmentById(INDY_PROOF_ATTACHMENT_ID) + if (!presentationAttachment) return false + + // We are in the ContentApproved case. We need to make sure we've sent a request, and it matches the presentation + const requestMessage = await this.findRequestMessage(agentContext, proofRecord.id) + const requestAttachment = requestMessage?.getRequestAttachmentById(INDY_PROOF_REQUEST_ATTACHMENT_ID) + if (!requestAttachment) return false + + // We are in the ContentApproved case. We need to make sure we've sent a proposal, and it matches the request + const proposalMessage = await this.findProposalMessage(agentContext, proofRecord.id) + + const rfc0592Proposal = proposalMessage + ? JsonTransformer.toJSON( + createRequestFromPreview({ + name: 'Proof Request', + nonce: await agentContext.wallet.generateNonce(), + version: '1.0', + attributes: proposalMessage.presentationProposal.attributes, + predicates: proposalMessage.presentationProposal.predicates, + }) + ) + : undefined + + return this.indyProofFormat.shouldAutoRespondToPresentation(agentContext, { + proofRecord, + requestAttachment, + presentationAttachment, + proposalAttachment: new Attachment({ + data: { + json: rfc0592Proposal, + }, + }), + }) + } + + public async findProposalMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + return await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V1ProposePresentationMessage, + }) + } + + public async findRequestMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + return await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V1RequestPresentationMessage, + }) + } + + public async findPresentationMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + return await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V1PresentationMessage, + }) + } + + public async getFormatData( + agentContext: AgentContext, + proofRecordId: string + ): Promise> { + // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. + const [proposalMessage, requestMessage, presentationMessage] = await Promise.all([ + this.findProposalMessage(agentContext, proofRecordId), + this.findRequestMessage(agentContext, proofRecordId), + this.findPresentationMessage(agentContext, proofRecordId), + ]) + + let indyProposeProof = undefined + const indyRequestProof = requestMessage?.indyProofRequest ?? undefined + const indyPresentProof = presentationMessage?.indyProof ?? undefined + + if (proposalMessage && indyRequestProof) { + indyProposeProof = createRequestFromPreview({ + name: indyRequestProof.name, + version: indyRequestProof.version, + nonce: indyRequestProof.nonce, + attributes: proposalMessage.presentationProposal.attributes, + predicates: proposalMessage.presentationProposal.predicates, + }) + } else if (proposalMessage) { + indyProposeProof = createRequestFromPreview({ + name: 'Proof Request', + version: '1.0', + nonce: await agentContext.wallet.generateNonce(), + attributes: proposalMessage.presentationProposal.attributes, + predicates: proposalMessage.presentationProposal.predicates, + }) + } + + return { + proposal: proposalMessage + ? { + indy: indyProposeProof?.toJSON(), + } + : undefined, + request: requestMessage + ? { + indy: indyRequestProof, + } + : undefined, + presentation: presentationMessage + ? { + indy: indyPresentProof, + } + : undefined, + } + } + + private assertOnlyIndyFormat(proofFormats: Record) { + const formatKeys = Object.keys(proofFormats) + + // It's fine to not have any formats in some cases, if indy is required the method that calls this should check for this + if (formatKeys.length === 0) return + + if (formatKeys.length !== 1 || !formatKeys.includes('indy')) { + throw new AriesFrameworkError('Only indy proof format is supported for present proof v1 protocol') + } + } +} diff --git a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts b/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts deleted file mode 100644 index 22b43cdc93..0000000000 --- a/packages/core/src/modules/proofs/protocol/v1/V1ProofService.ts +++ /dev/null @@ -1,1111 +0,0 @@ -import type { AgentContext } from '../../../../agent' -import type { AgentMessage } from '../../../../agent/AgentMessage' -import type { Dispatcher } from '../../../../agent/Dispatcher' -import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { MediationRecipientService } from '../../../routing/services/MediationRecipientService' -import type { RoutingService } from '../../../routing/services/RoutingService' -import type { ProofResponseCoordinator } from '../../ProofResponseCoordinator' -import type { ProofFormat } from '../../formats/ProofFormat' -import type { IndyProofFormat, IndyProposeProofFormat } from '../../formats/indy/IndyProofFormat' -import type { ProofAttributeInfo } from '../../formats/indy/models' -import type { - CreateProblemReportOptions, - FormatCreatePresentationOptions, -} from '../../formats/models/ProofFormatServiceOptions' -import type { - CreateAckOptions, - CreatePresentationOptions, - CreateProofRequestFromProposalOptions, - CreateProposalAsResponseOptions, - CreateProposalOptions, - CreateRequestAsResponseOptions, - CreateRequestOptions, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, - GetFormatDataReturn, - GetRequestedCredentialsForProofRequestOptions, - ProofRequestFromProposalOptions, -} from '../../models/ProofServiceOptions' - -import { validateOrReject } from 'class-validator' -import { inject, Lifecycle, scoped } from 'tsyringe' - -import { AgentConfig } from '../../../../agent/AgentConfig' -import { EventEmitter } from '../../../../agent/EventEmitter' -import { InjectionSymbols } from '../../../../constants' -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { DidCommMessageRole } from '../../../../storage' -import { DidCommMessageRepository } from '../../../../storage/didcomm/DidCommMessageRepository' -import { checkProofRequestForDuplicates } from '../../../../utils' -import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { MessageValidator } from '../../../../utils/MessageValidator' -import { Wallet } from '../../../../wallet' -import { AckStatus } from '../../../common/messages/AckMessage' -import { ConnectionService } from '../../../connections' -import { CredentialRepository } from '../../../credentials' -import { IndyCredentialInfo } from '../../../credentials/formats/indy/models/IndyCredentialInfo' -import { IndyHolderService, IndyRevocationService } from '../../../indy' -import { IndyLedgerService } from '../../../ledger/services/IndyLedgerService' -import { ProofService } from '../../ProofService' -import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' -import { IndyProofFormatService } from '../../formats/indy/IndyProofFormatService' -import { ProofRequest } from '../../formats/indy/models/ProofRequest' -import { RequestedCredentials } from '../../formats/indy/models/RequestedCredentials' -import { ProofState } from '../../models/ProofState' -import { ProofExchangeRecord } from '../../repository/ProofExchangeRecord' -import { ProofRepository } from '../../repository/ProofRepository' - -import { V1PresentationProblemReportError } from './errors' -import { - V1PresentationAckHandler, - V1PresentationHandler, - V1PresentationProblemReportHandler, - V1ProposePresentationHandler, - V1RequestPresentationHandler, -} from './handlers' -import { - INDY_PROOF_ATTACHMENT_ID, - INDY_PROOF_REQUEST_ATTACHMENT_ID, - V1PresentationAckMessage, - V1PresentationMessage, - V1ProposePresentationMessage, - V1RequestPresentationMessage, -} from './messages' -import { V1PresentationProblemReportMessage } from './messages/V1PresentationProblemReportMessage' -import { PresentationPreview } from './models/V1PresentationPreview' - -/** - * @todo add method to check if request matches proposal. Useful to see if a request I received is the same as the proposal I sent. - * @todo add method to reject / revoke messages - * @todo validate attachments / messages - */ -@scoped(Lifecycle.ContainerScoped) -export class V1ProofService extends ProofService<[IndyProofFormat]> { - private credentialRepository: CredentialRepository - private ledgerService: IndyLedgerService - private indyHolderService: IndyHolderService - private indyRevocationService: IndyRevocationService - private indyProofFormatService: IndyProofFormatService - - public constructor( - proofRepository: ProofRepository, - didCommMessageRepository: DidCommMessageRepository, - ledgerService: IndyLedgerService, - @inject(InjectionSymbols.Wallet) wallet: Wallet, - agentConfig: AgentConfig, - connectionService: ConnectionService, - eventEmitter: EventEmitter, - credentialRepository: CredentialRepository, - formatService: IndyProofFormatService, - indyHolderService: IndyHolderService, - indyRevocationService: IndyRevocationService - ) { - super(agentConfig, proofRepository, connectionService, didCommMessageRepository, wallet, eventEmitter) - this.credentialRepository = credentialRepository - this.ledgerService = ledgerService - this.wallet = wallet - this.indyProofFormatService = formatService - this.indyHolderService = indyHolderService - this.indyRevocationService = indyRevocationService - } - - /** - * The version of the present proof protocol this service supports - */ - public readonly version = 'v1' as const - - public async createProposal( - agentContext: AgentContext, - options: CreateProposalOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const { connectionRecord, proofFormats } = options - - // Assert - connectionRecord.assertReady() - - if (!proofFormats.indy || Object.keys(proofFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - - const presentationProposal = new PresentationPreview({ - attributes: proofFormats.indy?.attributes, - predicates: proofFormats.indy?.predicates, - }) - - // Create message - const proposalMessage = new V1ProposePresentationMessage({ - comment: options?.comment, - presentationProposal, - parentThreadId: options.parentThreadId, - }) - - // Create record - const proofRecord = new ProofExchangeRecord({ - connectionId: connectionRecord.id, - threadId: proposalMessage.threadId, - parentThreadId: proposalMessage.thread?.parentThreadId, - state: ProofState.ProposalSent, - autoAcceptProof: options?.autoAcceptProof, - protocolVersion: 'v1', - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: proposalMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - await this.proofRepository.save(agentContext, proofRecord) - this.emitStateChangedEvent(agentContext, proofRecord, null) - - return { proofRecord, message: proposalMessage } - } - - public async createProposalAsResponse( - agentContext: AgentContext, - options: CreateProposalAsResponseOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const { proofRecord, proofFormats, comment } = options - - // Assert - proofRecord.assertState(ProofState.RequestReceived) - - if (!proofFormats.indy || Object.keys(proofFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - - // Create message - const presentationPreview = new PresentationPreview({ - attributes: proofFormats.indy?.attributes, - predicates: proofFormats.indy?.predicates, - }) - - const proposalMessage: V1ProposePresentationMessage = new V1ProposePresentationMessage({ - comment, - presentationProposal: presentationPreview, - }) - - proposalMessage.setThread({ threadId: proofRecord.threadId }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: proposalMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - // Update record - await this.updateState(agentContext, proofRecord, ProofState.ProposalSent) - - return { proofRecord, message: proposalMessage } - } - - public async processProposal( - messageContext: InboundMessageContext - ): Promise { - let proofRecord: ProofExchangeRecord - const { message: proposalMessage, connection } = messageContext - - this.logger.debug(`Processing presentation proposal with id ${proposalMessage.id}`) - - try { - // Proof record already exists - proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - proposalMessage.threadId, - connection?.id - ) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.RequestSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proposalMessage, - previousSentMessage: requestMessage ?? undefined, - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proposalMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Update record - await this.updateState(messageContext.agentContext, proofRecord, ProofState.ProposalReceived) - } catch { - // No proof record exists with thread id - proofRecord = new ProofExchangeRecord({ - connectionId: connection?.id, - threadId: proposalMessage.threadId, - parentThreadId: proposalMessage.thread?.parentThreadId, - state: ProofState.ProposalReceived, - protocolVersion: 'v1', - }) - - // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) - - // Save record - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proposalMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - await this.proofRepository.save(messageContext.agentContext, proofRecord) - - this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) - } - - return proofRecord - } - - public async createRequestAsResponse( - agentContext: AgentContext, - options: CreateRequestAsResponseOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const { proofRecord, comment, proofFormats } = options - if (!proofFormats.indy) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - - // Assert - proofRecord.assertState(ProofState.ProposalReceived) - - // Create message - const { attachment } = await this.indyProofFormatService.createRequest({ - id: INDY_PROOF_REQUEST_ATTACHMENT_ID, - formats: proofFormats, - }) - - const requestPresentationMessage = new V1RequestPresentationMessage({ - comment, - requestPresentationAttachments: [attachment], - }) - requestPresentationMessage.setThread({ - threadId: proofRecord.threadId, - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: requestPresentationMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - // Update record - await this.updateState(agentContext, proofRecord, ProofState.RequestSent) - - return { message: requestPresentationMessage, proofRecord } - } - - public async createRequest( - agentContext: AgentContext, - options: CreateRequestOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - this.logger.debug(`Creating proof request`) - - // Assert - if (options.connectionRecord) { - options.connectionRecord.assertReady() - } - - if (!options.proofFormats.indy || Object.keys(options.proofFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - - // Create message - const { attachment } = await this.indyProofFormatService.createRequest({ - id: INDY_PROOF_REQUEST_ATTACHMENT_ID, - formats: options.proofFormats, - }) - - const requestPresentationMessage = new V1RequestPresentationMessage({ - comment: options?.comment, - requestPresentationAttachments: [attachment], - parentThreadId: options.parentThreadId, - }) - - // Create record - const proofRecord = new ProofExchangeRecord({ - connectionId: options.connectionRecord?.id, - threadId: requestPresentationMessage.threadId, - parentThreadId: requestPresentationMessage.thread?.parentThreadId, - state: ProofState.RequestSent, - autoAcceptProof: options?.autoAcceptProof, - protocolVersion: 'v1', - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: requestPresentationMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - await this.proofRepository.save(agentContext, proofRecord) - this.emitStateChangedEvent(agentContext, proofRecord, null) - - return { message: requestPresentationMessage, proofRecord } - } - - public async processRequest( - messageContext: InboundMessageContext - ): Promise { - let proofRecord: ProofExchangeRecord - const { message: proofRequestMessage, connection } = messageContext - - this.logger.debug(`Processing presentation request with id ${proofRequestMessage.id}`) - - const requestAttachments = proofRequestMessage.getAttachmentFormats() - - for (const attachmentFormat of requestAttachments) { - await this.indyProofFormatService.processRequest({ - requestAttachment: attachmentFormat, - }) - } - - const proofRequest = proofRequestMessage.indyProofRequest - - // Assert attachment - if (!proofRequest) { - throw new V1PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - await validateOrReject(proofRequest) - - // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(proofRequest) - - this.logger.debug('received proof request', proofRequest) - - try { - // Proof record already exists - proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - proofRequestMessage.threadId, - connection?.id - ) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.ProposalSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: requestMessage ?? undefined, - previousSentMessage: proposalMessage ?? undefined, - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proofRequestMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Update record - await this.updateState(messageContext.agentContext, proofRecord, ProofState.RequestReceived) - } catch { - // No proof record exists with thread id - proofRecord = new ProofExchangeRecord({ - connectionId: connection?.id, - threadId: proofRequestMessage.threadId, - parentThreadId: proofRequestMessage.thread?.parentThreadId, - state: ProofState.RequestReceived, - protocolVersion: 'v1', - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proofRequestMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) - - // Save in repository - await this.proofRepository.save(messageContext.agentContext, proofRecord) - this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) - } - - return proofRecord - } - - public async createPresentation( - agentContext: AgentContext, - options: CreatePresentationOptions<[IndyProofFormat]> - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const { proofRecord, proofFormats } = options - - this.logger.debug(`Creating presentation for proof record with id ${proofRecord.id}`) - - if (!proofFormats.indy || Object.keys(proofFormats).length !== 1) { - throw new AriesFrameworkError('Only indy proof format is supported for present proof protocol v1') - } - - // Assert - proofRecord.assertState(ProofState.RequestReceived) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - const requestAttachment = requestMessage?.indyAttachment - - if (!requestAttachment) { - throw new V1PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation with thread id ${proofRecord.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - - const presentationOptions: FormatCreatePresentationOptions = { - id: INDY_PROOF_ATTACHMENT_ID, - attachment: requestAttachment, - proofFormats: proofFormats, - } - - const proof = await this.indyProofFormatService.createPresentation(agentContext, presentationOptions) - - // Extract proof request from attachment - const proofRequestJson = requestAttachment.getDataAsJson() ?? null - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - const requestedCredentials = new RequestedCredentials({ - requestedAttributes: proofFormats.indy?.requestedAttributes, - requestedPredicates: proofFormats.indy?.requestedPredicates, - selfAttestedAttributes: proofFormats.indy?.selfAttestedAttributes, - }) - - // Get the matching attachments to the requested credentials - const linkedAttachments = await this.getRequestedAttachmentsForRequestedCredentials( - agentContext, - proofRequest, - requestedCredentials - ) - - const presentationMessage = new V1PresentationMessage({ - comment: options?.comment, - presentationAttachments: [proof.attachment], - attachments: linkedAttachments, - }) - presentationMessage.setThread({ threadId: proofRecord.threadId }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: presentationMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - // Update record - await this.updateState(agentContext, proofRecord, ProofState.PresentationSent) - - return { message: presentationMessage, proofRecord } - } - - public async processPresentation( - messageContext: InboundMessageContext - ): Promise { - const { message: presentationMessage, connection } = messageContext - - this.logger.debug(`Processing presentation with id ${presentationMessage.id}`) - - const proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - presentationMessage.threadId, - connection?.id - ) - - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.RequestSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proposalMessage ?? undefined, - previousSentMessage: requestMessage ?? undefined, - }) - - try { - const isValid = await this.indyProofFormatService.processPresentation(messageContext.agentContext, { - record: proofRecord, - formatAttachments: { - presentation: presentationMessage.getAttachmentFormats(), - request: requestMessage.getAttachmentFormats(), - }, - }) - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: presentationMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Update record - proofRecord.isVerified = isValid - await this.updateState(messageContext.agentContext, proofRecord, ProofState.PresentationReceived) - } catch (e) { - if (e instanceof AriesFrameworkError) { - throw new V1PresentationProblemReportError(e.message, { - problemCode: PresentationProblemReportReason.Abandoned, - }) - } - throw e - } - - return proofRecord - } - - public async processAck( - messageContext: InboundMessageContext - ): Promise { - const { message: presentationAckMessage, connection } = messageContext - - this.logger.debug(`Processing presentation ack with id ${presentationAckMessage.id}`) - - const proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - presentationAckMessage.threadId, - connection?.id - ) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - const presentationMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1PresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.PresentationSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: requestMessage ?? undefined, - previousSentMessage: presentationMessage ?? undefined, - }) - - // Update record - await this.updateState(messageContext.agentContext, proofRecord, ProofState.Done) - - return proofRecord - } - - public async createProblemReport( - agentContext: AgentContext, - options: CreateProblemReportOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const msg = new V1PresentationProblemReportMessage({ - description: { - code: PresentationProblemReportReason.Abandoned, - en: options.description, - }, - }) - - msg.setThread({ - threadId: options.proofRecord.threadId, - parentThreadId: options.proofRecord.parentThreadId, - }) - - return { - proofRecord: options.proofRecord, - message: msg, - } - } - - public async processProblemReport( - messageContext: InboundMessageContext - ): Promise { - const { message: presentationProblemReportMessage } = messageContext - - const connection = messageContext.assertReadyConnection() - - this.logger.debug(`Processing problem report with id ${presentationProblemReportMessage.id}`) - - const proofRecord = await this.getByThreadAndConnectionId( - messageContext.agentContext, - presentationProblemReportMessage.threadId, - connection?.id - ) - - proofRecord.errorMessage = `${presentationProblemReportMessage.description.code}: ${presentationProblemReportMessage.description.en}` - await this.updateState(messageContext.agentContext, proofRecord, ProofState.Abandoned) - return proofRecord - } - - public async createProofRequestFromProposal( - agentContext: AgentContext, - options: CreateProofRequestFromProposalOptions - ): Promise> { - const proofRecordId = options.proofRecord.id - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V1ProposePresentationMessage, - }) - - if (!proposalMessage) { - throw new AriesFrameworkError(`Proof record with id ${proofRecordId} is missing required presentation proposal`) - } - - const indyProposeProofFormat: IndyProposeProofFormat = { - name: 'Proof Request', - version: '1.0', - nonce: await this.wallet.generateNonce(), - } - - const proofRequest: ProofRequest = await this.indyProofFormatService.createReferentForProofRequest( - indyProposeProofFormat, - proposalMessage.presentationProposal - ) - - return { - proofRecord: options.proofRecord, - proofFormats: { - indy: proofRequest, - }, - } - } - - /** - * Retrieves the linked attachments for an {@link indyProofRequest} - * @param indyProofRequest The proof request for which the linked attachments have to be found - * @param requestedCredentials The requested credentials - * @returns a list of attachments that are linked to the requested credentials - */ - public async getRequestedAttachmentsForRequestedCredentials( - agentContext: AgentContext, - indyProofRequest: ProofRequest, - requestedCredentials: RequestedCredentials - ): Promise { - const attachments: Attachment[] = [] - const credentialIds = new Set() - const requestedAttributesNames: (string | undefined)[] = [] - - // Get the credentialIds if it contains a hashlink - for (const [referent, requestedAttribute] of Object.entries(requestedCredentials.requestedAttributes)) { - // Find the requested Attributes - const requestedAttributes = indyProofRequest.requestedAttributes.get(referent) as ProofAttributeInfo - - // List the requested attributes - requestedAttributesNames.push(...(requestedAttributes.names ?? [requestedAttributes.name])) - - //Get credentialInfo - if (!requestedAttribute.credentialInfo) { - const indyCredentialInfo = await this.indyHolderService.getCredential( - agentContext, - requestedAttribute.credentialId - ) - requestedAttribute.credentialInfo = JsonTransformer.fromJSON(indyCredentialInfo, IndyCredentialInfo) - } - - // Find the attributes that have a hashlink as a value - for (const attribute of Object.values(requestedAttribute.credentialInfo.attributes)) { - if (attribute.toLowerCase().startsWith('hl:')) { - credentialIds.add(requestedAttribute.credentialId) - } - } - } - - // Only continues if there is an attribute value that contains a hashlink - for (const credentialId of credentialIds) { - // Get the credentialRecord that matches the ID - - const credentialRecord = await this.credentialRepository.getSingleByQuery(agentContext, { - credentialIds: [credentialId], - }) - - if (credentialRecord.linkedAttachments) { - // Get the credentials that have a hashlink as value and are requested - const requestedCredentials = credentialRecord.credentialAttributes?.filter( - (credential) => - credential.value.toLowerCase().startsWith('hl:') && requestedAttributesNames.includes(credential.name) - ) - - // Get the linked attachments that match the requestedCredentials - const linkedAttachments = credentialRecord.linkedAttachments.filter((attachment) => - requestedCredentials?.map((credential) => credential.value.split(':')[1]).includes(attachment.id) - ) - - if (linkedAttachments) { - attachments.push(...linkedAttachments) - } - } - } - - return attachments.length ? attachments : undefined - } - - public async shouldAutoRespondToProposal( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise { - const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - if (!proposal) return false - MessageValidator.validateSync(proposal) - - // check the proposal against a possible previous request - const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - if (!request) return false - - const proofRequest = request.indyProofRequest - - if (!proofRequest) { - throw new V1PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation request with thread id ${request.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - await validateOrReject(proofRequest) - - // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(proofRequest) - - const proposalAttributes = proposal.presentationProposal.attributes - const requestedAttributes = proofRequest.requestedAttributes - - const proposedAttributeNames = proposalAttributes.map((x) => x.name) - let requestedAttributeNames: string[] = [] - - const requestedAttributeList = Array.from(requestedAttributes.values()) - - requestedAttributeList.forEach((x) => { - if (x.name) { - requestedAttributeNames.push(x.name) - } else if (x.names) { - requestedAttributeNames = requestedAttributeNames.concat(x.names) - } - }) - - if (requestedAttributeNames.length > proposedAttributeNames.length) { - // more attributes are requested than have been proposed - return false - } - - requestedAttributeNames.forEach((x) => { - if (!proposedAttributeNames.includes(x)) { - this.logger.debug(`Attribute ${x} was requested but wasn't proposed.`) - return false - } - }) - - // assert that all requested attributes are provided - const providedPredicateNames = proposal.presentationProposal.predicates.map((x) => x.name) - proofRequest.requestedPredicates.forEach((x) => { - if (!providedPredicateNames.includes(x.name)) { - return false - } - }) - return true - } - - public async shouldAutoRespondToRequest( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise { - const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - if (!proposal) { - return false - } - - const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - if (!request) { - throw new AriesFrameworkError( - `Expected to find a request message for ProofExchangeRecord with id ${proofRecord.id}` - ) - } - - const proofRequest = request.indyProofRequest - - // Assert attachment - if (!proofRequest) { - throw new V1PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation request with thread id ${request.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - await validateOrReject(proofRequest) - - // Assert attribute and predicate (group) names do not match - checkProofRequestForDuplicates(proofRequest) - - const proposalAttributes = proposal.presentationProposal.attributes - const requestedAttributes = proofRequest.requestedAttributes - - const proposedAttributeNames = proposalAttributes.map((x) => x.name) - let requestedAttributeNames: string[] = [] - - const requestedAttributeList = Array.from(requestedAttributes.values()) - - requestedAttributeList.forEach((x) => { - if (x.name) { - requestedAttributeNames.push(x.name) - } else if (x.names) { - requestedAttributeNames = requestedAttributeNames.concat(x.names) - } - }) - - if (requestedAttributeNames.length > proposedAttributeNames.length) { - // more attributes are requested than have been proposed - return false - } - - requestedAttributeNames.forEach((x) => { - if (!proposedAttributeNames.includes(x)) { - this.logger.debug(`Attribute ${x} was requested but wasn't proposed.`) - return false - } - }) - - // assert that all requested attributes are provided - const providedPredicateNames = proposal.presentationProposal.predicates.map((x) => x.name) - proofRequest.requestedPredicates.forEach((x) => { - if (!providedPredicateNames.includes(x.name)) { - return false - } - }) - - return true - } - - public async shouldAutoRespondToPresentation( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise { - this.logger.debug(`Should auto respond to presentation for proof record id: ${proofRecord.id}`) - return true - } - - public async getRequestedCredentialsForProofRequest( - agentContext: AgentContext, - options: GetRequestedCredentialsForProofRequestOptions - ): Promise> { - const requestMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - const indyProofRequest = requestMessage?.requestPresentationAttachments - - if (!indyProofRequest) { - throw new AriesFrameworkError('Could not find proof request') - } - - const requestedCredentials: FormatRetrievedCredentialOptions<[IndyProofFormat]> = - await this.indyProofFormatService.getRequestedCredentialsForProofRequest(agentContext, { - attachment: indyProofRequest[0], - presentationProposal: proposalMessage?.presentationProposal, - config: options.config ?? undefined, - }) - return requestedCredentials - } - - public async autoSelectCredentialsForProofRequest( - options: FormatRetrievedCredentialOptions - ): Promise> { - return await this.indyProofFormatService.autoSelectCredentialsForProofRequest(options) - } - - public registerMessageHandlers( - dispatcher: Dispatcher, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - mediationRecipientService: MediationRecipientService, - routingService: RoutingService - ): void { - dispatcher.registerMessageHandler( - new V1ProposePresentationHandler(this, agentConfig, proofResponseCoordinator, this.didCommMessageRepository) - ) - - dispatcher.registerMessageHandler( - new V1RequestPresentationHandler( - this, - agentConfig, - proofResponseCoordinator, - mediationRecipientService, - this.didCommMessageRepository, - routingService - ) - ) - - dispatcher.registerMessageHandler( - new V1PresentationHandler(this, agentConfig, proofResponseCoordinator, this.didCommMessageRepository) - ) - dispatcher.registerMessageHandler(new V1PresentationAckHandler(this)) - dispatcher.registerMessageHandler(new V1PresentationProblemReportHandler(this)) - } - - public async findRequestMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V1RequestPresentationMessage, - }) - } - public async findPresentationMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V1PresentationMessage, - }) - } - - public async findProposalMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V1ProposePresentationMessage, - }) - } - - public async getFormatData( - agentContext: AgentContext, - proofRecordId: string - ): Promise> { - const [proposalMessage, requestMessage, presentationMessage] = await Promise.all([ - this.findProposalMessage(agentContext, proofRecordId), - this.findRequestMessage(agentContext, proofRecordId), - this.findPresentationMessage(agentContext, proofRecordId), - ]) - - const indyProposeProof = proposalMessage - ? JsonTransformer.toJSON(await this.rfc0592ProposalFromV1ProposeMessage(proposalMessage)) - : undefined - const indyRequestProof = requestMessage?.indyProofRequestJson ?? undefined - const indyPresentProof = presentationMessage?.indyProof ?? undefined - - return { - proposal: proposalMessage - ? { - indy: indyProposeProof, - } - : undefined, - request: requestMessage - ? { - indy: indyRequestProof, - } - : undefined, - presentation: presentationMessage - ? { - indy: indyPresentProof, - } - : undefined, - } - } - - private async rfc0592ProposalFromV1ProposeMessage( - proposalMessage: V1ProposePresentationMessage - ): Promise { - const indyFormat: IndyProposeProofFormat = { - name: 'Proof Request', - version: '1.0', - nonce: await this.wallet.generateNonce(), - attributes: proposalMessage.presentationProposal.attributes, - predicates: proposalMessage.presentationProposal.predicates, - } - - if (!indyFormat) { - throw new AriesFrameworkError('No Indy format found.') - } - - const preview = new PresentationPreview({ - attributes: indyFormat.attributes, - predicates: indyFormat.predicates, - }) - - return this.indyProofFormatService.createReferentForProofRequest(indyFormat, preview) - } - /** - * Retrieve all proof records - * - * @returns List containing all proof records - */ - public async getAll(agentContext: AgentContext): Promise { - return this.proofRepository.getAll(agentContext) - } - - /** - * Retrieve a proof record by connection id and thread id - * - * @param connectionId The connection id - * @param threadId The thread id - * @throws {RecordNotFoundError} If no record is found - * @throws {RecordDuplicateError} If multiple records are found - * @returns The proof record - */ - public async getByThreadAndConnectionId( - agentContext: AgentContext, - threadId: string, - connectionId?: string - ): Promise { - return this.proofRepository.getSingleByQuery(agentContext, { threadId, connectionId }) - } - - public async createAck( - gentContext: AgentContext, - options: CreateAckOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const { proofRecord } = options - this.logger.debug(`Creating presentation ack for proof record with id ${proofRecord.id}`) - - // Assert - proofRecord.assertState(ProofState.PresentationReceived) - - // Create message - const ackMessage = new V1PresentationAckMessage({ - status: AckStatus.OK, - threadId: proofRecord.threadId, - }) - - // Update record - await this.updateState(gentContext, proofRecord, ProofState.Done) - - return { message: ackMessage, proofRecord } - } -} diff --git a/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/V1ProofProtocol.test.ts similarity index 63% rename from packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/V1ProofProtocol.test.ts index 3da57e27c1..0b1febb680 100644 --- a/packages/core/src/modules/proofs/__tests__/V1ProofService.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/V1ProofProtocol.test.ts @@ -1,51 +1,43 @@ -import type { CustomProofTags } from './../repository/ProofExchangeRecord' -import type { AgentContext } from '../../../agent' -import type { Wallet } from '../../../wallet/Wallet' -import type { CredentialRepository } from '../../credentials/repository' -import type { ProofStateChangedEvent } from '../ProofEvents' +import type { AgentContext } from '../../../../../agent' +import type { AgentConfig } from '../../../../../agent/AgentConfig' +import type { ProofStateChangedEvent } from '../../../ProofEvents' +import type { CustomProofTags } from '../../../repository/ProofExchangeRecord' import { Subject } from 'rxjs' -import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../tests/helpers' -import { EventEmitter } from '../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { DidCommMessageRepository } from '../../../storage' -import { ConnectionService, DidExchangeState } from '../../connections' -import { IndyHolderService } from '../../indy/services/IndyHolderService' -import { IndyRevocationService } from '../../indy/services/IndyRevocationService' -import { IndyLedgerService } from '../../ledger/services' -import { ProofEventTypes } from '../ProofEvents' -import { PresentationProblemReportReason } from '../errors/PresentationProblemReportReason' -import { IndyProofFormatService } from '../formats/indy/IndyProofFormatService' -import { ProofState } from '../models/ProofState' -import { V1ProofService } from '../protocol/v1' -import { INDY_PROOF_REQUEST_ATTACHMENT_ID, V1RequestPresentationMessage } from '../protocol/v1/messages' -import { V1PresentationProblemReportMessage } from '../protocol/v1/messages/V1PresentationProblemReportMessage' -import { ProofExchangeRecord } from '../repository/ProofExchangeRecord' -import { ProofRepository } from '../repository/ProofRepository' - -import { credDef } from './fixtures' +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { DidCommMessageRepository } from '../../../../../storage' +import { ConnectionService, DidExchangeState } from '../../../../connections' +import { ProofEventTypes } from '../../../ProofEvents' +import { PresentationProblemReportReason } from '../../../errors/PresentationProblemReportReason' +import { IndyProofFormatService } from '../../../formats/indy/IndyProofFormatService' +import { ProofState } from '../../../models/ProofState' +import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' +import { ProofRepository } from '../../../repository/ProofRepository' +import { V1ProofProtocol } from '../V1ProofProtocol' +import { INDY_PROOF_REQUEST_ATTACHMENT_ID, V1RequestPresentationMessage } from '../messages' +import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' // Mock classes -jest.mock('../repository/ProofRepository') -jest.mock('../../../modules/ledger/services/IndyLedgerService') -jest.mock('../../indy/services/IndyHolderService') -jest.mock('../../indy/services/IndyIssuerService') -jest.mock('../../indy/services/IndyVerifierService') -jest.mock('../../indy/services/IndyRevocationService') -jest.mock('../../connections/services/ConnectionService') -jest.mock('../../../storage/Repository') +jest.mock('../../../repository/ProofRepository') +jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') +jest.mock('../../../../connections/services/ConnectionService') +jest.mock('../../../../../storage/Repository') // Mock typed object const ProofRepositoryMock = ProofRepository as jest.Mock -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const IndyHolderServiceMock = IndyHolderService as jest.Mock -const IndyRevocationServiceMock = IndyRevocationService as jest.Mock const connectionServiceMock = ConnectionService as jest.Mock const didCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock const indyProofFormatServiceMock = IndyProofFormatService as jest.Mock +const proofRepository = new ProofRepositoryMock() +const connectionService = new connectionServiceMock() +const didCommMessageRepository = new didCommMessageRepositoryMock() +const indyProofFormatService = new indyProofFormatServiceMock() + const connection = getMockConnection({ id: '123', state: DidExchangeState.Completed, @@ -78,7 +70,7 @@ const mockProofExchangeRecord = ({ } = {}) => { const requestPresentationMessage = new V1RequestPresentationMessage({ comment: 'some comment', - requestPresentationAttachments: [requestAttachment], + requestAttachments: [requestAttachment], }) const proofRecord = new ProofExchangeRecord({ @@ -93,58 +85,37 @@ const mockProofExchangeRecord = ({ return proofRecord } -describe('V1ProofService', () => { - let proofRepository: ProofRepository - let proofService: V1ProofService - let ledgerService: IndyLedgerService - let wallet: Wallet - let indyHolderService: IndyHolderService - let indyRevocationService: IndyRevocationService +describe('V1ProofProtocol', () => { let eventEmitter: EventEmitter - let credentialRepository: CredentialRepository - let connectionService: ConnectionService - let didCommMessageRepository: DidCommMessageRepository - let indyProofFormatService: IndyProofFormatService + let agentConfig: AgentConfig let agentContext: AgentContext + let proofProtocol: V1ProofProtocol beforeEach(() => { - const agentConfig = getAgentConfig('V1ProofServiceTest') - agentContext = getAgentContext() - proofRepository = new ProofRepositoryMock() - indyHolderService = new IndyHolderServiceMock() - indyRevocationService = new IndyRevocationServiceMock() - ledgerService = new IndyLedgerServiceMock() + // real objects + agentConfig = getAgentConfig('V1ProofProtocolTest') eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) - connectionService = new connectionServiceMock() - didCommMessageRepository = new didCommMessageRepositoryMock() - indyProofFormatService = new indyProofFormatServiceMock() - agentContext = getAgentContext() - - proofService = new V1ProofService( - proofRepository, - didCommMessageRepository, - ledgerService, - wallet, + + agentContext = getAgentContext({ + registerInstances: [ + [ProofRepository, proofRepository], + [DidCommMessageRepository, didCommMessageRepository], + [EventEmitter, eventEmitter], + [ConnectionService, connectionService], + ], agentConfig, - connectionService, - eventEmitter, - credentialRepository, - indyProofFormatService, - indyHolderService, - indyRevocationService - ) - - mockFunction(ledgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) + }) + proofProtocol = new V1ProofProtocol({ indyProofFormat: indyProofFormatService }) }) - describe('processProofRequest', () => { + describe('processRequest', () => { let presentationRequest: V1RequestPresentationMessage let messageContext: InboundMessageContext beforeEach(() => { presentationRequest = new V1RequestPresentationMessage({ comment: 'abcd', - requestPresentationAttachments: [requestAttachment], + requestAttachments: [requestAttachment], }) messageContext = new InboundMessageContext(presentationRequest, { connection, @@ -156,7 +127,7 @@ describe('V1ProofService', () => { const repositorySaveSpy = jest.spyOn(proofRepository, 'save') // when - const returnedProofExchangeRecord = await proofService.processRequest(messageContext) + const returnedProofExchangeRecord = await proofProtocol.processRequest(messageContext) // then const expectedProofExchangeRecord = { @@ -178,7 +149,7 @@ describe('V1ProofService', () => { eventEmitter.on(ProofEventTypes.ProofStateChanged, eventListenerMock) // when - await proofService.processRequest(messageContext) + await proofProtocol.processRequest(messageContext) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -261,7 +232,7 @@ describe('V1ProofService', () => { mockFunction(proofRepository.getSingleByQuery).mockReturnValue(Promise.resolve(proof)) // when - const returnedCredentialRecord = await proofService.processProblemReport(messageContext) + const returnedCredentialRecord = await proofProtocol.processProblemReport(messageContext) // then const expectedCredentialRecord = { diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts index ee7b481cbb..e7f7a6f5f1 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts @@ -1,8 +1,8 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions, NegotiateProposalOptions } from '../../../ProofsApiOptions' +import type { AcceptProofProposalOptions, NegotiateProofProposalOptions } from '../../../ProofsApiOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { PresentationPreview } from '../models/V1PresentationPreview' +import type { V1PresentationPreview } from '../models/V1PresentationPreview' import type { CredDefId } from 'indy-sdk' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' @@ -20,7 +20,7 @@ describe('Present Proof', () => { let aliceAgent: Agent let credDefId: CredDefId let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository @@ -54,7 +54,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes.filter((attribute) => attribute.name !== 'name'), predicates: presentationPreview.predicates, @@ -135,7 +134,7 @@ describe('Present Proof', () => { }), } - const requestProofAsResponseOptions: NegotiateProposalOptions = { + const requestProofAsResponseOptions: NegotiateProofProposalOptions = { proofRecordId: faberProofExchangeRecord.id, proofFormats: { indy: { @@ -169,7 +168,7 @@ describe('Present Proof', () => { expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -200,7 +199,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes.filter((attribute) => attribute.name === 'name'), predicates: presentationPreview.predicates, @@ -276,7 +274,7 @@ describe('Present Proof', () => { expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -296,9 +294,8 @@ describe('Present Proof', () => { protocolVersion: 'v1', }) - const presentationProposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) - - expect(presentationProposalMessage).toMatchObject({ + const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) + expect(proposalMessage).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', id: expect.any(String), comment: 'V1 propose proof test 2', @@ -327,29 +324,31 @@ describe('Present Proof', () => { aliceProofExchangeRecord.id )) as V1RequestPresentationMessage - const predicateKey = proofRequestMessage.indyProofRequest?.requestedPredicates?.keys().next().value - const predicate = Object.values(predicates)[0] - + const predicateKey = Object.keys(proofRequestMessage.indyProofRequest?.requested_predicates ?? {})[0] expect(proofRequestMessage.indyProofRequest).toMatchObject({ name: 'Proof Request', version: '1.0', - requestedAttributes: new Map( - Object.entries({ - '0': new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - }) - ), - requestedPredicates: new Map( - Object.entries({ - [predicateKey]: predicate, - }) - ), + requested_attributes: { + '0': { + name: 'name', + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, + requested_predicates: { + [predicateKey]: { + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, }) }) }) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts similarity index 93% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts index 8b32bbe14c..86095b8f01 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts @@ -1,19 +1,19 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { PresentationPreview } from '../models/V1PresentationPreview' +import type { V1PresentationPreview } from '../models/V1PresentationPreview' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage/didcomm' import { ProofState } from '../../../models/ProofState' -import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' +import { ProofExchangeRecord } from '../../../repository' import { V1PresentationMessage, V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository @@ -21,8 +21,8 @@ describe('Present Proof', () => { beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' + 'Faber Agent Proofs', + 'Alice Agent Proofs' )) }) @@ -47,7 +47,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'ProofRequest', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, @@ -128,7 +127,7 @@ describe('Present Proof', () => { expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -150,11 +149,8 @@ describe('Present Proof', () => { }) test(`Alice accepts presentation request from Faber`, async () => { - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { @@ -217,7 +213,7 @@ describe('Present Proof', () => { }) // Faber accepts the presentation provided by Alice - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts similarity index 95% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts index 606f1e7ff8..f69048fece 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts @@ -1,7 +1,7 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { PresentationPreview } from '../models/V1PresentationPreview' +import type { V1PresentationPreview } from '../models/V1PresentationPreview' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' @@ -13,7 +13,7 @@ describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository @@ -46,7 +46,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'ProofRequest', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, diff --git a/packages/core/tests/v1-connectionless-proofs.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts similarity index 89% rename from packages/core/tests/v1-connectionless-proofs.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index d2fe8af3c3..fcfaaaebf1 100644 --- a/packages/core/tests/v1-connectionless-proofs.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -1,36 +1,29 @@ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { ProofStateChangedEvent } from '../src/modules/proofs' +import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import type { ProofStateChangedEvent } from '../../../ProofEvents' import { Subject, ReplaySubject } from 'rxjs' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { Agent } from '../src/agent/Agent' -import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' -import { HandshakeProtocol } from '../src/modules/connections' -import { V1CredentialPreview } from '../src/modules/credentials' +import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' import { - PredicateType, - ProofState, - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - AutoAcceptProof, - ProofEventTypes, -} from '../src/modules/proofs' -import { MediatorPickupStrategy } from '../src/modules/routing' -import { LinkedAttachment } from '../src/utils/LinkedAttachment' -import { uuid } from '../src/utils/uuid' - -import { - getAgentOptions, - issueCredential, - makeConnection, - prepareForIssuance, setupProofsTest, waitForProofExchangeRecordSubject, -} from './helpers' -import testLogger from './logger' + getAgentOptions, + prepareForIssuance, + makeConnection, + issueCredential, +} from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { Agent } from '../../../../../agent/Agent' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' +import { uuid } from '../../../../../utils/uuid' +import { HandshakeProtocol } from '../../../../connections' +import { V1CredentialPreview } from '../../../../credentials' +import { MediatorPickupStrategy } from '../../../../routing' +import { ProofEventTypes } from '../../../ProofEvents' +import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { AutoAcceptProof, ProofState } from '../../../models' describe('Present Proof', () => { let agents: Agent[] @@ -86,7 +79,6 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - nonce: '12345678901', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -104,11 +96,8 @@ describe('Present Proof', () => { let aliceProofExchangeRecord = await aliceProofExchangeRecordPromise testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { @@ -133,7 +122,7 @@ describe('Present Proof', () => { }) // Faber accepts presentation - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits till it receives presentation ack aliceProofExchangeRecord = await aliceProofExchangeRecordPromise @@ -189,7 +178,6 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - nonce: '12345678901', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -353,7 +341,6 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - nonce: '12345678901', requestedAttributes: attributes, requestedPredicates: predicates, }, diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proof-request.e2e.test.ts similarity index 70% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proof-request.e2e.test.ts index c3bccd9f9b..8c9278b879 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proof-request.e2e.test.ts @@ -1,23 +1,19 @@ import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { PresentationPreview } from '../models/V1PresentationPreview' +import type { ConnectionRecord } from '../../../../connections' +import type { ProofExchangeRecord } from '../../../repository' +import type { V1PresentationPreview } from '../models' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage/didcomm' -import { ProofState } from '../../../models/ProofState' -import { V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' +import { ProofState } from '../../../models' -describe('Present Proof', () => { +describe('Present Proof | V1ProofProtocol', () => { let faberAgent: Agent let aliceAgent: Agent let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository beforeAll(async () => { testLogger.test('Initializing the agents') @@ -47,8 +43,7 @@ describe('Present Proof', () => { protocolVersion: 'v1', proofFormats: { indy: { - name: 'ProofRequest', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + name: 'Proof Request', version: '1.0', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, @@ -60,13 +55,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1ProposePresentationMessage, - }) - + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', id: expect.any(String), @@ -103,34 +92,26 @@ describe('Present Proof', () => { }) }) - test(`Faber accepts the Proposal send by Alice and Creates Proof Request`, async () => { - // Accept Proposal - const acceptProposalOptions: AcceptProofProposalOptions = { - proofRecordId: faberProofExchangeRecord.id, - } - + test(`Faber accepts the Proposal sent by Alice and Creates Proof Request`, async () => { const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, }) + // Accept Proposal testLogger.test('Faber accepts presentation proposal from Alice') - faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ + proofRecordId: faberProofExchangeRecord.id, + }) testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -143,6 +124,7 @@ describe('Present Proof', () => { threadId: faberProofExchangeRecord.threadId, }, }) + expect(aliceProofExchangeRecord).toMatchObject({ id: expect.anything(), threadId: faberProofExchangeRecord.threadId, diff --git a/packages/core/tests/v1-indy-proofs.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts similarity index 81% rename from packages/core/tests/v1-indy-proofs.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts index 440da6a5d4..918673b0b3 100644 --- a/packages/core/tests/v1-indy-proofs.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts @@ -1,42 +1,29 @@ -import type { Agent, ConnectionRecord } from '../src' -import type { AcceptProofProposalOptions } from '../src/modules/proofs/ProofsApiOptions' -import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' -import type { CredDefId } from 'indy-sdk' - -import { ProofExchangeRecord } from '../src' -import { getGroupKeysFromIndyProofFormatData } from '../src/modules/proofs/__tests__/groupKeys' -import { - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - PredicateType, -} from '../src/modules/proofs/formats/indy/models' -import { ProofState } from '../src/modules/proofs/models/ProofState' -import { - V1ProposePresentationMessage, - V1RequestPresentationMessage, - V1PresentationMessage, -} from '../src/modules/proofs/protocol/v1/messages' -import { DidCommMessageRepository } from '../src/storage/didcomm' - -import { setupProofsTest, waitForProofExchangeRecord } from './helpers' -import testLogger from './logger' +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections' +import type { V1PresentationPreview } from '../models' + +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { getGroupKeysFromIndyProofFormatData } from '../../../formats/indy/__tests__/groupKeys' +import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { ProofState } from '../../../models' +import { ProofExchangeRecord } from '../../../repository' +import { V1ProposePresentationMessage, V1RequestPresentationMessage, V1PresentationMessage } from '../messages' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent - let credDefId: CredDefId + let credDefId: string let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord - let presentationPreview: PresentationPreview - let didCommMessageRepository: DidCommMessageRepository + let presentationPreview: V1PresentationPreview beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('Faber agent', 'Alice agent')) + await setupProofsTest('Faber agent v1', 'Alice agent v1')) testLogger.test('Issuing second credential') }) @@ -70,14 +57,7 @@ describe('Present Proof', () => { // Faber waits for a presentation proposal from Alice testLogger.test('Faber waits for a presentation proposal from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1ProposePresentationMessage, - }) - + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', id: expect.any(String), @@ -112,10 +92,6 @@ describe('Present Proof', () => { protocolVersion: 'v1', }) - const acceptProposalOptions: AcceptProofProposalOptions = { - proofRecordId: faberProofExchangeRecord.id, - } - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { threadId: aliceProofExchangeRecord.threadId, state: ProofState.RequestReceived, @@ -123,21 +99,19 @@ describe('Present Proof', () => { // Faber accepts the presentation proposal from Alice testLogger.test('Faber accepts presentation proposal from Alice') - faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ + proofRecordId: faberProofExchangeRecord.id, + }) // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -154,11 +128,8 @@ describe('Present Proof', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { @@ -175,11 +146,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1PresentationMessage, - }) - + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) expect(presentation).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/presentation', id: expect.any(String), @@ -192,15 +159,15 @@ describe('Present Proof', () => { }, }, ], - appendedAttachments: [ - { - id: expect.any(String), - filename: expect.any(String), - data: { - base64: expect.any(String), - }, - }, - ], + // appendedAttachments: [ + // { + // id: expect.any(String), + // filename: expect.any(String), + // data: { + // base64: expect.any(String), + // }, + // }, + // ], thread: { threadId: expect.any(String), }, @@ -220,7 +187,7 @@ describe('Present Proof', () => { // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') @@ -390,17 +357,11 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -421,11 +382,8 @@ describe('Present Proof', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { @@ -442,11 +400,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1PresentationMessage, - }) - + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) expect(presentation).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/presentation', id: expect.any(String), @@ -459,15 +413,15 @@ describe('Present Proof', () => { }, }, ], - appendedAttachments: [ - { - id: expect.any(String), - filename: expect.any(String), - data: { - base64: expect.any(String), - }, - }, - ], + // appendedAttachments: [ + // { + // id: expect.any(String), + // filename: expect.any(String), + // data: { + // base64: expect.any(String), + // }, + // }, + // ], thread: { threadId: expect.any(String), }, @@ -487,7 +441,7 @@ describe('Present Proof', () => { // Faber accepts the presentation testLogger.test('Faber accept the presentation from Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she receives a presentation acknowledgement testLogger.test('Alice waits for acceptance by Faber') @@ -548,7 +502,6 @@ describe('Present Proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -604,7 +557,6 @@ describe('Present Proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -615,17 +567,11 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), - requestPresentationAttachments: [ + requestAttachments: [ { id: 'libindy-request-presentation-0', mimeType: 'application/json', @@ -648,10 +594,10 @@ describe('Present Proof', () => { state: ProofState.Abandoned, }) - aliceProofExchangeRecord = await aliceAgent.proofs.sendProblemReport( - aliceProofExchangeRecord.id, - 'Problem inside proof request' - ) + aliceProofExchangeRecord = await aliceAgent.proofs.sendProblemReport({ + proofRecordId: aliceProofExchangeRecord.id, + description: 'Problem inside proof request', + }) faberProofExchangeRecord = await faberProofExchangeRecordPromise diff --git a/packages/core/tests/v1-proofs-auto-accept.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts similarity index 82% rename from packages/core/tests/v1-proofs-auto-accept.test.ts rename to packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts index 6c222e70e9..c8a116e8ed 100644 --- a/packages/core/tests/v1-proofs-auto-accept.test.ts +++ b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts @@ -1,17 +1,11 @@ -import type { Agent, ConnectionRecord } from '../src' -import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections' +import type { V1PresentationPreview } from '../models' -import { - AutoAcceptProof, - ProofState, - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - PredicateType, -} from '../src' - -import { setupProofsTest, waitForProofExchangeRecord } from './helpers' -import testLogger from './logger' +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { AutoAcceptProof, ProofState } from '../../../models' describe('Auto accept present proof', () => { let faberAgent: Agent @@ -19,9 +13,9 @@ describe('Auto accept present proof', () => { let credDefId: string let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview - describe('Auto accept on `always`', () => { + describe("Auto accept on 'always'", () => { beforeAll(async () => { ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = await setupProofsTest( @@ -37,7 +31,7 @@ describe('Auto accept present proof', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { + test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'always'", async () => { testLogger.test('Alice sends presentation proposal to Faber') await aliceAgent.proofs.proposeProof({ @@ -45,7 +39,6 @@ describe('Auto accept present proof', () => { protocolVersion: 'v1', proofFormats: { indy: { - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', name: 'abc', version: '1.0', attributes: presentationPreview.attributes, @@ -62,7 +55,7 @@ describe('Auto accept present proof', () => { ]) }) - test('Faber starts with proof requests to Alice, both with autoAcceptProof on `always`', async () => { + test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'always'", async () => { testLogger.test('Faber sends presentation request to Alice') const attributes = { name: new ProofAttributeInfo({ @@ -94,7 +87,6 @@ describe('Auto accept present proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -109,7 +101,7 @@ describe('Auto accept present proof', () => { }) }) - describe('Auto accept on `contentApproved`', () => { + describe("Auto accept on 'contentApproved'", () => { beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = @@ -127,7 +119,7 @@ describe('Auto accept present proof', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with proof proposal to Faber, both with autoacceptproof on `contentApproved`', async () => { + test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'contentApproved'", async () => { testLogger.test('Alice sends presentation proposal to Faber') const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ @@ -135,7 +127,6 @@ describe('Auto accept present proof', () => { protocolVersion: 'v1', proofFormats: { indy: { - nonce: '1298236324864', name: 'abc', version: '1.0', attributes: presentationPreview.attributes, @@ -159,7 +150,7 @@ describe('Auto accept present proof', () => { ]) }) - test('Faber starts with proof requests to Alice, both with autoacceptproof on `contentApproved`', async () => { + test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'contentApproved'", async () => { testLogger.test('Faber sends presentation request to Alice') const attributes = { name: new ProofAttributeInfo({ @@ -191,7 +182,6 @@ describe('Auto accept present proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324866', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -203,7 +193,7 @@ describe('Auto accept present proof', () => { state: ProofState.RequestReceived, }) - const { proofFormats } = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ proofRecordId }) + const { proofFormats } = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId }) await aliceAgent.proofs.acceptRequest({ proofRecordId, proofFormats }) await Promise.all([ diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts index cdc9f6d797..c8f331c3a8 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts @@ -1,17 +1,17 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { V1ProofService } from '../V1ProofService' +import type { V1ProofProtocol } from '../V1ProofProtocol' import { V1PresentationAckMessage } from '../messages' export class V1PresentationAckHandler implements MessageHandler { - private proofService: V1ProofService + private proofProtocol: V1ProofProtocol public supportedMessages = [V1PresentationAckMessage] - public constructor(proofService: V1ProofService) { - this.proofService = proofService + public constructor(proofProtocol: V1ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - await this.proofService.processAck(messageContext) + await this.proofProtocol.processAck(messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts index 6918979829..e5553b1283 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts @@ -1,77 +1,69 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { DidCommMessageRepository } from '../../../../../storage' -import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' import type { ProofExchangeRecord } from '../../../repository' -import type { V1ProofService } from '../V1ProofService' +import type { V1ProofProtocol } from '../V1ProofProtocol' import { OutboundMessageContext } from '../../../../../agent/models' +import { DidCommMessageRepository } from '../../../../../storage' import { V1PresentationMessage, V1RequestPresentationMessage } from '../messages' export class V1PresentationHandler implements MessageHandler { - private proofService: V1ProofService - private agentConfig: AgentConfig - private proofResponseCoordinator: ProofResponseCoordinator - private didCommMessageRepository: DidCommMessageRepository + private proofProtocol: V1ProofProtocol public supportedMessages = [V1PresentationMessage] - public constructor( - proofService: V1ProofService, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - didCommMessageRepository: DidCommMessageRepository - ) { - this.proofService = proofService - this.agentConfig = agentConfig - this.proofResponseCoordinator = proofResponseCoordinator - this.didCommMessageRepository = didCommMessageRepository + public constructor(proofProtocol: V1ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const proofRecord = await this.proofService.processPresentation(messageContext) + const proofRecord = await this.proofProtocol.processPresentation(messageContext) - const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToPresentation( - messageContext.agentContext, - proofRecord - ) + const shouldAutoRespond = await this.proofProtocol.shouldAutoRespondToPresentation(messageContext.agentContext, { + presentationMessage: messageContext.message, + proofRecord, + }) if (shouldAutoRespond) { - return await this.createAck(proofRecord, messageContext) + return await this.acceptPresentation(proofRecord, messageContext) } } - private async createAck( - record: ProofExchangeRecord, + private async acceptPresentation( + proofRecord: ProofExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptProofs}` - ) - - const { message, proofRecord } = await this.proofService.createAck(messageContext.agentContext, { - proofRecord: record, - }) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1RequestPresentationMessage, - }) - - const presentationMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1PresentationMessage, - }) + messageContext.agentContext.config.logger.info(`Automatically sending acknowledgement with autoAccept`) if (messageContext.connection) { + const { message } = await this.proofProtocol.acceptPresentation(messageContext.agentContext, { + proofRecord, + }) + return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, connection: messageContext.connection, associatedRecord: proofRecord, }) - } else if (requestMessage?.service && presentationMessage?.service) { - const recipientService = presentationMessage?.service + } else if (messageContext.message.service) { + const { message } = await this.proofProtocol.acceptPresentation(messageContext.agentContext, { + proofRecord, + }) + + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) + const requestMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V1RequestPresentationMessage, + }) + + const recipientService = messageContext.message.service const ourService = requestMessage?.service + if (!ourService) { + messageContext.agentContext.config.logger.error( + `Could not automatically create presentation ack. Missing ourService on request message` + ) + return + } + return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, serviceParams: { @@ -81,6 +73,6 @@ export class V1PresentationHandler implements MessageHandler { }) } - this.agentConfig.logger.error(`Could not automatically create presentation ack`) + messageContext.agentContext.config.logger.error(`Could not automatically create presentation ack`) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts index e3c7f97410..eee0266a68 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts @@ -1,17 +1,17 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { V1ProofService } from '../V1ProofService' +import type { V1ProofProtocol } from '../V1ProofProtocol' import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' export class V1PresentationProblemReportHandler implements MessageHandler { - private proofService: V1ProofService + private proofProtocol: V1ProofProtocol public supportedMessages = [V1PresentationProblemReportMessage] - public constructor(proofService: V1ProofService) { - this.proofService = proofService + public constructor(proofProtocol: V1ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - await this.proofService.processProblemReport(messageContext) + await this.proofProtocol.processProblemReport(messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts index e69764d3d5..113c695c98 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts @@ -1,107 +1,44 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' -import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' -import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' -import type { IndyProofRequestFromProposalOptions } from '../../../formats/indy/IndyProofFormatsServiceOptions' -import type { ProofRequestFromProposalOptions } from '../../../models/ProofServiceOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V1ProofService } from '../V1ProofService' +import type { V1ProofProtocol } from '../V1ProofProtocol' import { OutboundMessageContext } from '../../../../../agent/models' -import { AriesFrameworkError } from '../../../../../error' import { V1ProposePresentationMessage } from '../messages' export class V1ProposePresentationHandler implements MessageHandler { - private proofService: V1ProofService - private agentConfig: AgentConfig - private didCommMessageRepository: DidCommMessageRepository - private proofResponseCoordinator: ProofResponseCoordinator + private proofProtocol: V1ProofProtocol public supportedMessages = [V1ProposePresentationMessage] - public constructor( - proofService: V1ProofService, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - didCommMessageRepository: DidCommMessageRepository - ) { - this.proofService = proofService - this.agentConfig = agentConfig - this.proofResponseCoordinator = proofResponseCoordinator - this.didCommMessageRepository = didCommMessageRepository + public constructor(proofProtocol: V1ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const proofRecord = await this.proofService.processProposal(messageContext) + const proofRecord = await this.proofProtocol.processProposal(messageContext) - const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToProposal( - messageContext.agentContext, - proofRecord - ) + const shouldAutoRespond = await this.proofProtocol.shouldAutoRespondToProposal(messageContext.agentContext, { + proofRecord, + proposalMessage: messageContext.message, + }) if (shouldAutoRespond) { - return await this.createRequest(proofRecord, messageContext) + return await this.acceptProposal(proofRecord, messageContext) } } - private async createRequest( + private async acceptProposal( proofRecord: ProofExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptProofs}` - ) + messageContext.agentContext.config.logger.info(`Automatically sending request with autoAccept`) if (!messageContext.connection) { - this.agentConfig.logger.error('No connection on the messageContext') - throw new AriesFrameworkError('No connection on the messageContext') - } - - const proposalMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - if (!proposalMessage) { - this.agentConfig.logger.error(`Proof record with id ${proofRecord.id} is missing required credential proposal`) - throw new AriesFrameworkError(`Proof record with id ${proofRecord.id} is missing required credential proposal`) + messageContext.agentContext.config.logger.error('No connection on the messageContext, aborting auto accept') + return } - const proofRequestFromProposalOptions: IndyProofRequestFromProposalOptions = { - name: 'proof-request', - version: '1.0', - nonce: await messageContext.agentContext.wallet.generateNonce(), + const { message } = await this.proofProtocol.acceptProposal(messageContext.agentContext, { proofRecord, - } - - const proofRequest: ProofRequestFromProposalOptions<[IndyProofFormat]> = - await this.proofService.createProofRequestFromProposal( - messageContext.agentContext, - proofRequestFromProposalOptions - ) - - const indyProofRequest = proofRequest.proofFormats - - if (!indyProofRequest || !indyProofRequest.indy) { - this.agentConfig.logger.error(`No Indy proof request was found`) - throw new AriesFrameworkError('No Indy proof request was found') - } - - const { message } = await this.proofService.createRequestAsResponse(messageContext.agentContext, { - proofFormats: { - indy: { - name: indyProofRequest.indy?.name, - version: indyProofRequest.indy?.version, - nonRevoked: indyProofRequest.indy?.nonRevoked, - requestedAttributes: indyProofRequest.indy?.requestedAttributes, - requestedPredicates: indyProofRequest.indy?.requestedPredicates, - ver: indyProofRequest.indy?.ver, - nonce: indyProofRequest.indy?.nonce, - }, - }, - proofRecord: proofRecord, - autoAcceptProof: proofRecord.autoAcceptProof, - willConfirm: true, }) return new OutboundMessageContext(message, { diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts index d460bbe122..340307e50a 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts @@ -1,126 +1,74 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' -import type { MediationRecipientService, RoutingService } from '../../../../routing' -import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' -import type { IndyProofFormat } from '../../../formats/indy/IndyProofFormat' -import type { - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, -} from '../../../models/ProofServiceOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V1ProofService } from '../V1ProofService' +import type { V1ProofProtocol } from '../V1ProofProtocol' import { OutboundMessageContext } from '../../../../../agent/models' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' -import { AriesFrameworkError } from '../../../../../error' -import { DidCommMessageRole } from '../../../../../storage' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' +import { RoutingService } from '../../../../routing' import { V1RequestPresentationMessage } from '../messages' export class V1RequestPresentationHandler implements MessageHandler { - private proofService: V1ProofService - private agentConfig: AgentConfig - private proofResponseCoordinator: ProofResponseCoordinator - private mediationRecipientService: MediationRecipientService - private didCommMessageRepository: DidCommMessageRepository - private routingService: RoutingService + private proofProtocol: V1ProofProtocol public supportedMessages = [V1RequestPresentationMessage] - public constructor( - proofService: V1ProofService, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - mediationRecipientService: MediationRecipientService, - didCommMessageRepository: DidCommMessageRepository, - routingService: RoutingService - ) { - this.proofService = proofService - this.agentConfig = agentConfig - this.proofResponseCoordinator = proofResponseCoordinator - this.mediationRecipientService = mediationRecipientService - this.didCommMessageRepository = didCommMessageRepository - this.routingService = routingService + public constructor(proofProtocol: V1ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const proofRecord = await this.proofService.processRequest(messageContext) + const proofRecord = await this.proofProtocol.processRequest(messageContext) - const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToRequest( - messageContext.agentContext, - proofRecord - ) + const shouldAutoRespond = await this.proofProtocol.shouldAutoRespondToRequest(messageContext.agentContext, { + proofRecord, + requestMessage: messageContext.message, + }) if (shouldAutoRespond) { - return await this.createPresentation(proofRecord, messageContext) + return await this.acceptRequest(proofRecord, messageContext) } } - private async createPresentation( - record: ProofExchangeRecord, + private async acceptRequest( + proofRecord: ProofExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { - associatedRecordId: record.id, - messageClass: V1RequestPresentationMessage, - }) - - const indyProofRequest = requestMessage.indyProofRequest - - this.agentConfig.logger.info( - `Automatically sending presentation with autoAccept on ${this.agentConfig.autoAcceptProofs}` - ) + messageContext.agentContext.config.logger.info(`Automatically sending presentation with autoAccept on`) - if (!indyProofRequest) { - this.agentConfig.logger.error('Proof request is undefined.') - throw new AriesFrameworkError('No proof request found.') - } - - const retrievedCredentials: FormatRetrievedCredentialOptions<[IndyProofFormat]> = - await this.proofService.getRequestedCredentialsForProofRequest(messageContext.agentContext, { - proofRecord: record, - config: { - filterByPresentationPreview: true, - }, + if (messageContext.connection) { + const { message } = await this.proofProtocol.acceptRequest(messageContext.agentContext, { + proofRecord, }) - if (!retrievedCredentials.proofFormats.indy) { - this.agentConfig.logger.error('No matching Indy credentials could be retrieved.') - throw new AriesFrameworkError('No matching Indy credentials could be retrieved.') - } - const options: FormatRetrievedCredentialOptions<[IndyProofFormat]> = { - proofFormats: retrievedCredentials.proofFormats, - } - const requestedCredentials: FormatRequestedCredentialReturn<[IndyProofFormat]> = - await this.proofService.autoSelectCredentialsForProofRequest(options) - - const { message, proofRecord } = await this.proofService.createPresentation(messageContext.agentContext, { - proofRecord: record, - proofFormats: { - indy: requestedCredentials.proofFormats.indy, - }, - willConfirm: true, - }) - - if (messageContext.connection) { return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, connection: messageContext.connection, associatedRecord: proofRecord, }) - } else if (requestMessage.service) { - const routing = await this.routingService.getRouting(messageContext.agentContext) - message.service = new ServiceDecorator({ + } else if (messageContext.message.service) { + const { message } = await this.proofProtocol.acceptRequest(messageContext.agentContext, { + proofRecord, + }) + + const routingService = messageContext.agentContext.dependencyManager.resolve(RoutingService) + const routing = await routingService.getRouting(messageContext.agentContext) + const ourService = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) - const recipientService = requestMessage.service + const recipientService = messageContext.message.service - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + // Set and save ~service decorator to record (to remember our verkey) + message.service = ourService + + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) + await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: message, associatedRecordId: proofRecord.id, role: DidCommMessageRole.Sender, }) + return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, serviceParams: { @@ -130,6 +78,6 @@ export class V1RequestPresentationHandler implements MessageHandler { }) } - this.agentConfig.logger.error(`Could not automatically create presentation`) + messageContext.agentContext.config.logger.error(`Could not automatically create presentation`) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/index.ts b/packages/core/src/modules/proofs/protocol/v1/index.ts index a7e92f64d6..e698bc7140 100644 --- a/packages/core/src/modules/proofs/protocol/v1/index.ts +++ b/packages/core/src/modules/proofs/protocol/v1/index.ts @@ -1,4 +1,4 @@ export * from './errors' export * from './messages' export * from './models' -export * from './V1ProofService' +export * from './V1ProofProtocol' diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts index d66360d0e5..12d2978ab0 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts @@ -1,4 +1,3 @@ -import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' import type { IndyProof } from 'indy-sdk' import { Expose, Type } from 'class-transformer' @@ -6,14 +5,11 @@ import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { V2_INDY_PRESENTATION } from '../../../formats/ProofFormatConstants' -import { ProofFormatSpec } from '../../../models/ProofFormatSpec' export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0' -export interface PresentationOptions { +export interface V1PresentationMessageOptions { id?: string comment?: string presentationAttachments: Attachment[] @@ -27,7 +23,7 @@ export interface PresentationOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#presentation */ export class V1PresentationMessage extends AgentMessage { - public constructor(options: PresentationOptions) { + public constructor(options: V1PresentationMessageOptions) { super() if (options) { @@ -61,30 +57,16 @@ export class V1PresentationMessage extends AgentMessage { @IsInstance(Attachment, { each: true }) public presentationAttachments!: Attachment[] - public getAttachmentFormats(): ProofAttachmentFormat[] { - const attachment = this.indyAttachment - - if (!attachment) { - throw new AriesFrameworkError(`Could not find a presentation attachment`) - } - - return [ - { - format: new ProofFormatSpec({ format: V2_INDY_PRESENTATION }), - attachment: attachment, - }, - ] - } - - public get indyAttachment(): Attachment | null { - return this.presentationAttachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) ?? null - } - public get indyProof(): IndyProof | null { - const attachment = this.indyAttachment + const attachment = + this.presentationAttachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) ?? null const proofJson = attachment?.getDataAsJson() ?? null return proofJson } + + public getPresentationAttachmentById(id: string): Attachment | undefined { + return this.presentationAttachments.find((attachment) => attachment.id === id) + } } diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts index fab2d86765..bef0a44c7a 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts @@ -3,13 +3,12 @@ import { IsInstance, IsOptional, IsString, ValidateNested } from 'class-validato import { AgentMessage } from '../../../../../agent/AgentMessage' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { PresentationPreview } from '../models/V1PresentationPreview' +import { V1PresentationPreview } from '../models/V1PresentationPreview' -export interface ProposePresentationMessageOptions { +export interface V1ProposePresentationMessageOptions { id?: string comment?: string - parentThreadId?: string - presentationProposal: PresentationPreview + presentationProposal: V1PresentationPreview } /** @@ -18,17 +17,12 @@ export interface ProposePresentationMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#propose-presentation */ export class V1ProposePresentationMessage extends AgentMessage { - public constructor(options: ProposePresentationMessageOptions) { + public constructor(options: V1ProposePresentationMessageOptions) { super() if (options) { this.id = options.id ?? this.generateId() this.comment = options.comment - if (options.parentThreadId) { - this.setThread({ - parentThreadId: options.parentThreadId, - }) - } this.presentationProposal = options.presentationProposal } } @@ -48,8 +42,8 @@ export class V1ProposePresentationMessage extends AgentMessage { * Represents the presentation example that prover wants to provide. */ @Expose({ name: 'presentation_proposal' }) - @Type(() => PresentationPreview) + @Type(() => V1PresentationPreview) @ValidateNested() - @IsInstance(PresentationPreview) - public presentationProposal!: PresentationPreview + @IsInstance(V1PresentationPreview) + public presentationProposal!: V1PresentationPreview } diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts index a65dae533f..5ac5fd6798 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts @@ -1,4 +1,3 @@ -import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' import type { IndyProofRequest } from 'indy-sdk' import { Expose, Type } from 'class-transformer' @@ -6,18 +5,12 @@ import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' -import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { V2_INDY_PRESENTATION_REQUEST } from '../../../formats/ProofFormatConstants' -import { ProofRequest } from '../../../formats/indy/models/ProofRequest' -import { ProofFormatSpec } from '../../../models/ProofFormatSpec' -export interface RequestPresentationOptions { +export interface V1RequestPresentationMessageOptions { id?: string comment?: string - parentThreadId?: string - requestPresentationAttachments: Attachment[] + requestAttachments: Attachment[] } export const INDY_PROOF_REQUEST_ATTACHMENT_ID = 'libindy-request-presentation-0' @@ -28,18 +21,13 @@ export const INDY_PROOF_REQUEST_ATTACHMENT_ID = 'libindy-request-presentation-0' * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#request-presentation */ export class V1RequestPresentationMessage extends AgentMessage { - public constructor(options: RequestPresentationOptions) { + public constructor(options: V1RequestPresentationMessageOptions) { super() if (options) { this.id = options.id ?? this.generateId() this.comment = options.comment - this.requestPresentationAttachments = options.requestPresentationAttachments - if (options.parentThreadId) { - this.setThread({ - parentThreadId: options.parentThreadId, - }) - } + this.requestAttachments = options.requestAttachments } } @@ -64,43 +52,15 @@ export class V1RequestPresentationMessage extends AgentMessage { each: true, }) @IsInstance(Attachment, { each: true }) - public requestPresentationAttachments!: Attachment[] + public requestAttachments!: Attachment[] - public get indyProofRequest(): ProofRequest | null { - // Extract proof request from attachment - const proofRequestJson = this.indyProofRequestJson - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - return proofRequest - } - - public get indyProofRequestJson(): IndyProofRequest | null { - const attachment = this.requestPresentationAttachments.find( - (attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID - ) + public get indyProofRequest(): IndyProofRequest | null { + const attachment = this.requestAttachments.find((attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID) // Extract proof request from attachment return attachment?.getDataAsJson() ?? null } - public getAttachmentFormats(): ProofAttachmentFormat[] { - const attachment = this.indyAttachment - - if (!attachment) { - throw new AriesFrameworkError(`Could not find a request presentation attachment`) - } - - return [ - { - format: new ProofFormatSpec({ format: V2_INDY_PRESENTATION_REQUEST }), - attachment: attachment, - }, - ] - } - - public get indyAttachment(): Attachment | null { - return ( - this.requestPresentationAttachments.find((attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID) ?? - null - ) + public getRequestAttachmentById(id: string): Attachment | undefined { + return this.requestAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts b/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts index 2ac71e903e..0de67b3a00 100644 --- a/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts +++ b/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts @@ -16,7 +16,7 @@ import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../../../utils/messageType' import { PredicateType } from '../../../formats/indy/models/PredicateType' -export interface PresentationPreviewAttributeOptions { +export interface V1PresentationPreviewAttributeOptions { name: string credentialDefinitionId?: string mimeType?: string @@ -24,8 +24,8 @@ export interface PresentationPreviewAttributeOptions { referent?: string } -export class PresentationPreviewAttribute { - public constructor(options: PresentationPreviewAttributeOptions) { +export class V1PresentationPreviewAttribute { + public constructor(options: V1PresentationPreviewAttributeOptions) { if (options) { this.name = options.name this.credentialDefinitionId = options.credentialDefinitionId @@ -39,7 +39,7 @@ export class PresentationPreviewAttribute { @Expose({ name: 'cred_def_id' }) @IsString() - @ValidateIf((o: PresentationPreviewAttribute) => o.referent !== undefined) + @ValidateIf((o: V1PresentationPreviewAttribute) => o.referent !== undefined) @Matches(credDefIdRegex) public credentialDefinitionId?: string @@ -61,15 +61,15 @@ export class PresentationPreviewAttribute { } } -export interface PresentationPreviewPredicateOptions { +export interface V1PresentationPreviewPredicateOptions { name: string credentialDefinitionId: string predicate: PredicateType threshold: number } -export class PresentationPreviewPredicate { - public constructor(options: PresentationPreviewPredicateOptions) { +export class V1PresentationPreviewPredicate { + public constructor(options: V1PresentationPreviewPredicateOptions) { if (options) { this.name = options.name this.credentialDefinitionId = options.credentialDefinitionId @@ -97,9 +97,9 @@ export class PresentationPreviewPredicate { } } -export interface PresentationPreviewOptions { - attributes?: PresentationPreviewAttribute[] - predicates?: PresentationPreviewPredicate[] +export interface V1PresentationPreviewOptions { + attributes?: V1PresentationPreviewAttributeOptions[] + predicates?: V1PresentationPreviewPredicateOptions[] } /** @@ -109,31 +109,31 @@ export interface PresentationPreviewOptions { * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#presentation-preview */ -export class PresentationPreview { - public constructor(options: PresentationPreviewOptions) { +export class V1PresentationPreview { + public constructor(options: V1PresentationPreviewOptions) { if (options) { - this.attributes = options.attributes ?? [] - this.predicates = options.predicates ?? [] + this.attributes = options.attributes?.map((a) => new V1PresentationPreviewAttribute(a)) ?? [] + this.predicates = options.predicates?.map((p) => new V1PresentationPreviewPredicate(p)) ?? [] } } @Expose({ name: '@type' }) - @IsValidMessageType(PresentationPreview.type) + @IsValidMessageType(V1PresentationPreview.type) @Transform(({ value }) => replaceLegacyDidSovPrefix(value), { toClassOnly: true, }) - public readonly type = PresentationPreview.type.messageTypeUri + public readonly type = V1PresentationPreview.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/present-proof/1.0/presentation-preview') - @Type(() => PresentationPreviewAttribute) + @Type(() => V1PresentationPreviewAttribute) @ValidateNested({ each: true }) - @IsInstance(PresentationPreviewAttribute, { each: true }) - public attributes!: PresentationPreviewAttribute[] + @IsInstance(V1PresentationPreviewAttribute, { each: true }) + public attributes!: V1PresentationPreviewAttribute[] - @Type(() => PresentationPreviewPredicate) + @Type(() => V1PresentationPreviewPredicate) @ValidateNested({ each: true }) - @IsInstance(PresentationPreviewPredicate, { each: true }) - public predicates!: PresentationPreviewPredicate[] + @IsInstance(V1PresentationPreviewPredicate, { each: true }) + public predicates!: V1PresentationPreviewPredicate[] public toJSON(): Record { return JsonTransformer.toJSON(this) diff --git a/packages/core/src/modules/proofs/protocol/v1/models/index.ts b/packages/core/src/modules/proofs/protocol/v1/models/index.ts index 35ec9d0545..56c1a4fde0 100644 --- a/packages/core/src/modules/proofs/protocol/v1/models/index.ts +++ b/packages/core/src/modules/proofs/protocol/v1/models/index.ts @@ -1,5 +1 @@ -export * from './PartialProof' -export * from './ProofAttribute' -export * from './ProofIdentifier' -export * from './RequestedProof' export * from './V1PresentationPreview' diff --git a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts new file mode 100644 index 0000000000..54615d7034 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts @@ -0,0 +1,519 @@ +import type { AgentContext } from '../../../../agent' +import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { + ExtractProofFormats, + ProofFormatCredentialForRequestPayload, + ProofFormatPayload, + ProofFormatService, +} from '../../formats' +import type { ProofFormatSpec } from '../../models/ProofFormatSpec' +import type { ProofExchangeRecord } from '../../repository' + +import { AriesFrameworkError } from '../../../../error' +import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' + +import { V2PresentationMessage, V2ProposePresentationMessage, V2RequestPresentationMessage } from './messages' + +export class ProofFormatCoordinator { + /** + * Create a {@link V2ProposePresentationMessage}. + * + * @param options + * @returns The created {@link V2ProposePresentationMessage} + * + */ + public async createProposal( + agentContext: AgentContext, + { + proofFormats, + formatServices, + proofRecord, + comment, + goalCode, + }: { + formatServices: ProofFormatService[] + proofFormats: ProofFormatPayload, 'createProposal'> + proofRecord: ProofExchangeRecord + comment?: string + goalCode?: string + } + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: ProofFormatSpec[] = [] + const proposalAttachments: Attachment[] = [] + + for (const formatService of formatServices) { + const { format, attachment } = await formatService.createProposal(agentContext, { + proofFormats, + proofRecord, + }) + + proposalAttachments.push(attachment) + formats.push(format) + } + + const message = new V2ProposePresentationMessage({ + id: proofRecord.threadId, + formats, + proposalAttachments, + comment: comment, + goalCode, + }) + + message.setThread({ threadId: proofRecord.threadId, parentThreadId: proofRecord.parentThreadId }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: proofRecord.id, + }) + + return message + } + + public async processProposal( + agentContext: AgentContext, + { + proofRecord, + message, + formatServices, + }: { + proofRecord: ProofExchangeRecord + message: V2ProposePresentationMessage + formatServices: ProofFormatService[] + } + ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + for (const formatService of formatServices) { + const attachment = this.getAttachmentForService(formatService, message.formats, message.proposalAttachments) + + await formatService.processProposal(agentContext, { + attachment, + proofRecord, + }) + } + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + role: DidCommMessageRole.Receiver, + associatedRecordId: proofRecord.id, + }) + } + + public async acceptProposal( + agentContext: AgentContext, + { + proofRecord, + proofFormats, + formatServices, + comment, + goalCode, + presentMultiple, + willConfirm, + }: { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatPayload, 'acceptProposal'> + formatServices: ProofFormatService[] + comment?: string + goalCode?: string + presentMultiple?: boolean + willConfirm?: boolean + } + ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: ProofFormatSpec[] = [] + const requestAttachments: Attachment[] = [] + + const proposalMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + + for (const formatService of formatServices) { + const proposalAttachment = this.getAttachmentForService( + formatService, + proposalMessage.formats, + proposalMessage.proposalAttachments + ) + + const { attachment, format } = await formatService.acceptProposal(agentContext, { + proofRecord, + proofFormats, + proposalAttachment, + }) + + requestAttachments.push(attachment) + formats.push(format) + } + + const message = new V2RequestPresentationMessage({ + formats, + requestAttachments, + comment, + goalCode, + presentMultiple, + willConfirm, + }) + + message.setThread({ threadId: proofRecord.threadId, parentThreadId: proofRecord.parentThreadId }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + return message + } + + /** + * Create a {@link V2RequestPresentationMessage}. + * + * @param options + * @returns The created {@link V2RequestPresentationMessage} + * + */ + public async createRequest( + agentContext: AgentContext, + { + proofFormats, + formatServices, + proofRecord, + comment, + goalCode, + presentMultiple, + willConfirm, + }: { + formatServices: ProofFormatService[] + proofFormats: ProofFormatPayload, 'createRequest'> + proofRecord: ProofExchangeRecord + comment?: string + goalCode?: string + presentMultiple?: boolean + willConfirm?: boolean + } + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: ProofFormatSpec[] = [] + const requestAttachments: Attachment[] = [] + + for (const formatService of formatServices) { + const { format, attachment } = await formatService.createRequest(agentContext, { + proofFormats, + proofRecord, + }) + + requestAttachments.push(attachment) + formats.push(format) + } + + const message = new V2RequestPresentationMessage({ + formats, + comment, + requestAttachments, + goalCode, + presentMultiple, + willConfirm, + }) + + message.setThread({ threadId: proofRecord.threadId, parentThreadId: proofRecord.parentThreadId }) + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + role: DidCommMessageRole.Sender, + associatedRecordId: proofRecord.id, + }) + + return message + } + + public async processRequest( + agentContext: AgentContext, + { + proofRecord, + message, + formatServices, + }: { + proofRecord: ProofExchangeRecord + message: V2RequestPresentationMessage + formatServices: ProofFormatService[] + } + ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + for (const formatService of formatServices) { + const attachment = this.getAttachmentForService(formatService, message.formats, message.requestAttachments) + + await formatService.processRequest(agentContext, { + attachment, + proofRecord, + }) + } + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + role: DidCommMessageRole.Receiver, + associatedRecordId: proofRecord.id, + }) + } + + public async acceptRequest( + agentContext: AgentContext, + { + proofRecord, + proofFormats, + formatServices, + comment, + lastPresentation, + goalCode, + }: { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatPayload, 'acceptRequest'> + formatServices: ProofFormatService[] + comment?: string + lastPresentation?: boolean + goalCode?: string + } + ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + + // create message. there are two arrays in each message, one for formats the other for attachments + const formats: ProofFormatSpec[] = [] + const presentationAttachments: Attachment[] = [] + + for (const formatService of formatServices) { + const requestAttachment = this.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const proposalAttachment = proposalMessage + ? this.getAttachmentForService(formatService, proposalMessage.formats, proposalMessage.proposalAttachments) + : undefined + + const { attachment, format } = await formatService.acceptRequest(agentContext, { + requestAttachment, + proposalAttachment, + proofRecord, + proofFormats, + }) + + presentationAttachments.push(attachment) + formats.push(format) + } + + const message = new V2PresentationMessage({ + formats, + presentationAttachments, + comment, + lastPresentation, + goalCode, + }) + + message.setThread({ threadId: proofRecord.threadId }) + message.setPleaseAck() + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + associatedRecordId: proofRecord.id, + role: DidCommMessageRole.Sender, + }) + + return message + } + + public async getCredentialsForRequest( + agentContext: AgentContext, + { + proofRecord, + proofFormats, + formatServices, + }: { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatCredentialForRequestPayload< + ExtractProofFormats, + 'getCredentialsForRequest', + 'input' + > + formatServices: ProofFormatService[] + } + ): Promise, 'getCredentialsForRequest', 'output'>> { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + + const credentialsForRequest: Record = {} + + for (const formatService of formatServices) { + const requestAttachment = this.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const proposalAttachment = proposalMessage + ? this.getAttachmentForService(formatService, proposalMessage.formats, proposalMessage.proposalAttachments) + : undefined + + const credentialsForFormat = await formatService.getCredentialsForRequest(agentContext, { + requestAttachment, + proposalAttachment, + proofRecord, + proofFormats, + }) + + credentialsForRequest[formatService.formatKey] = credentialsForFormat + } + + return credentialsForRequest + } + + public async selectCredentialsForRequest( + agentContext: AgentContext, + { + proofRecord, + proofFormats, + formatServices, + }: { + proofRecord: ProofExchangeRecord + proofFormats?: ProofFormatCredentialForRequestPayload< + ExtractProofFormats, + 'selectCredentialsForRequest', + 'input' + > + formatServices: ProofFormatService[] + } + ): Promise< + ProofFormatCredentialForRequestPayload, 'selectCredentialsForRequest', 'output'> + > { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const proposalMessage = await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + + const credentialsForRequest: Record = {} + + for (const formatService of formatServices) { + const requestAttachment = this.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const proposalAttachment = proposalMessage + ? this.getAttachmentForService(formatService, proposalMessage.formats, proposalMessage.proposalAttachments) + : undefined + + const credentialsForFormat = await formatService.selectCredentialsForRequest(agentContext, { + requestAttachment, + proposalAttachment, + proofRecord, + proofFormats, + }) + + credentialsForRequest[formatService.formatKey] = credentialsForFormat + } + + return credentialsForRequest + } + + public async processPresentation( + agentContext: AgentContext, + { + proofRecord, + message, + requestMessage, + formatServices, + }: { + proofRecord: ProofExchangeRecord + message: V2PresentationMessage + requestMessage: V2RequestPresentationMessage + formatServices: ProofFormatService[] + } + ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const formatVerificationResults: boolean[] = [] + + for (const formatService of formatServices) { + const attachment = this.getAttachmentForService(formatService, message.formats, message.presentationAttachments) + const requestAttachment = this.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const isValid = await formatService.processPresentation(agentContext, { + attachment, + requestAttachment, + proofRecord, + }) + + formatVerificationResults.push(isValid) + } + + await didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { + agentMessage: message, + role: DidCommMessageRole.Receiver, + associatedRecordId: proofRecord.id, + }) + + return formatVerificationResults.every((isValid) => isValid === true) + } + + public getAttachmentForService( + credentialFormatService: ProofFormatService, + formats: ProofFormatSpec[], + attachments: Attachment[] + ) { + const attachmentId = this.getAttachmentIdForService(credentialFormatService, formats) + const attachment = attachments.find((attachment) => attachment.id === attachmentId) + + if (!attachment) { + throw new AriesFrameworkError(`Attachment with id ${attachmentId} not found in attachments.`) + } + + return attachment + } + + private getAttachmentIdForService(credentialFormatService: ProofFormatService, formats: ProofFormatSpec[]) { + const format = formats.find((format) => credentialFormatService.supportsFormat(format.format)) + + if (!format) throw new AriesFrameworkError(`No attachment found for service ${credentialFormatService.formatKey}`) + + return format.attachmentId + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts new file mode 100644 index 0000000000..7c3bfbc88c --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts @@ -0,0 +1,1069 @@ +import type { AgentContext } from '../../../../agent' +import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../../plugins' +import type { ProblemReportMessage } from '../../../problem-reports' +import type { + ExtractProofFormats, + ProofFormat, + ProofFormatCredentialForRequestPayload, + ProofFormatPayload, +} from '../../formats' +import type { ProofFormatService } from '../../formats/ProofFormatService' +import type { ProofFormatSpec } from '../../models/ProofFormatSpec' +import type { ProofProtocol } from '../ProofProtocol' +import type { + AcceptPresentationOptions, + AcceptProofProposalOptions, + AcceptProofRequestOptions, + CreateProofProblemReportOptions, + CreateProofProposalOptions, + CreateProofRequestOptions, + ProofFormatDataMessagePayload, + GetCredentialsForRequestOptions, + GetCredentialsForRequestReturn, + GetProofFormatDataReturn, + NegotiateProofProposalOptions, + NegotiateProofRequestOptions, + ProofProtocolMsgReturnType, + SelectCredentialsForRequestOptions, + SelectCredentialsForRequestReturn, +} from '../ProofProtocolOptions' + +import { Protocol } from '../../../../agent/models' +import { AriesFrameworkError } from '../../../../error' +import { DidCommMessageRepository } from '../../../../storage' +import { uuid } from '../../../../utils/uuid' +import { AckStatus } from '../../../common' +import { ConnectionService } from '../../../connections' +import { V2ProposeCredentialMessage } from '../../../credentials' +import { ProofsModuleConfig } from '../../ProofsModuleConfig' +import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' +import { AutoAcceptProof, ProofState } from '../../models' +import { ProofExchangeRecord, ProofRepository } from '../../repository' +import { composeAutoAccept } from '../../utils/composeAutoAccept' +import { BaseProofProtocol } from '../BaseProofProtocol' + +import { ProofFormatCoordinator } from './ProofFormatCoordinator' +import { V2PresentationAckHandler } from './handlers/V2PresentationAckHandler' +import { V2PresentationHandler } from './handlers/V2PresentationHandler' +import { V2PresentationProblemReportHandler } from './handlers/V2PresentationProblemReportHandler' +import { V2ProposePresentationHandler } from './handlers/V2ProposePresentationHandler' +import { V2RequestPresentationHandler } from './handlers/V2RequestPresentationHandler' +import { V2PresentationAckMessage, V2RequestPresentationMessage } from './messages' +import { V2PresentationMessage } from './messages/V2PresentationMessage' +import { V2PresentationProblemReportMessage } from './messages/V2PresentationProblemReportMessage' +import { V2ProposePresentationMessage } from './messages/V2ProposePresentationMessage' + +export interface V2ProofProtocolConfig { + proofFormats: ProofFormatServices +} + +export class V2ProofProtocol + extends BaseProofProtocol + implements ProofProtocol +{ + private proofFormatCoordinator = new ProofFormatCoordinator() + private proofFormats: PFs + + public constructor({ proofFormats }: V2ProofProtocolConfig) { + super() + + this.proofFormats = proofFormats + } + + /** + * The version of the present proof protocol this service supports + */ + public readonly version = 'v2' as const + + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { + // Register message handlers for the Present Proof V2 Protocol + dependencyManager.registerMessageHandlers([ + new V2ProposePresentationHandler(this), + new V2RequestPresentationHandler(this), + new V2PresentationHandler(this), + new V2PresentationAckHandler(this), + new V2PresentationProblemReportHandler(this), + ]) + + // Register Present Proof V2 in feature registry, with supported roles + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/present-proof/2.0', + roles: ['prover', 'verifier'], + }) + ) + } + + public async createProposal( + agentContext: AgentContext, + { + connectionRecord, + proofFormats, + comment, + autoAcceptProof, + goalCode, + parentThreadId, + }: CreateProofProposalOptions + ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + const formatServices = this.getFormatServices(proofFormats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to create proposal. No supported formats`) + } + + const proofRecord = new ProofExchangeRecord({ + connectionId: connectionRecord.id, + threadId: uuid(), + parentThreadId, + state: ProofState.ProposalSent, + protocolVersion: 'v2', + autoAcceptProof, + }) + + const proposalMessage = await this.proofFormatCoordinator.createProposal(agentContext, { + proofFormats, + proofRecord, + formatServices, + comment, + goalCode, + }) + + agentContext.config.logger.debug('Save record and emit state change event') + await proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + + return { + proofRecord, + message: proposalMessage, + } + } + + /** + * Method called by {@link V2ProposeCredentialHandler} on reception of a propose presentation message + * We do the necessary processing here to accept the proposal and do the state change, emit event etc. + * @param messageContext the inbound propose presentation message + * @returns proof record appropriate for this incoming message (once accepted) + */ + public async processProposal( + messageContext: InboundMessageContext + ): Promise { + const { message: proposalMessage, connection, agentContext } = messageContext + + agentContext.config.logger.debug(`Processing presentation proposal with id ${proposalMessage.id}`) + + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + let proofRecord = await this.findByThreadAndConnectionId( + messageContext.agentContext, + proposalMessage.threadId, + connection?.id + ) + + const formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to process proposal. No supported formats`) + } + + // credential record already exists + if (proofRecord) { + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + const previousSentMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.RequestSent) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + await this.proofFormatCoordinator.processProposal(messageContext.agentContext, { + proofRecord, + formatServices, + message: proposalMessage, + }) + + await this.updateState(messageContext.agentContext, proofRecord, ProofState.ProposalReceived) + + return proofRecord + } else { + // Assert + connectionService.assertConnectionOrServiceDecorator(messageContext) + + // No proof record exists with thread id + proofRecord = new ProofExchangeRecord({ + connectionId: connection?.id, + threadId: proposalMessage.threadId, + state: ProofState.ProposalReceived, + protocolVersion: 'v2', + parentThreadId: proposalMessage.thread?.parentThreadId, + }) + + await this.proofFormatCoordinator.processProposal(messageContext.agentContext, { + proofRecord, + formatServices, + message: proposalMessage, + }) + + // Save record and emit event + await proofRepository.save(messageContext.agentContext, proofRecord) + this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) + + return proofRecord + } + } + + public async acceptProposal( + agentContext: AgentContext, + { proofRecord, proofFormats, autoAcceptProof, comment, goalCode, willConfirm }: AcceptProofProposalOptions + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.ProposalReceived) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // Use empty proofFormats if not provided to denote all formats should be accepted + let formatServices = this.getFormatServices(proofFormats ?? {}) + + // if no format services could be extracted from the proofFormats + // take all available format services from the proposal message + if (formatServices.length === 0) { + const proposalMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + + formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) + } + + // If the format services list is still empty, throw an error as we don't support any + // of the formats + if (formatServices.length === 0) { + throw new AriesFrameworkError( + `Unable to accept proposal. No supported formats provided as input or in proposal message` + ) + } + + const requestMessage = await this.proofFormatCoordinator.acceptProposal(agentContext, { + proofRecord, + formatServices, + comment, + proofFormats, + goalCode, + willConfirm, + // Not supported at the moment + presentMultiple: false, + }) + + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.RequestSent) + + return { proofRecord, message: requestMessage } + } + + /** + * Negotiate a proof proposal as verifier (by sending a proof request message) to the connection + * associated with the proof record. + * + * @param options configuration for the request see {@link NegotiateProofProposalOptions} + * @returns Proof exchange record associated with the proof request + * + */ + public async negotiateProposal( + agentContext: AgentContext, + { proofRecord, proofFormats, autoAcceptProof, comment, goalCode, willConfirm }: NegotiateProofProposalOptions + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.ProposalReceived) + + if (!proofRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support negotiation.` + ) + } + + const formatServices = this.getFormatServices(proofFormats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to create request. No supported formats`) + } + + const requestMessage = await this.proofFormatCoordinator.createRequest(agentContext, { + formatServices, + proofFormats, + proofRecord, + comment, + goalCode, + willConfirm, + // Not supported at the moment + presentMultiple: false, + }) + + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.RequestSent) + + return { proofRecord, message: requestMessage } + } + + /** + * Create a {@link V2RequestPresentationMessage} as beginning of protocol process. + * @returns Object containing request message and associated credential record + * + */ + public async createRequest( + agentContext: AgentContext, + { + proofFormats, + autoAcceptProof, + comment, + connectionRecord, + parentThreadId, + goalCode, + willConfirm, + }: CreateProofRequestOptions + ): Promise> { + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + + const formatServices = this.getFormatServices(proofFormats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to create request. No supported formats`) + } + + const proofRecord = new ProofExchangeRecord({ + connectionId: connectionRecord?.id, + threadId: uuid(), + state: ProofState.RequestSent, + autoAcceptProof, + protocolVersion: 'v2', + parentThreadId, + }) + + const requestMessage = await this.proofFormatCoordinator.createRequest(agentContext, { + formatServices, + proofFormats, + proofRecord, + comment, + goalCode, + willConfirm, + }) + + agentContext.config.logger.debug( + `Saving record and emitting state changed for proof exchange record ${proofRecord.id}` + ) + await proofRepository.save(agentContext, proofRecord) + this.emitStateChangedEvent(agentContext, proofRecord, null) + + return { proofRecord, message: requestMessage } + } + + /** + * Process a received {@link V2RequestPresentationMessage}. This will not accept the proof request + * or send a proof. It will only update the existing proof record with + * the information from the proof request message. Use {@link createCredential} + * after calling this method to create a proof. + *z + * @param messageContext The message context containing a v2 proof request message + * @returns proof record associated with the proof request message + * + */ + public async processRequest( + messageContext: InboundMessageContext + ): Promise { + const { message: requestMessage, connection, agentContext } = messageContext + + const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + agentContext.config.logger.debug(`Processing proof request with id ${requestMessage.id}`) + + let proofRecord = await this.findByThreadAndConnectionId( + messageContext.agentContext, + requestMessage.threadId, + connection?.id + ) + + const formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to process request. No supported formats`) + } + + // proof record already exists + if (proofRecord) { + const previousSentMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposeCredentialMessage, + }) + + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.ProposalSent) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + await this.proofFormatCoordinator.processRequest(messageContext.agentContext, { + proofRecord, + formatServices, + message: requestMessage, + }) + + await this.updateState(messageContext.agentContext, proofRecord, ProofState.RequestReceived) + return proofRecord + } else { + // Assert + connectionService.assertConnectionOrServiceDecorator(messageContext) + + // No proof record exists with thread id + agentContext.config.logger.debug('No proof record found for request, creating a new one') + proofRecord = new ProofExchangeRecord({ + connectionId: connection?.id, + threadId: requestMessage.threadId, + state: ProofState.RequestReceived, + protocolVersion: 'v2', + parentThreadId: requestMessage.thread?.parentThreadId, + }) + + await this.proofFormatCoordinator.processRequest(messageContext.agentContext, { + proofRecord, + formatServices, + message: requestMessage, + }) + + // Save in repository + agentContext.config.logger.debug('Saving proof record and emit request-received event') + await proofRepository.save(messageContext.agentContext, proofRecord) + + this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) + return proofRecord + } + } + + public async acceptRequest( + agentContext: AgentContext, + { proofRecord, autoAcceptProof, comment, proofFormats, goalCode }: AcceptProofRequestOptions + ) { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.RequestReceived) + + // Use empty proofFormats if not provided to denote all formats should be accepted + let formatServices = this.getFormatServices(proofFormats ?? {}) + + // if no format services could be extracted from the proofFormats + // take all available format services from the request message + if (formatServices.length === 0) { + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + } + + // If the format services list is still empty, throw an error as we don't support any + // of the formats + if (formatServices.length === 0) { + throw new AriesFrameworkError( + `Unable to accept request. No supported formats provided as input or in request message` + ) + } + const message = await this.proofFormatCoordinator.acceptRequest(agentContext, { + proofRecord, + formatServices, + comment, + proofFormats, + goalCode, + // Sending multiple presentation messages not supported at the moment + lastPresentation: true, + }) + + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.PresentationSent) + + return { proofRecord, message } + } + + /** + * Create a {@link V2ProposePresentationMessage} as response to a received credential request. + * To create a proposal not bound to an existing proof exchange, use {@link createProposal}. + * + * @param options configuration to use for the proposal + * @returns Object containing proposal message and associated proof record + * + */ + public async negotiateRequest( + agentContext: AgentContext, + { proofRecord, proofFormats, autoAcceptProof, comment, goalCode }: NegotiateProofRequestOptions + ): Promise> { + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.RequestReceived) + + if (!proofRecord.connectionId) { + throw new AriesFrameworkError( + `No connectionId found for proof record '${proofRecord.id}'. Connection-less verification does not support negotiation.` + ) + } + + const formatServices = this.getFormatServices(proofFormats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to create proposal. No supported formats`) + } + + const proposalMessage = await this.proofFormatCoordinator.createProposal(agentContext, { + formatServices, + proofFormats, + proofRecord, + comment, + goalCode, + }) + + proofRecord.autoAcceptProof = autoAcceptProof ?? proofRecord.autoAcceptProof + await this.updateState(agentContext, proofRecord, ProofState.ProposalSent) + + return { proofRecord, message: proposalMessage } + } + + public async getCredentialsForRequest( + agentContext: AgentContext, + { proofRecord, proofFormats }: GetCredentialsForRequestOptions + ): Promise> { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.RequestReceived) + + // Use empty proofFormats if not provided to denote all formats should be accepted + let formatServices = this.getFormatServices(proofFormats ?? {}) + + // if no format services could be extracted from the proofFormats + // take all available format services from the request message + if (formatServices.length === 0) { + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + } + + // If the format services list is still empty, throw an error as we don't support any + // of the formats + if (formatServices.length === 0) { + throw new AriesFrameworkError( + `Unable to get credentials for request. No supported formats provided as input or in request message` + ) + } + + const result = await this.proofFormatCoordinator.getCredentialsForRequest(agentContext, { + formatServices, + proofFormats, + proofRecord, + }) + + return { + proofFormats: result, + } + } + + public async selectCredentialsForRequest( + agentContext: AgentContext, + { proofRecord, proofFormats }: SelectCredentialsForRequestOptions + ): Promise> { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.RequestReceived) + + // Use empty proofFormats if not provided to denote all formats should be accepted + let formatServices = this.getFormatServices(proofFormats ?? {}) + + // if no format services could be extracted from the proofFormats + // take all available format services from the request message + if (formatServices.length === 0) { + const requestMessage = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + } + + // If the format services list is still empty, throw an error as we don't support any + // of the formats + if (formatServices.length === 0) { + throw new AriesFrameworkError( + `Unable to get credentials for request. No supported formats provided as input or in request message` + ) + } + + const result = await this.proofFormatCoordinator.selectCredentialsForRequest(agentContext, { + formatServices, + proofFormats, + proofRecord, + }) + + return { + proofFormats: result, + } + } + + public async processPresentation( + messageContext: InboundMessageContext + ): Promise { + const { message: presentationMessage, connection, agentContext } = messageContext + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + agentContext.config.logger.debug(`Processing presentation with id ${presentationMessage.id}`) + + const proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + presentationMessage.threadId, + connection?.id + ) + + const previousSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const previousReceivedMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2ProposePresentationMessage, + }) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.RequestSent) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + const formatServices = this.getFormatServicesFromMessage(presentationMessage.formats) + if (formatServices.length === 0) { + throw new AriesFrameworkError(`Unable to process presentation. No supported formats`) + } + + const isValid = await this.proofFormatCoordinator.processPresentation(messageContext.agentContext, { + proofRecord, + formatServices, + requestMessage: previousSentMessage, + message: presentationMessage, + }) + + proofRecord.isVerified = isValid + await this.updateState(messageContext.agentContext, proofRecord, ProofState.PresentationReceived) + + return proofRecord + } + + public async acceptPresentation( + agentContext: AgentContext, + { proofRecord }: AcceptPresentationOptions + ): Promise> { + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.PresentationReceived) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + // assert we've received the final presentation + const presentation = await didCommMessageRepository.getAgentMessage(agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2PresentationMessage, + }) + + if (!presentation.lastPresentation) { + throw new AriesFrameworkError( + `Trying to send an ack message while presentation with id ${presentation.id} indicates this is not the last presentation (presentation.last_presentation is set to false)` + ) + } + + const message = new V2PresentationAckMessage({ + threadId: proofRecord.threadId, + status: AckStatus.OK, + }) + + await this.updateState(agentContext, proofRecord, ProofState.Done) + + return { + message, + proofRecord, + } + } + + public async processAck( + messageContext: InboundMessageContext + ): Promise { + const { message: ackMessage, connection, agentContext } = messageContext + + agentContext.config.logger.debug(`Processing proof ack with id ${ackMessage.id}`) + + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + const connectionService = agentContext.dependencyManager.resolve(ConnectionService) + + const proofRecord = await this.getByThreadAndConnectionId( + messageContext.agentContext, + ackMessage.threadId, + connection?.id + ) + proofRecord.connectionId = connection?.id + + const previousReceivedMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2RequestPresentationMessage, + }) + + const previousSentMessage = await didCommMessageRepository.getAgentMessage(messageContext.agentContext, { + associatedRecordId: proofRecord.id, + messageClass: V2PresentationMessage, + }) + + // Assert + proofRecord.assertProtocolVersion('v2') + proofRecord.assertState(ProofState.PresentationSent) + connectionService.assertConnectionOrServiceDecorator(messageContext, { + previousReceivedMessage, + previousSentMessage, + }) + + // Update record + await this.updateState(messageContext.agentContext, proofRecord, ProofState.Done) + + return proofRecord + } + + public async createProblemReport( + agentContext: AgentContext, + { description, proofRecord }: CreateProofProblemReportOptions + ): Promise> { + const message = new V2PresentationProblemReportMessage({ + description: { + en: description, + code: PresentationProblemReportReason.Abandoned, + }, + }) + + message.setThread({ + threadId: proofRecord.threadId, + parentThreadId: proofRecord.parentThreadId, + }) + + return { + proofRecord, + message, + } + } + + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + options: { + proofRecord: ProofExchangeRecord + proposalMessage: V2ProposePresentationMessage + } + ): Promise { + const { proofRecord, proposalMessage } = options + const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) + + const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + + // Handle always / never cases + if (autoAccept === AutoAcceptProof.Always) return true + if (autoAccept === AutoAcceptProof.Never) return false + + const requestMessage = await this.findRequestMessage(agentContext, proofRecord.id) + if (!requestMessage) return false + + // NOTE: we take the formats from the requestMessage so we always check all services that we last sent + // Otherwise we'll only check the formats from the proposal, which could be different from the formats + // we use. + const formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + + for (const formatService of formatServices) { + const requestAttachment = this.proofFormatCoordinator.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const proposalAttachment = this.proofFormatCoordinator.getAttachmentForService( + formatService, + proposalMessage.formats, + proposalMessage.proposalAttachments + ) + + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToProposal(agentContext, { + proofRecord, + requestAttachment, + proposalAttachment, + }) + // If any of the formats return false, we should not auto accept + if (!shouldAutoRespondToFormat) return false + } + + return true + } + + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + options: { + proofRecord: ProofExchangeRecord + requestMessage: V2RequestPresentationMessage + } + ): Promise { + const { proofRecord, requestMessage } = options + const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) + + const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + + // Handle always / never cases + if (autoAccept === AutoAcceptProof.Always) return true + if (autoAccept === AutoAcceptProof.Never) return false + + const proposalMessage = await this.findProposalMessage(agentContext, proofRecord.id) + if (!proposalMessage) return false + + // NOTE: we take the formats from the proposalMessage so we always check all services that we last sent + // Otherwise we'll only check the formats from the request, which could be different from the formats + // we use. + const formatServices = this.getFormatServicesFromMessage(proposalMessage.formats) + + for (const formatService of formatServices) { + const proposalAttachment = this.proofFormatCoordinator.getAttachmentForService( + formatService, + proposalMessage.formats, + proposalMessage.proposalAttachments + ) + + const requestAttachment = this.proofFormatCoordinator.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToRequest(agentContext, { + proofRecord, + requestAttachment, + proposalAttachment, + }) + + // If any of the formats return false, we should not auto accept + if (!shouldAutoRespondToFormat) return false + } + + return true + } + + public async shouldAutoRespondToPresentation( + agentContext: AgentContext, + options: { proofRecord: ProofExchangeRecord; presentationMessage: V2PresentationMessage } + ): Promise { + const { proofRecord, presentationMessage } = options + const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) + + // If this isn't the last presentation yet, we should not auto accept + if (!presentationMessage.lastPresentation) return false + + const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + + // Handle always / never cases + if (autoAccept === AutoAcceptProof.Always) return true + if (autoAccept === AutoAcceptProof.Never) return false + + const proposalMessage = await this.findProposalMessage(agentContext, proofRecord.id) + + const requestMessage = await this.findRequestMessage(agentContext, proofRecord.id) + if (!requestMessage) return false + if (!requestMessage.willConfirm) return false + + // NOTE: we take the formats from the requestMessage so we always check all services that we last sent + // Otherwise we'll only check the formats from the credential, which could be different from the formats + // we use. + const formatServices = this.getFormatServicesFromMessage(requestMessage.formats) + + for (const formatService of formatServices) { + const proposalAttachment = proposalMessage + ? this.proofFormatCoordinator.getAttachmentForService( + formatService, + proposalMessage.formats, + proposalMessage.proposalAttachments + ) + : undefined + + const requestAttachment = this.proofFormatCoordinator.getAttachmentForService( + formatService, + requestMessage.formats, + requestMessage.requestAttachments + ) + + const presentationAttachment = this.proofFormatCoordinator.getAttachmentForService( + formatService, + presentationMessage.formats, + presentationMessage.presentationAttachments + ) + + const shouldAutoRespondToFormat = await formatService.shouldAutoRespondToPresentation(agentContext, { + proofRecord, + presentationAttachment, + requestAttachment, + proposalAttachment, + }) + + // If any of the formats return false, we should not auto accept + if (!shouldAutoRespondToFormat) return false + } + return true + } + + public async findRequestMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + return await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V2RequestPresentationMessage, + }) + } + + public async findPresentationMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + return await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V2PresentationMessage, + }) + } + + public async findProposalMessage( + agentContext: AgentContext, + proofRecordId: string + ): Promise { + const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) + + return await didCommMessageRepository.findAgentMessage(agentContext, { + associatedRecordId: proofRecordId, + messageClass: V2ProposePresentationMessage, + }) + } + + public async getFormatData(agentContext: AgentContext, proofRecordId: string): Promise { + // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. + const [proposalMessage, requestMessage, presentationMessage] = await Promise.all([ + this.findProposalMessage(agentContext, proofRecordId), + this.findRequestMessage(agentContext, proofRecordId), + this.findPresentationMessage(agentContext, proofRecordId), + ]) + + // Create object with the keys and the message formats/attachments. We can then loop over this in a generic + // way so we don't have to add the same operation code four times + const messages = { + proposal: [proposalMessage?.formats, proposalMessage?.proposalAttachments], + request: [requestMessage?.formats, requestMessage?.requestAttachments], + presentation: [presentationMessage?.formats, presentationMessage?.presentationAttachments], + } as const + + const formatData: GetProofFormatDataReturn = {} + + // We loop through all of the message keys as defined above + for (const [messageKey, [formats, attachments]] of Object.entries(messages)) { + // Message can be undefined, so we continue if it is not defined + if (!formats || !attachments) continue + + // Find all format services associated with the message + const formatServices = this.getFormatServicesFromMessage(formats) + + const messageFormatData: ProofFormatDataMessagePayload = {} + + // Loop through all of the format services, for each we will extract the attachment data and assign this to the object + // using the unique format key (e.g. indy) + for (const formatService of formatServices) { + const attachment = this.proofFormatCoordinator.getAttachmentForService(formatService, formats, attachments) + messageFormatData[formatService.formatKey] = attachment.getDataAsJson() + } + + formatData[messageKey as keyof GetProofFormatDataReturn] = messageFormatData + } + + return formatData + } + + /** + * Get all the format service objects for a given proof format from an incoming message + * @param messageFormats the format objects containing the format name (eg indy) + * @return the proof format service objects in an array - derived from format object keys + */ + private getFormatServicesFromMessage(messageFormats: ProofFormatSpec[]): ProofFormatService[] { + const formatServices = new Set() + + for (const msg of messageFormats) { + const service = this.getFormatServiceForFormat(msg.format) + if (service) formatServices.add(service) + } + + return Array.from(formatServices) + } + + /** + * Get all the format service objects for a given proof format + * @param proofFormats the format object containing various optional parameters + * @return the proof format service objects in an array - derived from format object keys + */ + private getFormatServices( + proofFormats: M extends 'selectCredentialsForRequest' | 'getCredentialsForRequest' + ? ProofFormatCredentialForRequestPayload, M, 'input'> + : ProofFormatPayload, M> + ): ProofFormatService[] { + const formats = new Set() + + for (const formatKey of Object.keys(proofFormats)) { + const formatService = this.getFormatServiceForFormatKey(formatKey) + + if (formatService) formats.add(formatService) + } + + return Array.from(formats) + } + + private getFormatServiceForFormatKey(formatKey: string): ProofFormatService | null { + const formatService = this.proofFormats.find((proofFormats) => proofFormats.formatKey === formatKey) + + return formatService ?? null + } + + private getFormatServiceForFormat(format: string): ProofFormatService | null { + const formatService = this.proofFormats.find((proofFormats) => proofFormats.supportsFormat(format)) + + return formatService ?? null + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts deleted file mode 100644 index cfe6ae8e43..0000000000 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofService.ts +++ /dev/null @@ -1,953 +0,0 @@ -import type { AgentContext } from '../../../../agent' -import type { AgentMessage } from '../../../../agent/AgentMessage' -import type { Dispatcher } from '../../../../agent/Dispatcher' -import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import type { Attachment } from '../../../../decorators/attachment/Attachment' -import type { MediationRecipientService } from '../../../routing/services/MediationRecipientService' -import type { RoutingService } from '../../../routing/services/RoutingService' -import type { ProofResponseCoordinator } from '../../ProofResponseCoordinator' -import type { ProofFormatServiceMap } from '../../formats' -import type { ProofFormat } from '../../formats/ProofFormat' -import type { ProofFormatService } from '../../formats/ProofFormatService' -import type { CreateProblemReportOptions } from '../../formats/models/ProofFormatServiceOptions' -import type { ProofFormatSpec } from '../../models/ProofFormatSpec' -import type { - CreateAckOptions, - CreatePresentationOptions, - CreateProofRequestFromProposalOptions, - CreateProposalAsResponseOptions, - CreateProposalOptions, - CreateRequestAsResponseOptions, - CreateRequestOptions, - FormatDataMessagePayload, - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, - GetFormatDataReturn, - GetRequestedCredentialsForProofRequestOptions, - ProofRequestFromProposalOptions, -} from '../../models/ProofServiceOptions' - -import { inject, Lifecycle, scoped } from 'tsyringe' - -import { AgentConfig } from '../../../../agent/AgentConfig' -import { EventEmitter } from '../../../../agent/EventEmitter' -import { InjectionSymbols } from '../../../../constants' -import { AriesFrameworkError } from '../../../../error' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' -import { MessageValidator } from '../../../../utils/MessageValidator' -import { Wallet } from '../../../../wallet/Wallet' -import { AckStatus } from '../../../common' -import { ConnectionService } from '../../../connections' -import { ProofService } from '../../ProofService' -import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' -import { V2_INDY_PRESENTATION_REQUEST } from '../../formats/ProofFormatConstants' -import { IndyProofFormatService } from '../../formats/indy/IndyProofFormatService' -import { ProofState } from '../../models/ProofState' -import { ProofExchangeRecord, ProofRepository } from '../../repository' - -import { V2PresentationProblemReportError } from './errors' -import { V2PresentationAckHandler } from './handlers/V2PresentationAckHandler' -import { V2PresentationHandler } from './handlers/V2PresentationHandler' -import { V2PresentationProblemReportHandler } from './handlers/V2PresentationProblemReportHandler' -import { V2ProposePresentationHandler } from './handlers/V2ProposePresentationHandler' -import { V2RequestPresentationHandler } from './handlers/V2RequestPresentationHandler' -import { V2PresentationAckMessage } from './messages' -import { V2PresentationMessage } from './messages/V2PresentationMessage' -import { V2PresentationProblemReportMessage } from './messages/V2PresentationProblemReportMessage' -import { V2ProposalPresentationMessage } from './messages/V2ProposalPresentationMessage' -import { V2RequestPresentationMessage } from './messages/V2RequestPresentationMessage' - -@scoped(Lifecycle.ContainerScoped) -export class V2ProofService extends ProofService { - private formatServiceMap: { [key: string]: ProofFormatService } - - public constructor( - agentConfig: AgentConfig, - connectionService: ConnectionService, - proofRepository: ProofRepository, - didCommMessageRepository: DidCommMessageRepository, - eventEmitter: EventEmitter, - indyProofFormatService: IndyProofFormatService, - @inject(InjectionSymbols.Wallet) wallet: Wallet - ) { - super(agentConfig, proofRepository, connectionService, didCommMessageRepository, wallet, eventEmitter) - this.wallet = wallet - // Dynamically build format service map. This will be extracted once services are registered dynamically - this.formatServiceMap = [indyProofFormatService].reduce( - (formatServiceMap, formatService) => ({ - ...formatServiceMap, - [formatService.formatKey]: formatService, - }), - {} - ) as ProofFormatServiceMap - } - - /** - * The version of the present proof protocol this service supports - */ - public readonly version = 'v2' as const - - public async createProposal( - agentContext: AgentContext, - options: CreateProposalOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const formats = [] - for (const key of Object.keys(options.proofFormats)) { - const service = this.formatServiceMap[key] - formats.push(await service.createProposal({ formats: options.proofFormats })) - } - - const proposalMessage = new V2ProposalPresentationMessage({ - attachmentInfo: formats, - comment: options.comment, - willConfirm: options.willConfirm, - goalCode: options.goalCode, - parentThreadId: options.parentThreadId, - }) - - const proofRecord = new ProofExchangeRecord({ - connectionId: options.connectionRecord.id, - threadId: proposalMessage.threadId, - parentThreadId: proposalMessage.thread?.parentThreadId, - state: ProofState.ProposalSent, - protocolVersion: 'v2', - }) - - await this.proofRepository.save(agentContext, proofRecord) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: proposalMessage, - role: DidCommMessageRole.Sender, - associatedRecordId: proofRecord.id, - }) - - this.emitStateChangedEvent(agentContext, proofRecord, null) - - return { - proofRecord: proofRecord, - message: proposalMessage, - } - } - - public async createProposalAsResponse( - agentContext: AgentContext, - options: CreateProposalAsResponseOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - options.proofRecord.assertState(ProofState.RequestReceived) - - const formats = [] - for (const key of Object.keys(options.proofFormats)) { - const service = this.formatServiceMap[key] - formats.push( - await service.createProposal({ - formats: options.proofFormats, - }) - ) - } - - const proposalMessage = new V2ProposalPresentationMessage({ - attachmentInfo: formats, - comment: options.comment, - goalCode: options.goalCode, - willConfirm: options.willConfirm, - }) - - proposalMessage.setThread({ threadId: options.proofRecord.threadId }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: proposalMessage, - role: DidCommMessageRole.Sender, - associatedRecordId: options.proofRecord.id, - }) - - await this.updateState(agentContext, options.proofRecord, ProofState.ProposalSent) - - return { message: proposalMessage, proofRecord: options.proofRecord } - } - - public async processProposal( - messageContext: InboundMessageContext - ): Promise { - const { message: proposalMessage, connection: connectionRecord } = messageContext - let proofRecord: ProofExchangeRecord - - const proposalAttachments = proposalMessage.getAttachmentFormats() - - for (const attachmentFormat of proposalAttachments) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - await service?.processProposal({ - proposal: attachmentFormat, - }) - } - - try { - proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { - threadId: proposalMessage.threadId, - connectionId: connectionRecord?.id, - }) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.RequestSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proposalMessage, - previousSentMessage: requestMessage ?? undefined, - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proposalMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - await this.updateState(messageContext.agentContext, proofRecord, ProofState.ProposalReceived) - } catch { - // No proof record exists with thread id - proofRecord = new ProofExchangeRecord({ - connectionId: connectionRecord?.id, - threadId: proposalMessage.threadId, - parentThreadId: proposalMessage.thread?.parentThreadId, - state: ProofState.ProposalReceived, - protocolVersion: 'v2', - }) - - // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) - - // Save record - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proposalMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - await this.proofRepository.save(messageContext.agentContext, proofRecord) - this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) - } - - return proofRecord - } - - public async createRequest( - agentContext: AgentContext, - options: CreateRequestOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - // create attachment formats - const formats = [] - for (const key of Object.keys(options.proofFormats)) { - const service = this.formatServiceMap[key] - formats.push( - await service.createRequest({ - formats: options.proofFormats, - }) - ) - } - - // create request message - const requestMessage = new V2RequestPresentationMessage({ - attachmentInfo: formats, - comment: options.comment, - willConfirm: options.willConfirm, - goalCode: options.goalCode, - parentThreadId: options.parentThreadId, - }) - - // create & store proof record - const proofRecord = new ProofExchangeRecord({ - connectionId: options.connectionRecord?.id, - threadId: requestMessage.threadId, - parentThreadId: requestMessage.thread?.parentThreadId, - state: ProofState.RequestSent, - protocolVersion: 'v2', - }) - - await this.proofRepository.save(agentContext, proofRecord) - - // create DIDComm message - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: requestMessage, - role: DidCommMessageRole.Sender, - associatedRecordId: proofRecord.id, - }) - - this.emitStateChangedEvent(agentContext, proofRecord, null) - - return { - proofRecord: proofRecord, - message: requestMessage, - } - } - - public async createRequestAsResponse( - agentContext: AgentContext, - options: CreateRequestAsResponseOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - options.proofRecord.assertState(ProofState.ProposalReceived) - - const proposal = await this.didCommMessageRepository.getAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - - if (!proposal) { - throw new AriesFrameworkError( - `Proof record with id ${options.proofRecord.id} is missing required presentation proposal` - ) - } - - // create attachment formats - const formats = [] - - for (const key of Object.keys(options.proofFormats)) { - const service = this.formatServiceMap[key] - const requestOptions: CreateRequestAsResponseOptions = { - proofFormats: options.proofFormats, - proofRecord: options.proofRecord, - } - formats.push(await service.createRequestAsResponse(requestOptions)) - } - - // create request message - const requestMessage = new V2RequestPresentationMessage({ - attachmentInfo: formats, - comment: options.comment, - willConfirm: options.willConfirm, - goalCode: options.goalCode, - }) - requestMessage.setThread({ threadId: options.proofRecord.threadId }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: requestMessage, - role: DidCommMessageRole.Sender, - associatedRecordId: options.proofRecord.id, - }) - - await this.updateState(agentContext, options.proofRecord, ProofState.RequestSent) - - return { message: requestMessage, proofRecord: options.proofRecord } - } - - public async processRequest( - messageContext: InboundMessageContext - ): Promise { - const { message: proofRequestMessage, connection: connectionRecord } = messageContext - - const requestAttachments = proofRequestMessage.getAttachmentFormats() - - for (const attachmentFormat of requestAttachments) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - await service?.processRequest({ - requestAttachment: attachmentFormat, - }) - } - - // assert - if (proofRequestMessage.requestPresentationsAttach.length === 0) { - throw new V2PresentationProblemReportError( - `Missing required base64 or json encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}`, - { problemCode: PresentationProblemReportReason.Abandoned } - ) - } - - this.logger.debug(`Received proof request`, proofRequestMessage) - - let proofRecord: ProofExchangeRecord - - try { - proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { - threadId: proofRequestMessage.threadId, - connectionId: connectionRecord?.id, - }) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.ProposalSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: requestMessage ?? undefined, - previousSentMessage: proposalMessage ?? undefined, - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proofRequestMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Update record - await this.updateState(messageContext.agentContext, proofRecord, ProofState.RequestReceived) - } catch { - // No proof record exists with thread id - proofRecord = new ProofExchangeRecord({ - connectionId: connectionRecord?.id, - threadId: proofRequestMessage.threadId, - parentThreadId: proofRequestMessage.thread?.parentThreadId, - state: ProofState.RequestReceived, - protocolVersion: 'v2', - }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: proofRequestMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Assert - this.connectionService.assertConnectionOrServiceDecorator(messageContext) - - // Save in repository - await this.proofRepository.save(messageContext.agentContext, proofRecord) - this.emitStateChangedEvent(messageContext.agentContext, proofRecord, null) - } - - return proofRecord - } - - public async createPresentation( - agentContext: AgentContext, - options: CreatePresentationOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - // assert state - options.proofRecord.assertState(ProofState.RequestReceived) - - const proofRequest = await this.didCommMessageRepository.getAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - const formats = [] - for (const key of Object.keys(options.proofFormats)) { - const service = this.formatServiceMap[key] - formats.push( - await service.createPresentation(agentContext, { - attachment: proofRequest.getAttachmentByFormatIdentifier(V2_INDY_PRESENTATION_REQUEST), - proofFormats: options.proofFormats, - }) - ) - } - - const presentationMessage = new V2PresentationMessage({ - comment: options.comment, - attachmentInfo: formats, - goalCode: options.goalCode, - lastPresentation: options.lastPresentation, - }) - presentationMessage.setThread({ threadId: options.proofRecord.threadId }) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(agentContext, { - agentMessage: presentationMessage, - associatedRecordId: options.proofRecord.id, - role: DidCommMessageRole.Sender, - }) - - await this.updateState(agentContext, options.proofRecord, ProofState.PresentationSent) - - return { message: presentationMessage, proofRecord: options.proofRecord } - } - - public async processPresentation( - messageContext: InboundMessageContext - ): Promise { - const { message: presentationMessage, connection: connectionRecord } = messageContext - - this.logger.debug(`Processing presentation with id ${presentationMessage.id}`) - - const proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { - threadId: presentationMessage.threadId, - connectionId: connectionRecord?.id, - }) - - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - - const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.RequestSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: proposalMessage ?? undefined, - previousSentMessage: requestMessage ?? undefined, - }) - - const formatVerificationResults = [] - for (const attachmentFormat of presentationMessage.getAttachmentFormats()) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - if (service) { - try { - formatVerificationResults.push( - await service.processPresentation(messageContext.agentContext, { - record: proofRecord, - formatAttachments: { - request: requestMessage?.getAttachmentFormats(), - presentation: presentationMessage.getAttachmentFormats(), - }, - }) - ) - } catch (e) { - if (e instanceof AriesFrameworkError) { - throw new V2PresentationProblemReportError(e.message, { - problemCode: PresentationProblemReportReason.Abandoned, - }) - } - throw e - } - } - } - if (formatVerificationResults.length === 0) { - throw new V2PresentationProblemReportError('None of the received formats are supported.', { - problemCode: PresentationProblemReportReason.Abandoned, - }) - } - - const isValid = formatVerificationResults.every((x) => x === true) - - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { - agentMessage: presentationMessage, - associatedRecordId: proofRecord.id, - role: DidCommMessageRole.Receiver, - }) - - // Update record - proofRecord.isVerified = isValid - await this.updateState(messageContext.agentContext, proofRecord, ProofState.PresentationReceived) - - return proofRecord - } - - public async createAck( - agentContext: AgentContext, - options: CreateAckOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - // assert we've received the final presentation - const presentation = await this.didCommMessageRepository.getAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - messageClass: V2PresentationMessage, - }) - - if (!presentation.lastPresentation) { - throw new AriesFrameworkError( - `Trying to send an ack message while presentation with id ${presentation.id} indicates this is not the last presentation (presentation.lastPresentation is set to false)` - ) - } - - const message = new V2PresentationAckMessage({ - threadId: options.proofRecord.threadId, - status: AckStatus.OK, - }) - - await this.updateState(agentContext, options.proofRecord, ProofState.Done) - - return { - message, - proofRecord: options.proofRecord, - } - } - - public async processAck( - messageContext: InboundMessageContext - ): Promise { - const { message: ackMessage, connection: connectionRecord } = messageContext - - const proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { - threadId: ackMessage.threadId, - connectionId: connectionRecord?.id, - }) - - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - const presentationMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2PresentationMessage, - }) - - // Assert - proofRecord.assertState(ProofState.PresentationSent) - this.connectionService.assertConnectionOrServiceDecorator(messageContext, { - previousReceivedMessage: requestMessage ?? undefined, - previousSentMessage: presentationMessage ?? undefined, - }) - - // Update record - await this.updateState(messageContext.agentContext, proofRecord, ProofState.Done) - - return proofRecord - } - - public async createProblemReport( - agentContext: AgentContext, - options: CreateProblemReportOptions - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { - const msg = new V2PresentationProblemReportMessage({ - description: { - code: PresentationProblemReportReason.Abandoned, - en: options.description, - }, - }) - - msg.setThread({ - threadId: options.proofRecord.threadId, - parentThreadId: options.proofRecord.threadId, - }) - - return { - proofRecord: options.proofRecord, - message: msg, - } - } - - public async processProblemReport( - messageContext: InboundMessageContext - ): Promise { - const { message: presentationProblemReportMessage } = messageContext - - const connectionRecord = messageContext.assertReadyConnection() - - this.logger.debug(`Processing problem report with id ${presentationProblemReportMessage.id}`) - - const proofRecord = await this.proofRepository.getSingleByQuery(messageContext.agentContext, { - threadId: presentationProblemReportMessage.threadId, - connectionId: connectionRecord?.id, - }) - - proofRecord.errorMessage = `${presentationProblemReportMessage.description.code}: ${presentationProblemReportMessage.description.en}` - await this.updateState(messageContext.agentContext, proofRecord, ProofState.Abandoned) - return proofRecord - } - - public async createProofRequestFromProposal( - agentContext: AgentContext, - options: CreateProofRequestFromProposalOptions - ): Promise> { - const proofRecordId = options.proofRecord.id - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V2ProposalPresentationMessage, - }) - - if (!proposalMessage) { - throw new AriesFrameworkError(`Proof record with id ${proofRecordId} is missing required presentation proposal`) - } - - const proposalAttachments = proposalMessage.getAttachmentFormats() - - let result = {} - - for (const attachmentFormat of proposalAttachments) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - - if (!service) { - throw new AriesFrameworkError('No format service found for getting requested.') - } - - result = { - ...result, - ...(await service.createProofRequestFromProposal({ - presentationAttachment: attachmentFormat.attachment, - })), - } - } - - const retVal: ProofRequestFromProposalOptions = { - proofRecord: options.proofRecord, - proofFormats: result, - } - return retVal - } - - public async shouldAutoRespondToProposal( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise { - const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - - if (!proposal) return false - - const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - if (!request) return false - - MessageValidator.validateSync(proposal) - - const proposalAttachments = proposal.getAttachmentFormats() - const requestAttachments = request.getAttachmentFormats() - - const equalityResults = [] - for (const attachmentFormat of proposalAttachments) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - equalityResults.push(service?.proposalAndRequestAreEqual(proposalAttachments, requestAttachments)) - } - return true - } - - public async shouldAutoRespondToRequest( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise { - const proposal = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - - if (!proposal) { - return false - } - - const request = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - if (!request) { - throw new AriesFrameworkError( - `Expected to find a request message for ProofExchangeRecord with id ${proofRecord.id}` - ) - } - - const proposalAttachments = proposal.getAttachmentFormats() - const requestAttachments = request.getAttachmentFormats() - - const equalityResults = [] - for (const attachmentFormat of proposalAttachments) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - equalityResults.push(service?.proposalAndRequestAreEqual(proposalAttachments, requestAttachments)) - } - - return equalityResults.every((x) => x === true) - } - - public async shouldAutoRespondToPresentation( - agentContext: AgentContext, - proofRecord: ProofExchangeRecord - ): Promise { - const request = await this.didCommMessageRepository.getAgentMessage(agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - return request.willConfirm - } - - public async findRequestMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V2RequestPresentationMessage, - }) - } - - public async findPresentationMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V2PresentationMessage, - }) - } - - public async findProposalMessage( - agentContext: AgentContext, - proofRecordId: string - ): Promise { - return await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: proofRecordId, - messageClass: V2ProposalPresentationMessage, - }) - } - - public async getFormatData(agentContext: AgentContext, proofRecordId: string): Promise { - // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. - const [proposalMessage, requestMessage, presentationMessage] = await Promise.all([ - this.findProposalMessage(agentContext, proofRecordId), - this.findRequestMessage(agentContext, proofRecordId), - this.findPresentationMessage(agentContext, proofRecordId), - ]) - - // Create object with the keys and the message formats/attachments. We can then loop over this in a generic - // way so we don't have to add the same operation code four times - const messages = { - proposal: [proposalMessage?.formats, proposalMessage?.proposalsAttach], - request: [requestMessage?.formats, requestMessage?.requestPresentationsAttach], - presentation: [presentationMessage?.formats, presentationMessage?.presentationsAttach], - } as const - - const formatData: GetFormatDataReturn = {} - - // We loop through all of the message keys as defined above - for (const [messageKey, [formats, attachments]] of Object.entries(messages)) { - // Message can be undefined, so we continue if it is not defined - if (!formats || !attachments) continue - - // Find all format services associated with the message - const formatServices = this.getFormatServicesFromMessage(formats) - - const messageFormatData: FormatDataMessagePayload = {} - - // Loop through all of the format services, for each we will extract the attachment data and assign this to the object - // using the unique format key (e.g. indy) - for (const formatService of formatServices) { - const attachment = this.getAttachmentForService(formatService, formats, attachments) - messageFormatData[formatService.formatKey] = attachment.getDataAsJson() - } - - formatData[messageKey as Exclude] = - messageFormatData - } - - return formatData - } - - private getFormatServicesFromMessage(messageFormats: ProofFormatSpec[]): ProofFormatService[] { - const formatServices = new Set() - - for (const msg of messageFormats) { - const service = this.getFormatServiceForFormat(msg) - if (service) formatServices.add(service) - } - - return Array.from(formatServices) - } - - private getAttachmentForService( - proofFormatService: ProofFormatService, - formats: ProofFormatSpec[], - attachments: Attachment[] - ) { - const attachmentId = this.getAttachmentIdForService(proofFormatService, formats) - const attachment = attachments.find((attachment) => attachment.id === attachmentId) - - if (!attachment) { - throw new AriesFrameworkError(`Attachment with id ${attachmentId} not found in attachments.`) - } - - return attachment - } - - private getAttachmentIdForService(proofFormatService: ProofFormatService, formats: ProofFormatSpec[]) { - const format = formats.find((format) => proofFormatService.supportsFormat(format.format)) - - if (!format) throw new AriesFrameworkError(`No attachment found for service ${proofFormatService.formatKey}`) - - return format.attachmentId - } - - public async getRequestedCredentialsForProofRequest( - agentContext: AgentContext, - options: GetRequestedCredentialsForProofRequestOptions - ): Promise> { - const requestMessage = await this.didCommMessageRepository.findAgentMessage(agentContext, { - associatedRecordId: options.proofRecord.id, - messageClass: V2RequestPresentationMessage, - }) - - if (!requestMessage) { - throw new AriesFrameworkError('No proof request found.') - } - - const requestAttachments = requestMessage.getAttachmentFormats() - - let result = { - proofFormats: {}, - } - for (const attachmentFormat of requestAttachments) { - const service = this.getFormatServiceForFormat(attachmentFormat.format) - - if (!service) { - throw new AriesFrameworkError('No format service found for getting requested.') - } - - result = { - ...result, - ...(await service.getRequestedCredentialsForProofRequest(agentContext, { - attachment: attachmentFormat.attachment, - presentationProposal: undefined, - config: options.config, - })), - } - } - - return result - } - - public async autoSelectCredentialsForProofRequest( - options: FormatRetrievedCredentialOptions - ): Promise> { - let returnValue = { - proofFormats: {}, - } - - for (const [id] of Object.entries(options.proofFormats)) { - const service = this.formatServiceMap[id] - const credentials = await service.autoSelectCredentialsForProofRequest(options) - returnValue = { ...returnValue, ...credentials } - } - - return returnValue - } - - public registerMessageHandlers( - dispatcher: Dispatcher, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - mediationRecipientService: MediationRecipientService, - routingService: RoutingService - ): void { - dispatcher.registerMessageHandler( - new V2ProposePresentationHandler(this, agentConfig, this.didCommMessageRepository, proofResponseCoordinator) - ) - - dispatcher.registerMessageHandler( - new V2RequestPresentationHandler( - this, - agentConfig, - proofResponseCoordinator, - mediationRecipientService, - this.didCommMessageRepository, - routingService - ) - ) - - dispatcher.registerMessageHandler( - new V2PresentationHandler(this, agentConfig, proofResponseCoordinator, this.didCommMessageRepository) - ) - dispatcher.registerMessageHandler(new V2PresentationAckHandler(this)) - dispatcher.registerMessageHandler(new V2PresentationProblemReportHandler(this)) - } - - private getFormatServiceForFormat(format: ProofFormatSpec) { - for (const service of Object.values(this.formatServiceMap)) { - if (service.supportsFormat(format.format)) { - return service - } - } - return null - } -} diff --git a/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts similarity index 61% rename from packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts index 255c97a92b..397e0b8866 100644 --- a/packages/core/src/modules/proofs/__tests__/V2ProofService.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts @@ -1,44 +1,55 @@ -import type { AgentContext } from '../../../agent' -import type { Wallet } from '../../../wallet/Wallet' -import type { ProofStateChangedEvent } from '../ProofEvents' -import type { CustomProofTags } from '../repository/ProofExchangeRecord' +import type { ProofStateChangedEvent } from '../../../ProofEvents' +import type { CustomProofTags } from '../../../repository/ProofExchangeRecord' import { Subject } from 'rxjs' -import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../tests/helpers' -import { EventEmitter } from '../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../decorators/attachment/Attachment' -import { DidCommMessageRepository } from '../../../storage' -import { ConnectionService, DidExchangeState } from '../../connections' -import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' -import { ProofEventTypes } from '../ProofEvents' -import { PresentationProblemReportReason } from '../errors/PresentationProblemReportReason' -import { V2_INDY_PRESENTATION, V2_INDY_PRESENTATION_REQUEST } from '../formats/ProofFormatConstants' -import { IndyProofFormatService } from '../formats/indy/IndyProofFormatService' -import { ProofState } from '../models/ProofState' -import { V2ProofService } from '../protocol/v2/V2ProofService' -import { V2PresentationProblemReportMessage, V2RequestPresentationMessage } from '../protocol/v2/messages' -import { ProofExchangeRecord } from '../repository/ProofExchangeRecord' -import { ProofRepository } from '../repository/ProofRepository' - -import { credDef } from './fixtures' +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { DidCommMessageRepository } from '../../../../../storage' +import { uuid } from '../../../../../utils/uuid' +import { ConnectionService, DidExchangeState } from '../../../../connections' +import { ProofEventTypes } from '../../../ProofEvents' +import { PresentationProblemReportReason } from '../../../errors/PresentationProblemReportReason' +import { IndyProofFormatService } from '../../../formats/indy/IndyProofFormatService' +import { ProofFormatSpec } from '../../../models/ProofFormatSpec' +import { ProofState } from '../../../models/ProofState' +import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' +import { ProofRepository } from '../../../repository/ProofRepository' +import { V2ProofProtocol } from '../V2ProofProtocol' +import { V2PresentationProblemReportMessage, V2RequestPresentationMessage } from '../messages' // Mock classes -jest.mock('../repository/ProofRepository') -jest.mock('../../../modules/ledger/services/IndyLedgerService') -jest.mock('../../indy/services/IndyHolderService') -jest.mock('../../indy/services/IndyIssuerService') -jest.mock('../../indy/services/IndyVerifierService') -jest.mock('../../connections/services/ConnectionService') -jest.mock('../../../storage/Repository') +jest.mock('../../../repository/ProofRepository') +jest.mock('../../../../connections/services/ConnectionService') +jest.mock('../../../../../storage/Repository') // Mock typed object const ProofRepositoryMock = ProofRepository as jest.Mock -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock const connectionServiceMock = ConnectionService as jest.Mock const didCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const indyProofFormatServiceMock = IndyProofFormatService as jest.Mock +const IndyProofFormatServiceMock = IndyProofFormatService as jest.Mock + +const proofRepository = new ProofRepositoryMock() +const connectionService = new connectionServiceMock() +const didCommMessageRepository = new didCommMessageRepositoryMock() +const indyProofFormatService = new IndyProofFormatServiceMock() + +const agentConfig = getAgentConfig('V2ProofProtocolTest') +const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) + +const agentContext = getAgentContext({ + registerInstances: [ + [ProofRepository, proofRepository], + [DidCommMessageRepository, didCommMessageRepository], + [ConnectionService, connectionService], + [EventEmitter, eventEmitter], + ], + agentConfig, +}) + +const proofProtocol = new V2ProofProtocol({ proofFormats: [indyProofFormatService] }) const connection = getMockConnection({ id: '123', @@ -64,30 +75,16 @@ const mockProofExchangeRecord = ({ id, }: { state?: ProofState - requestMessage?: V2RequestPresentationMessage tags?: CustomProofTags threadId?: string connectionId?: string id?: string } = {}) => { - const requestPresentationMessage = new V2RequestPresentationMessage({ - attachmentInfo: [ - { - format: { - attachmentId: 'abdc8b63-29c6-49ad-9e10-98f9d85db9a2', - format: V2_INDY_PRESENTATION, - }, - attachment: requestAttachment, - }, - ], - comment: 'some comment', - }) - const proofRecord = new ProofExchangeRecord({ protocolVersion: 'v2', id, state: state || ProofState.RequestSent, - threadId: threadId ?? requestPresentationMessage.id, + threadId: threadId ?? uuid(), connectionId: connectionId ?? '123', tags, }) @@ -95,57 +92,23 @@ const mockProofExchangeRecord = ({ return proofRecord } -describe('V2ProofService', () => { - let proofRepository: ProofRepository - let proofService: V2ProofService - let ledgerService: IndyLedgerService - let wallet: Wallet - let eventEmitter: EventEmitter - let connectionService: ConnectionService - let didCommMessageRepository: DidCommMessageRepository - let indyProofFormatService: IndyProofFormatService - let agentContext: AgentContext - - beforeEach(() => { - agentContext = getAgentContext() - const agentConfig = getAgentConfig('V2ProofServiceTest') - proofRepository = new ProofRepositoryMock() - ledgerService = new IndyLedgerServiceMock() - eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) - connectionService = new connectionServiceMock() - didCommMessageRepository = new didCommMessageRepositoryMock() - indyProofFormatService = new indyProofFormatServiceMock() - - proofService = new V2ProofService( - agentConfig, - connectionService, - proofRepository, - didCommMessageRepository, - eventEmitter, - indyProofFormatService, - wallet - ) - - mockFunction(ledgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) - }) - +describe('V2ProofProtocol', () => { describe('processProofRequest', () => { let presentationRequest: V2RequestPresentationMessage let messageContext: InboundMessageContext beforeEach(() => { presentationRequest = new V2RequestPresentationMessage({ - attachmentInfo: [ - { - format: { - attachmentId: 'abdc8b63-29c6-49ad-9e10-98f9d85db9a2', - format: V2_INDY_PRESENTATION_REQUEST, - }, - attachment: requestAttachment, - }, + formats: [ + new ProofFormatSpec({ + attachmentId: 'abdc8b63-29c6-49ad-9e10-98f9d85db9a2', + format: 'hlindy/proof-req@v2.0', + }), ], + requestAttachments: [requestAttachment], comment: 'Proof Request', }) + messageContext = new InboundMessageContext(presentationRequest, { agentContext, connection }) }) @@ -153,7 +116,7 @@ describe('V2ProofService', () => { const repositorySaveSpy = jest.spyOn(proofRepository, 'save') // when - const returnedProofExchangeRecord = await proofService.processRequest(messageContext) + const returnedProofExchangeRecord = await proofProtocol.processRequest(messageContext) // then const expectedProofExchangeRecord = { @@ -175,7 +138,7 @@ describe('V2ProofService', () => { eventEmitter.on(ProofEventTypes.ProofStateChanged, eventListenerMock) // when - await proofService.processRequest(messageContext) + await proofProtocol.processRequest(messageContext) // then expect(eventListenerMock).toHaveBeenCalledWith({ @@ -254,7 +217,7 @@ describe('V2ProofService', () => { mockFunction(proofRepository.getSingleByQuery).mockReturnValue(Promise.resolve(proof)) // when - const returnedCredentialRecord = await proofService.processProblemReport(messageContext) + const returnedCredentialRecord = await proofProtocol.processProblemReport(messageContext) // then const expectedCredentialRecord = { diff --git a/packages/core/tests/v2-connectionless-proofs.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts similarity index 88% rename from packages/core/tests/v2-connectionless-proofs.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index 921f6c3127..f924869f3f 100644 --- a/packages/core/tests/v2-connectionless-proofs.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -1,36 +1,29 @@ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { ProofStateChangedEvent } from '../src/modules/proofs' +import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import type { ProofStateChangedEvent } from '../../../ProofEvents' import { Subject, ReplaySubject } from 'rxjs' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { V1CredentialPreview } from '../src' -import { Agent } from '../src/agent/Agent' -import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' -import { HandshakeProtocol } from '../src/modules/connections/models/HandshakeProtocol' +import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' import { - PredicateType, - ProofState, - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - AutoAcceptProof, - ProofEventTypes, -} from '../src/modules/proofs' -import { MediatorPickupStrategy } from '../src/modules/routing/MediatorPickupStrategy' -import { LinkedAttachment } from '../src/utils/LinkedAttachment' -import { uuid } from '../src/utils/uuid' - -import { - getAgentOptions, - issueCredential, - makeConnection, - prepareForIssuance, setupProofsTest, waitForProofExchangeRecordSubject, -} from './helpers' -import testLogger from './logger' + getAgentOptions, + prepareForIssuance, + makeConnection, + issueCredential, +} from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { Agent } from '../../../../../agent/Agent' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' +import { uuid } from '../../../../../utils/uuid' +import { HandshakeProtocol } from '../../../../connections' +import { V1CredentialPreview } from '../../../../credentials' +import { MediatorPickupStrategy } from '../../../../routing' +import { ProofEventTypes } from '../../../ProofEvents' +import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { AutoAcceptProof, ProofState } from '../../../models' describe('Present Proof', () => { let agents: Agent[] @@ -44,8 +37,8 @@ describe('Present Proof', () => { test('Faber starts with connection-less proof requests to Alice', async () => { const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( - 'Faber connection-less Proofs', - 'Alice connection-less Proofs', + 'Faber connection-less Proofs v2', + 'Alice connection-less Proofs v2', AutoAcceptProof.Never ) agents = [aliceAgent, faberAgent] @@ -86,7 +79,6 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - nonce: '12345678901', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -106,11 +98,8 @@ describe('Present Proof', () => { testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { @@ -135,7 +124,7 @@ describe('Present Proof', () => { }) // Faber accepts presentation - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits till it receives presentation ack aliceProofExchangeRecord = await aliceProofExchangeRecordPromise @@ -191,7 +180,6 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - nonce: '12345678901', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -260,7 +248,6 @@ describe('Present Proof', () => { const aliceOptions = getAgentOptions(`Connectionless proofs with mediator Alice-${unique}`, { autoAcceptProofs: AutoAcceptProof.Always, - // logger: new TestLogger(LogLevel.test), mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ domain: 'https://example.com', }), @@ -356,7 +343,6 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - nonce: '12345678901', requestedAttributes: attributes, requestedPredicates: predicates, }, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts similarity index 84% rename from packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts index a337eca475..f35b4da5d3 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-negotiation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts @@ -1,29 +1,28 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions, NegotiateProposalOptions } from '../../../ProofsApiOptions' +import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' +import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' import type { CredDefId } from 'indy-sdk' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage/didcomm' import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST } from '../../../formats/ProofFormatConstants' import { AttributeFilter } from '../../../formats/indy/models/AttributeFilter' import { PredicateType } from '../../../formats/indy/models/PredicateType' import { ProofAttributeInfo } from '../../../formats/indy/models/ProofAttributeInfo' import { ProofPredicateInfo } from '../../../formats/indy/models/ProofPredicateInfo' import { ProofRequest } from '../../../formats/indy/models/ProofRequest' import { ProofState } from '../../../models/ProofState' -import { V2ProposalPresentationMessage, V2RequestPresentationMessage } from '../messages' +import { V2ProposePresentationMessage, V2RequestPresentationMessage } from '../messages' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent let credDefId: CredDefId let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository @@ -57,7 +56,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes.filter((attribute) => attribute.name !== 'name'), predicates: presentationPreview.predicates, @@ -73,7 +71,7 @@ describe('Present Proof', () => { let proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposalPresentationMessage, + messageClass: V2ProposePresentationMessage, }) expect(proposal).toMatchObject({ @@ -81,10 +79,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -97,7 +95,7 @@ describe('Present Proof', () => { comment: 'V2 propose proof test 1', }) // eslint-disable-next-line @typescript-eslint/no-explicit-any - let proposalAttach = proposal?.proposalsAttach[0].getDataAsJson() as any + let proposalAttach = proposal?.proposalAttachments[0].getDataAsJson() as any let attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] let predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] expect(proposalAttach).toMatchObject({ @@ -164,27 +162,24 @@ describe('Present Proof', () => { }), } - const requestProofAsResponseOptions: NegotiateProposalOptions = { + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + }) + + testLogger.test('Faber sends new proof request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.negotiateProposal({ proofRecordId: faberProofExchangeRecord.id, proofFormats: { indy: { name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', requestedAttributes: attributes, requestedPredicates: predicates, }, }, - } - - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - threadId: faberProofExchangeRecord.threadId, - state: ProofState.RequestReceived, }) - testLogger.test('Faber sends new proof request to Alice') - faberProofExchangeRecord = await faberAgent.proofs.negotiateProposal(requestProofAsResponseOptions) - testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise @@ -198,7 +193,7 @@ describe('Present Proof', () => { expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', id: expect.any(String), - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -229,7 +224,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes.filter((attribute) => attribute.name === 'name'), predicates: presentationPreview.predicates, @@ -245,7 +239,7 @@ describe('Present Proof', () => { proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposalPresentationMessage, + messageClass: V2ProposePresentationMessage, }) expect(proposal).toMatchObject({ @@ -253,10 +247,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -269,7 +263,7 @@ describe('Present Proof', () => { comment: 'V2 propose proof test 2', }) // eslint-disable-next-line @typescript-eslint/no-explicit-any - proposalAttach = proposal?.proposalsAttach[0].getDataAsJson() as any + proposalAttach = proposal?.proposalAttachments[0].getDataAsJson() as any attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] expect(proposalAttach).toMatchObject({ @@ -331,10 +325,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_REQUEST, + format: 'hlindy/proof-req@v2.0', }, ], - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -355,17 +349,17 @@ describe('Present Proof', () => { protocolVersion: 'v2', }) - const presentationProposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) + const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) - expect(presentationProposalMessage).toMatchObject({ + expect(proposalMessage).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -379,7 +373,7 @@ describe('Present Proof', () => { }) // eslint-disable-next-line @typescript-eslint/no-explicit-any - proposalAttach = proposal?.proposalsAttach[0].getDataAsJson() as any + proposalAttach = proposal?.proposalAttachments[0].getDataAsJson() as any attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] expect(proposalAttach).toMatchObject({ @@ -412,33 +406,37 @@ describe('Present Proof', () => { )) as V2RequestPresentationMessage const proofRequest = JsonTransformer.fromJSON( - proofRequestMessage.requestPresentationsAttach[0].getDataAsJson(), + proofRequestMessage.requestAttachments[0].getDataAsJson(), ProofRequest ) const predicateKey = proofRequest.requestedPredicates?.keys().next().value - const predicate = Object.values(predicates)[0] - expect(proofRequest).toMatchObject({ + expect(proofRequest.toJSON()).toMatchObject({ name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', + nonce: expect.any(String), version: '1.0', - requestedAttributes: new Map( - Object.entries({ - '0': new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - }) - ), - requestedPredicates: new Map( - Object.entries({ - [predicateKey]: predicate, - }) - ), + requested_attributes: { + '0': { + name: 'name', + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, + requested_predicates: { + [predicateKey]: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credDefId, + }, + ], + }, + }, }) }) }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts similarity index 89% rename from packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts index a8f2d6a531..2fccef1f98 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-presentation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts @@ -1,26 +1,21 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' -import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' +import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' -import { - V2_INDY_PRESENTATION_PROPOSAL, - V2_INDY_PRESENTATION_REQUEST, - V2_INDY_PRESENTATION, -} from '../../../formats/ProofFormatConstants' import { ProofState } from '../../../models/ProofState' import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' -import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' +import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository @@ -28,8 +23,8 @@ describe('Present Proof', () => { beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' + 'Faber agent v2 present proof', + 'Alice agent v2 present proof' )) }) @@ -54,7 +49,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'ProofRequest', - nonce: '947121108704767252195126', version: '1.0', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, @@ -70,7 +64,7 @@ describe('Present Proof', () => { const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposalPresentationMessage, + messageClass: V2ProposePresentationMessage, }) expect(proposal).toMatchObject({ @@ -78,10 +72,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -130,10 +124,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_REQUEST, + format: 'hlindy/proof-req@v2.0', }, ], - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -159,11 +153,8 @@ describe('Present Proof', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { @@ -190,10 +181,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION, + format: 'hlindy/proof@v2.0', }, ], - presentationsAttach: [ + presentationAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -223,7 +214,7 @@ describe('Present Proof', () => { // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts similarity index 84% rename from packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts index 39a29df125..68c09d5717 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-proposal.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts @@ -1,28 +1,27 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' import type { ProofExchangeRecord } from '../../../repository' -import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' +import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' -import { V2_INDY_PRESENTATION_PROPOSAL } from '../../../formats/ProofFormatConstants' import { ProofState } from '../../../models/ProofState' -import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' +import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberPresentationRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' + 'Faber agent v2', + 'Alice agent v2' )) }) @@ -47,7 +46,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'ProofRequest', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, @@ -63,7 +61,7 @@ describe('Present Proof', () => { const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberPresentationRecord.id, - messageClass: V2ProposalPresentationMessage, + messageClass: V2ProposePresentationMessage, }) expect(proposal).toMatchObject({ @@ -71,10 +69,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts similarity index 87% rename from packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts index 47a697821f..827555ec65 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/indy-proof-request.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts @@ -2,21 +2,20 @@ import type { Agent } from '../../../../../agent/Agent' import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { PresentationPreview } from '../../v1/models/V1PresentationPreview' +import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' -import { V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST } from '../../../formats/ProofFormatConstants' import { ProofState } from '../../../models/ProofState' import { V2RequestPresentationMessage } from '../messages' -import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' +import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord let didCommMessageRepository: DidCommMessageRepository @@ -24,8 +23,8 @@ describe('Present Proof', () => { beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' + 'Faber agent v2', + 'Alice agent v2' )) }) @@ -50,7 +49,6 @@ describe('Present Proof', () => { proofFormats: { indy: { name: 'ProofRequest', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, @@ -66,7 +64,7 @@ describe('Present Proof', () => { const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposalPresentationMessage, + messageClass: V2ProposePresentationMessage, }) expect(proposal).toMatchObject({ @@ -74,10 +72,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -97,7 +95,7 @@ describe('Present Proof', () => { }) }) - test(`Faber accepts the Proposal send by Alice`, async () => { + test(`Faber accepts the Proposal sent by Alice`, async () => { // Accept Proposal const acceptProposalOptions: AcceptProofProposalOptions = { proofRecordId: faberProofExchangeRecord.id, @@ -126,10 +124,10 @@ describe('Present Proof', () => { formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_REQUEST, + format: 'hlindy/proof-req@v2.0', }, ], - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', diff --git a/packages/core/tests/v2-proofs-auto-accept.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts similarity index 79% rename from packages/core/tests/v2-proofs-auto-accept.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts index 0ab3d81e13..e3a9841613 100644 --- a/packages/core/tests/v2-proofs-auto-accept.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts @@ -1,17 +1,11 @@ -import type { Agent, ConnectionRecord } from '../src' -import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections' +import type { V1PresentationPreview } from '../../v1' -import { - AutoAcceptProof, - ProofState, - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - PredicateType, -} from '../src' - -import { setupProofsTest, waitForProofExchangeRecord } from './helpers' -import testLogger from './logger' +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { AutoAcceptProof, ProofState } from '../../../models' describe('Auto accept present proof', () => { let faberAgent: Agent @@ -19,7 +13,7 @@ describe('Auto accept present proof', () => { let credDefId: string let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview describe('Auto accept on `always`', () => { beforeAll(async () => { @@ -45,7 +39,6 @@ describe('Auto accept present proof', () => { protocolVersion: 'v2', proofFormats: { indy: { - nonce: '1298236324864', name: 'abc', version: '1.0', attributes: presentationPreview.attributes, @@ -94,7 +87,6 @@ describe('Auto accept present proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -110,7 +102,7 @@ describe('Auto accept present proof', () => { }) }) - describe('Auto accept on `contentApproved`', () => { + describe("Auto accept on 'contentApproved'", () => { beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = @@ -128,15 +120,18 @@ describe('Auto accept present proof', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `contentApproved`', async () => { + test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'contentApproved'", async () => { testLogger.test('Alice sends presentation proposal to Faber') + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + await aliceAgent.proofs.proposeProof({ connectionId: aliceConnection.id, protocolVersion: 'v2', proofFormats: { indy: { - nonce: '1298236324864', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, name: 'abc', @@ -145,20 +140,18 @@ describe('Auto accept present proof', () => { }, }) - const { id: proofRecordId } = await waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, + const faberProofExchangeRecord = await faberProofExchangeRecordPromise + await faberAgent.proofs.acceptProposal({ + proofRecordId: faberProofExchangeRecord.id, }) - testLogger.test('Faber accepts presentation proposal from Alice') - await faberAgent.proofs.acceptProposal({ proofRecordId }) - await Promise.all([ waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), ]) }) - test('Faber starts with proof requests to Alice, both with autoAcceptProof on `contentApproved`', async () => { + test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'contentApproved'", async () => { testLogger.test('Faber sends presentation request to Alice') const attributes = { name: new ProofAttributeInfo({ @@ -183,6 +176,10 @@ describe('Auto accept present proof', () => { }), } + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + await faberAgent.proofs.requestProof({ protocolVersion: 'v2', connectionId: faberConnection.id, @@ -190,19 +187,16 @@ describe('Auto accept present proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324866', requestedAttributes: attributes, requestedPredicates: predicates, }, }, }) - testLogger.test('Alice waits for request from Faber') - const { id: proofRecordId } = await waitForProofExchangeRecord(aliceAgent, { - state: ProofState.RequestReceived, + const aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofExchangeRecord.id, }) - const { proofFormats } = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ proofRecordId }) - await aliceAgent.proofs.acceptRequest({ proofRecordId, proofFormats }) await Promise.all([ waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), diff --git a/packages/core/tests/v2-indy-proofs.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts similarity index 77% rename from packages/core/tests/v2-indy-proofs.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts index f54f6da15e..815188bf69 100644 --- a/packages/core/tests/v2-indy-proofs.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts @@ -1,47 +1,29 @@ -import type { Agent, ConnectionRecord } from '../src' -import type { AcceptProofProposalOptions } from '../src/modules/proofs/ProofsApiOptions' -import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' -import type { CredDefId } from 'indy-sdk' - -import { - ProofExchangeRecord, - AttributeFilter, - PredicateType, - ProofAttributeInfo, - ProofPredicateInfo, - ProofState, -} from '../src' -import { getGroupKeysFromIndyProofFormatData } from '../src/modules/proofs/__tests__/groupKeys' -import { - V2_INDY_PRESENTATION_PROPOSAL, - V2_INDY_PRESENTATION_REQUEST, - V2_INDY_PRESENTATION, -} from '../src/modules/proofs/formats/ProofFormatConstants' -import { - V2PresentationMessage, - V2ProposalPresentationMessage, - V2RequestPresentationMessage, -} from '../src/modules/proofs/protocol/v2/messages' -import { DidCommMessageRepository } from '../src/storage/didcomm' - -import { setupProofsTest, waitForProofExchangeRecord } from './helpers' -import testLogger from './logger' +import type { Agent } from '../../../../../agent/Agent' +import type { ConnectionRecord } from '../../../../connections' +import type { V1PresentationPreview } from '../../v1' + +import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import testLogger from '../../../../../../tests/logger' +import { getGroupKeysFromIndyProofFormatData } from '../../../formats/indy/__tests__/groupKeys' +import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { ProofState } from '../../../models' +import { ProofExchangeRecord } from '../../../repository' +import { V2ProposePresentationMessage, V2RequestPresentationMessage, V2PresentationMessage } from '../messages' describe('Present Proof', () => { let faberAgent: Agent let aliceAgent: Agent - let credDefId: CredDefId + let credDefId: string let aliceConnection: ConnectionRecord let faberConnection: ConnectionRecord let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord - let presentationPreview: PresentationPreview - let didCommMessageRepository: DidCommMessageRepository + let presentationPreview: V1PresentationPreview beforeAll(async () => { testLogger.test('Initializing the agents') ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('Faber agent', 'Alice agent')) + await setupProofsTest('Faber agent indy proofs', 'Alice agent indy proofs')) }) afterAll(async () => { @@ -67,7 +49,6 @@ describe('Present Proof', () => { indy: { name: 'abc', version: '1.0', - nonce: '947121108704767252195126', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, }, @@ -78,22 +59,16 @@ describe('Present Proof', () => { testLogger.test('Faber waits for a presentation proposal from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_PROPOSAL, + format: 'hlindy/proof-req@v2.0', }, ], - proposalsAttach: [ + proposalAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -111,36 +86,30 @@ describe('Present Proof', () => { protocolVersion: 'v2', }) - const acceptProposalOptions: AcceptProofProposalOptions = { - proofRecordId: faberProofExchangeRecord.id, - } - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) // Faber accepts the presentation proposal from Alice testLogger.test('Faber accepts presentation proposal from Alice') - faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ + proofRecordId: faberProofExchangeRecord.id, + }) // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_REQUEST, + format: 'hlindy/proof-req@v2.0', }, ], - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -158,11 +127,8 @@ describe('Present Proof', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { @@ -179,20 +145,16 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2PresentationMessage, - }) - + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) expect(presentation).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION, + format: 'hlindy/proof@v2.0', }, ], - presentationsAttach: [ + presentationAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -220,7 +182,7 @@ describe('Present Proof', () => { // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') @@ -249,7 +211,7 @@ describe('Present Proof', () => { const requestMessage = await aliceAgent.proofs.findRequestMessage(aliceProofExchangeRecord.id) const presentationMessage = await aliceAgent.proofs.findPresentationMessage(aliceProofExchangeRecord.id) - expect(proposalMessage).toBeInstanceOf(V2ProposalPresentationMessage) + expect(proposalMessage).toBeInstanceOf(V2ProposePresentationMessage) expect(requestMessage).toBeInstanceOf(V2RequestPresentationMessage) expect(presentationMessage).toBeInstanceOf(V2PresentationMessage) @@ -263,7 +225,7 @@ describe('Present Proof', () => { indy: { name: 'abc', version: '1.0', - nonce: '947121108704767252195126', + nonce: expect.any(String), requested_attributes: { 0: { name: 'name', @@ -295,7 +257,7 @@ describe('Present Proof', () => { indy: { name: 'abc', version: '1.0', - nonce: '947121108704767252195126', + nonce: expect.any(String), requested_attributes: { 0: { name: 'name', @@ -389,6 +351,8 @@ describe('Present Proof', () => { connectionId: faberConnection.id, proofFormats: { indy: { + name: 'Proof Request', + version: '1.0.0', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -399,20 +363,16 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_REQUEST, + format: 'hlindy/proof-req@v2.0', }, ], - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -434,11 +394,8 @@ describe('Present Proof', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { @@ -455,20 +412,16 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2PresentationMessage, - }) - + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) expect(presentation).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION, + format: 'hlindy/proof@v2.0', }, ], - presentationsAttach: [ + presentationAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -496,7 +449,7 @@ describe('Present Proof', () => { // Faber accepts the presentation testLogger.test('Faber accept the presentation from Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she receives a presentation acknowledgement testLogger.test('Alice waits for acceptance by Faber') @@ -567,9 +520,8 @@ describe('Present Proof', () => { connectionId: faberConnection.id, proofFormats: { indy: { - name: 'proof-request', - version: '1.0', - nonce: '1298236324864', + name: 'Proof Request', + version: '1.0.0', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -580,18 +532,76 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - const retrievedCredentials = await faberAgent.proofs.getRequestedCredentialsForProofRequest({ - proofRecordId: faberProofExchangeRecord.id, - config: {}, + const retrievedCredentials = await aliceAgent.proofs.getCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, }) - if (retrievedCredentials.proofFormats.indy) { - const keys = Object.keys(retrievedCredentials.proofFormats.indy?.requestedAttributes) - expect(keys).toContain('name') - expect(keys).toContain('image_0') - } else { - fail() - } + expect(retrievedCredentials).toMatchObject({ + proofFormats: { + indy: { + attributes: { + name: [ + { + credentialId: expect.any(String), + revealed: true, + credentialInfo: { + referent: expect.any(String), + attributes: { + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + name: 'John', + age: '99', + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + revocationRegistryId: null, + credentialRevocationId: null, + }, + }, + ], + image_0: [ + { + credentialId: expect.any(String), + revealed: true, + credentialInfo: { + referent: expect.any(String), + attributes: { + age: '99', + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + name: 'John', + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + revocationRegistryId: null, + credentialRevocationId: null, + }, + }, + ], + }, + predicates: { + age: [ + { + credentialId: expect.any(String), + credentialInfo: { + referent: expect.any(String), + attributes: { + image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', + image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', + name: 'John', + age: '99', + }, + schemaId: expect.any(String), + credentialDefinitionId: expect.any(String), + revocationRegistryId: null, + credentialRevocationId: null, + }, + }, + ], + }, + }, + }, + }) }) test('Faber starts with proof request to Alice but gets Problem Reported', async () => { @@ -641,7 +651,6 @@ describe('Present Proof', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -652,20 +661,17 @@ describe('Present Proof', () => { testLogger.test('Alice waits for presentation request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, - }) + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', formats: [ { attachmentId: expect.any(String), - format: V2_INDY_PRESENTATION_REQUEST, + format: 'hlindy/proof-req@v2.0', }, ], - requestPresentationsAttach: [ + requestAttachments: [ { id: expect.any(String), mimeType: 'application/json', @@ -689,10 +695,10 @@ describe('Present Proof', () => { state: ProofState.Abandoned, }) - aliceProofExchangeRecord = await aliceAgent.proofs.sendProblemReport( - aliceProofExchangeRecord.id, - 'Problem inside proof request' - ) + aliceProofExchangeRecord = await aliceAgent.proofs.sendProblemReport({ + description: 'Problem inside proof request', + proofRecordId: aliceProofExchangeRecord.id, + }) faberProofExchangeRecord = await faberProofExchangeRecordPromise diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts index 3d28970a6e..43b9e15a69 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationAckHandler.ts @@ -1,17 +1,17 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { ProofService } from '../../../ProofService' +import type { ProofProtocol } from '../../ProofProtocol' import { V2PresentationAckMessage } from '../messages' export class V2PresentationAckHandler implements MessageHandler { - private proofService: ProofService + private proofProtocol: ProofProtocol public supportedMessages = [V2PresentationAckMessage] - public constructor(proofService: ProofService) { - this.proofService = proofService + public constructor(proofProtocol: ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - await this.proofService.processAck(messageContext) + await this.proofProtocol.processAck(messageContext) } } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts index 9f2d074d67..e3c68c1d84 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts @@ -1,75 +1,56 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { DidCommMessageRepository } from '../../../../../storage' -import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' import type { ProofExchangeRecord } from '../../../repository' -import type { V2ProofService } from '../V2ProofService' +import type { V2ProofProtocol } from '../V2ProofProtocol' import { OutboundMessageContext } from '../../../../../agent/models' +import { DidCommMessageRepository } from '../../../../../storage' import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' export class V2PresentationHandler implements MessageHandler { - private proofService: V2ProofService - private agentConfig: AgentConfig - private proofResponseCoordinator: ProofResponseCoordinator - private didCommMessageRepository: DidCommMessageRepository + private proofProtocol: V2ProofProtocol public supportedMessages = [V2PresentationMessage] - public constructor( - proofService: V2ProofService, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - didCommMessageRepository: DidCommMessageRepository - ) { - this.proofService = proofService - this.agentConfig = agentConfig - this.proofResponseCoordinator = proofResponseCoordinator - this.didCommMessageRepository = didCommMessageRepository + public constructor(proofProtocol: V2ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const proofRecord = await this.proofService.processPresentation(messageContext) + const proofRecord = await this.proofProtocol.processPresentation(messageContext) - const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToPresentation( - messageContext.agentContext, - proofRecord - ) + const shouldAutoRespond = await this.proofProtocol.shouldAutoRespondToPresentation(messageContext.agentContext, { + proofRecord, + presentationMessage: messageContext.message, + }) if (shouldAutoRespond) { - return await this.createAck(proofRecord, messageContext) + return await this.acceptPresentation(proofRecord, messageContext) } } - private async createAck( - record: ProofExchangeRecord, + private async acceptPresentation( + proofRecord: ProofExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending acknowledgement with autoAccept on ${this.agentConfig.autoAcceptProofs}` - ) + messageContext.agentContext.config.logger.info(`Automatically sending acknowledgement with autoAccept`) - const { message, proofRecord } = await this.proofService.createAck(messageContext.agentContext, { - proofRecord: record, + const { message } = await this.proofProtocol.acceptPresentation(messageContext.agentContext, { + proofRecord, }) - const requestMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) + const requestMessage = await didCommMessageRepository.findAgentMessage(messageContext.agentContext, { associatedRecordId: proofRecord.id, messageClass: V2RequestPresentationMessage, }) - const presentationMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2PresentationMessage, - }) - if (messageContext.connection) { return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, connection: messageContext.connection, associatedRecord: proofRecord, }) - } else if (requestMessage?.service && presentationMessage?.service) { - const recipientService = presentationMessage?.service + } else if (requestMessage?.service && messageContext.message?.service) { + const recipientService = messageContext.message?.service const ourService = requestMessage?.service return new OutboundMessageContext(message, { @@ -81,6 +62,6 @@ export class V2PresentationHandler implements MessageHandler { }) } - this.agentConfig.logger.error(`Could not automatically create presentation ack`) + messageContext.agentContext.config.logger.error(`Could not automatically create presentation ack`) } } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts index 947a8c6c44..5d9512d824 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationProblemReportHandler.ts @@ -1,13 +1,13 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { V2ProofService } from '../V2ProofService' +import type { V2ProofProtocol } from '../V2ProofProtocol' import { V2PresentationProblemReportMessage } from '../messages' export class V2PresentationProblemReportHandler implements MessageHandler { - private proofService: V2ProofService + private proofService: V2ProofProtocol public supportedMessages = [V2PresentationProblemReportMessage] - public constructor(proofService: V2ProofService) { + public constructor(proofService: V2ProofProtocol) { this.proofService = proofService } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts index 9432a3ca56..589ff6db3e 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts @@ -1,99 +1,42 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { DidCommMessageRepository } from '../../../../../storage' -import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' -import type { ProofFormat } from '../../../formats/ProofFormat' -import type { - CreateProofRequestFromProposalOptions, - CreateRequestAsResponseOptions, - ProofRequestFromProposalOptions, -} from '../../../models/ProofServiceOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V2ProofService } from '../V2ProofService' +import type { V2ProofProtocol } from '../V2ProofProtocol' import { OutboundMessageContext } from '../../../../../agent/models' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' -import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' +import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' -export class V2ProposePresentationHandler implements MessageHandler { - private proofService: V2ProofService - private agentConfig: AgentConfig - private didCommMessageRepository: DidCommMessageRepository - private proofResponseCoordinator: ProofResponseCoordinator - public supportedMessages = [V2ProposalPresentationMessage] +export class V2ProposePresentationHandler implements MessageHandler { + private proofProtocol: V2ProofProtocol + public supportedMessages = [V2ProposePresentationMessage] - public constructor( - proofService: V2ProofService, - agentConfig: AgentConfig, - didCommMessageRepository: DidCommMessageRepository, - proofResponseCoordinator: ProofResponseCoordinator - ) { - this.proofService = proofService - this.agentConfig = agentConfig - this.didCommMessageRepository = didCommMessageRepository - this.proofResponseCoordinator = proofResponseCoordinator + public constructor(proofProtocol: V2ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const proofRecord = await this.proofService.processProposal(messageContext) + const proofRecord = await this.proofProtocol.processProposal(messageContext) - const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToProposal( - messageContext.agentContext, - proofRecord - ) + const shouldAutoRespond = await this.proofProtocol.shouldAutoRespondToProposal(messageContext.agentContext, { + proofRecord, + proposalMessage: messageContext.message, + }) if (shouldAutoRespond) { - return this.createRequest(proofRecord, messageContext) + return this.acceptProposal(proofRecord, messageContext) } } - - private async createRequest( + private async acceptProposal( proofRecord: ProofExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - this.agentConfig.logger.info( - `Automatically sending request with autoAccept on ${this.agentConfig.autoAcceptProofs}` - ) + messageContext.agentContext.config.logger.info(`Automatically sending request with autoAccept`) if (!messageContext.connection) { - this.agentConfig.logger.error('No connection on the messageContext') - throw new AriesFrameworkError('No connection on the messageContext') - } - - const proposalMessage = await this.didCommMessageRepository.findAgentMessage(messageContext.agentContext, { - associatedRecordId: proofRecord.id, - messageClass: V2ProposalPresentationMessage, - }) - - if (!proposalMessage) { - this.agentConfig.logger.error(`Proof record with id ${proofRecord.id} is missing required credential proposal`) - throw new AriesFrameworkError(`Proof record with id ${proofRecord.id} is missing required credential proposal`) - } - - const proofRequestFromProposalOptions: CreateProofRequestFromProposalOptions = { - proofRecord, - } - - const proofRequest: ProofRequestFromProposalOptions = await this.proofService.createProofRequestFromProposal( - messageContext.agentContext, - proofRequestFromProposalOptions - ) - - const indyProofRequest = proofRequest.proofFormats - - if (!indyProofRequest) { - this.agentConfig.logger.error('Failed to create proof request') - throw new AriesFrameworkError('Failed to create proof request.') - } - - const options: CreateRequestAsResponseOptions = { - proofRecord: proofRecord, - autoAcceptProof: proofRecord.autoAcceptProof, - proofFormats: indyProofRequest, - willConfirm: true, + messageContext.agentContext.config.logger.error('No connection on the messageContext, aborting auto accept') + return } - const { message } = await this.proofService.createRequestAsResponse(messageContext.agentContext, options) + const { message } = await this.proofProtocol.acceptProposal(messageContext.agentContext, { proofRecord }) return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts index 65edcd85c5..e43a60df7e 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts @@ -1,86 +1,45 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' -import type { MediationRecipientService, RoutingService } from '../../../../routing' -import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator' -import type { ProofFormat } from '../../../formats/ProofFormat' -import type { - FormatRequestedCredentialReturn, - FormatRetrievedCredentialOptions, -} from '../../../models/ProofServiceOptions' import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V2ProofService } from '../V2ProofService' +import type { V2ProofProtocol } from '../V2ProofProtocol' import { OutboundMessageContext } from '../../../../../agent/models' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' import { DidCommMessageRole } from '../../../../../storage' +import { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' +import { RoutingService } from '../../../../routing' import { V2RequestPresentationMessage } from '../messages/V2RequestPresentationMessage' -export class V2RequestPresentationHandler implements MessageHandler { - private proofService: V2ProofService - private agentConfig: AgentConfig - private proofResponseCoordinator: ProofResponseCoordinator - private mediationRecipientService: MediationRecipientService - private didCommMessageRepository: DidCommMessageRepository - private routingService: RoutingService +export class V2RequestPresentationHandler implements MessageHandler { + private proofProtocol: V2ProofProtocol public supportedMessages = [V2RequestPresentationMessage] - public constructor( - proofService: V2ProofService, - agentConfig: AgentConfig, - proofResponseCoordinator: ProofResponseCoordinator, - mediationRecipientService: MediationRecipientService, - didCommMessageRepository: DidCommMessageRepository, - routingService: RoutingService - ) { - this.proofService = proofService - this.agentConfig = agentConfig - this.proofResponseCoordinator = proofResponseCoordinator - this.mediationRecipientService = mediationRecipientService - this.didCommMessageRepository = didCommMessageRepository - this.routingService = routingService + public constructor(proofProtocol: V2ProofProtocol) { + this.proofProtocol = proofProtocol } public async handle(messageContext: MessageHandlerInboundMessage) { - const proofRecord = await this.proofService.processRequest(messageContext) + const proofRecord = await this.proofProtocol.processRequest(messageContext) + + const shouldAutoRespond = await this.proofProtocol.shouldAutoRespondToRequest(messageContext.agentContext, { + proofRecord, + requestMessage: messageContext.message, + }) - const shouldAutoRespond = await this.proofResponseCoordinator.shouldAutoRespondToRequest( - messageContext.agentContext, - proofRecord - ) + messageContext.agentContext.config.logger.debug(`Should auto respond to request: ${shouldAutoRespond}`) if (shouldAutoRespond) { - return await this.createPresentation(proofRecord, messageContext) + return await this.acceptRequest(proofRecord, messageContext) } } - private async createPresentation( - record: ProofExchangeRecord, + private async acceptRequest( + proofRecord: ProofExchangeRecord, messageContext: MessageHandlerInboundMessage ) { - const requestMessage = await this.didCommMessageRepository.getAgentMessage(messageContext.agentContext, { - associatedRecordId: record.id, - messageClass: V2RequestPresentationMessage, - }) - - this.agentConfig.logger.info( - `Automatically sending presentation with autoAccept on ${this.agentConfig.autoAcceptProofs}` - ) + messageContext.agentContext.config.logger.info(`Automatically sending presentation with autoAccept`) - const retrievedCredentials: FormatRetrievedCredentialOptions = - await this.proofService.getRequestedCredentialsForProofRequest(messageContext.agentContext, { - proofRecord: record, - config: { - filterByPresentationPreview: false, - }, - }) - - const requestedCredentials: FormatRequestedCredentialReturn = - await this.proofService.autoSelectCredentialsForProofRequest(retrievedCredentials) - - const { message, proofRecord } = await this.proofService.createPresentation(messageContext.agentContext, { - proofRecord: record, - proofFormats: requestedCredentials.proofFormats, + const { message } = await this.proofProtocol.acceptRequest(messageContext.agentContext, { + proofRecord, }) if (messageContext.connection) { @@ -89,16 +48,19 @@ export class V2RequestPresentationHandler(RoutingService) + const didCommMessageRepository = messageContext.agentContext.dependencyManager.resolve(DidCommMessageRepository) + + const routing = await routingService.getRouting(messageContext.agentContext) message.service = new ServiceDecorator({ serviceEndpoint: routing.endpoints[0], recipientKeys: [routing.recipientKey.publicKeyBase58], routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) - const recipientService = requestMessage.service + const recipientService = messageContext.message.service - await this.didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { + await didCommMessageRepository.saveOrUpdateAgentMessage(messageContext.agentContext, { agentMessage: message, associatedRecordId: proofRecord.id, role: DidCommMessageRole.Sender, @@ -113,6 +75,6 @@ export class V2RequestPresentationHandler { - const attachment = this.presentationsAttach.find((attachment) => attachment.id === format.attachmentId) - - if (!attachment) { - throw new AriesFrameworkError(`Could not find a matching attachment with attachmentId: ${format.attachmentId}`) - } - - attachmentFormats.push({ format, attachment }) - }) - return attachmentFormats - } - @IsValidMessageType(V2PresentationMessage.type) public readonly type = V2PresentationMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/present-proof/2.0/presentation') @@ -89,5 +62,9 @@ export class V2PresentationMessage extends AgentMessage { @IsArray() @ValidateNested({ each: true }) @IsInstance(Attachment, { each: true }) - public presentationsAttach!: Attachment[] + public presentationAttachments!: Attachment[] + + public getPresentationAttachmentById(id: string): Attachment | undefined { + return this.presentationAttachments.find((attachment) => attachment.id === id) + } } diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationProblemReportMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationProblemReportMessage.ts index b36a69a7fe..ed97f72319 100644 --- a/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationProblemReportMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationProblemReportMessage.ts @@ -1,22 +1,10 @@ -import type { ProblemReportMessageOptions } from '../../../../problem-reports/messages/ProblemReportMessage' - import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' -export type V2PresentationProblemReportMessageOptions = ProblemReportMessageOptions - /** * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md */ export class V2PresentationProblemReportMessage extends ProblemReportMessage { - /** - * Create new PresentationProblemReportMessage instance. - * @param options - */ - public constructor(options: V2PresentationProblemReportMessageOptions) { - super(options) - } - @IsValidMessageType(V2PresentationProblemReportMessage.type) public readonly type = V2PresentationProblemReportMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/present-proof/2.0/problem-report') diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposalPresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposalPresentationMessage.ts deleted file mode 100644 index 265ed8ae7e..0000000000 --- a/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposalPresentationMessage.ts +++ /dev/null @@ -1,100 +0,0 @@ -import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' - -import { Expose, Type } from 'class-transformer' -import { IsArray, IsBoolean, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' - -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { uuid } from '../../../../../utils/uuid' -import { ProofFormatSpec } from '../../../models/ProofFormatSpec' - -export interface V2ProposePresentationMessageOptions { - id?: string - comment?: string - goalCode?: string - willConfirm?: boolean - parentThreadId?: string - attachmentInfo: ProofAttachmentFormat[] -} - -export class V2ProposalPresentationMessage extends AgentMessage { - public constructor(options: V2ProposePresentationMessageOptions) { - super() - - if (options) { - this.formats = [] - this.proposalsAttach = [] - this.id = options.id ?? uuid() - this.comment = options.comment - this.goalCode = options.goalCode - this.willConfirm = options.willConfirm ?? false - - if (options.parentThreadId) { - this.setThread({ - parentThreadId: options.parentThreadId, - }) - } - - for (const entry of options.attachmentInfo) { - this.addProposalsAttachment(entry) - } - } - } - - public addProposalsAttachment(attachment: ProofAttachmentFormat) { - this.formats.push(attachment.format) - this.proposalsAttach.push(attachment.attachment) - } - - /** - * Every attachment has a corresponding entry in the formats array. - * This method pairs those together in a {@link ProofAttachmentFormat} object. - */ - public getAttachmentFormats(): ProofAttachmentFormat[] { - const attachmentFormats: ProofAttachmentFormat[] = [] - - this.formats.forEach((format) => { - const attachment = this.proposalsAttach.find((attachment) => attachment.id === format.attachmentId) - - if (!attachment) { - throw new AriesFrameworkError(`Could not find a matching attachment with attachmentId: ${format.attachmentId}`) - } - - attachmentFormats.push({ format, attachment }) - }) - return attachmentFormats - } - - @IsValidMessageType(V2ProposalPresentationMessage.type) - public readonly type = V2ProposalPresentationMessage.type.messageTypeUri - public static readonly type = parseMessageType(`https://didcomm.org/present-proof/2.0/propose-presentation`) - - @IsString() - @IsOptional() - public comment?: string - - @Expose({ name: 'goal_code' }) - @IsString() - @IsOptional() - public goalCode?: string - - @Expose({ name: 'will_confirm' }) - @IsBoolean() - public willConfirm = false - - @Expose({ name: 'formats' }) - @Type(() => ProofFormatSpec) - @IsArray() - @ValidateNested({ each: true }) - @IsInstance(ProofFormatSpec, { each: true }) - public formats!: ProofFormatSpec[] - - @Expose({ name: 'proposals~attach' }) - @Type(() => Attachment) - @IsArray() - @ValidateNested({ each: true }) - @IsInstance(Attachment, { each: true }) - public proposalsAttach!: Attachment[] -} diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposePresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposePresentationMessage.ts new file mode 100644 index 0000000000..385925a5d0 --- /dev/null +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposePresentationMessage.ts @@ -0,0 +1,62 @@ +import { Expose, Type } from 'class-transformer' +import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { uuid } from '../../../../../utils/uuid' +import { ProofFormatSpec } from '../../../models/ProofFormatSpec' + +export interface V2ProposePresentationMessageOptions { + id?: string + comment?: string + goalCode?: string + proposalAttachments: Attachment[] + formats: ProofFormatSpec[] +} + +export class V2ProposePresentationMessage extends AgentMessage { + public constructor(options: V2ProposePresentationMessageOptions) { + super() + + if (options) { + this.formats = [] + this.proposalAttachments = [] + this.id = options.id ?? uuid() + this.comment = options.comment + this.goalCode = options.goalCode + this.formats = options.formats + this.proposalAttachments = options.proposalAttachments + } + } + + @IsValidMessageType(V2ProposePresentationMessage.type) + public readonly type = V2ProposePresentationMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/present-proof/2.0/propose-presentation') + + @IsString() + @IsOptional() + public comment?: string + + @Expose({ name: 'goal_code' }) + @IsString() + @IsOptional() + public goalCode?: string + + @Type(() => ProofFormatSpec) + @IsArray() + @ValidateNested({ each: true }) + @IsInstance(ProofFormatSpec, { each: true }) + public formats!: ProofFormatSpec[] + + @Expose({ name: 'proposals~attach' }) + @Type(() => Attachment) + @IsArray() + @ValidateNested({ each: true }) + @IsInstance(Attachment, { each: true }) + public proposalAttachments!: Attachment[] + + public getProposalAttachmentById(id: string): Attachment | undefined { + return this.proposalAttachments.find((attachment) => attachment.id === id) + } +} diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts index 060badc050..f39ba81538 100644 --- a/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts @@ -1,11 +1,8 @@ -import type { ProofAttachmentFormat } from '../../../formats/models/ProofAttachmentFormat' - import { Expose, Type } from 'class-transformer' import { IsArray, IsBoolean, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' import { AgentMessage } from '../../../../../agent/AgentMessage' import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../../error' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { uuid } from '../../../../../utils/uuid' import { ProofFormatSpec } from '../../../models/ProofFormatSpec' @@ -16,8 +13,8 @@ export interface V2RequestPresentationMessageOptions { goalCode?: string presentMultiple?: boolean willConfirm?: boolean - parentThreadId?: string - attachmentInfo: ProofAttachmentFormat[] + formats: ProofFormatSpec[] + requestAttachments: Attachment[] } export class V2RequestPresentationMessage extends AgentMessage { @@ -26,68 +23,17 @@ export class V2RequestPresentationMessage extends AgentMessage { if (options) { this.formats = [] - this.requestPresentationsAttach = [] + this.requestAttachments = [] this.id = options.id ?? uuid() this.comment = options.comment this.goalCode = options.goalCode this.willConfirm = options.willConfirm ?? true this.presentMultiple = options.presentMultiple ?? false - - if (options.parentThreadId) { - this.setThread({ - parentThreadId: options.parentThreadId, - }) - } - - for (const entry of options.attachmentInfo) { - this.addRequestPresentationsAttachment(entry) - } + this.requestAttachments = options.requestAttachments + this.formats = options.formats } } - public addRequestPresentationsAttachment(attachment: ProofAttachmentFormat) { - this.formats.push(attachment.format) - this.requestPresentationsAttach.push(attachment.attachment) - } - - public getAttachmentByFormatIdentifier(formatIdentifier: string) { - const format = this.formats.find((x) => x.format === formatIdentifier) - if (!format) { - throw new AriesFrameworkError( - `Expected to find a format entry of type: ${formatIdentifier}, but none could be found.` - ) - } - - const attachment = this.requestPresentationsAttach.find((x) => x.id === format.attachmentId) - - if (!attachment) { - throw new AriesFrameworkError( - `Expected to find an attachment entry with id: ${format.attachmentId}, but none could be found.` - ) - } - - return attachment - } - - /** - * Every attachment has a corresponding entry in the formats array. - * This method pairs those together in a {@link ProofAttachmentFormat} object. - */ - public getAttachmentFormats(): ProofAttachmentFormat[] { - const attachmentFormats: ProofAttachmentFormat[] = [] - - this.formats.forEach((format) => { - const attachment = this.requestPresentationsAttach.find((attachment) => attachment.id === format.attachmentId) - - if (!attachment) { - throw new AriesFrameworkError(`Could not find a matching attachment with attachmentId: ${format.attachmentId}`) - } - - attachmentFormats.push({ format, attachment }) - }) - return attachmentFormats - } - @IsValidMessageType(V2RequestPresentationMessage.type) public readonly type = V2RequestPresentationMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/present-proof/2.0/request-presentation') @@ -121,5 +67,9 @@ export class V2RequestPresentationMessage extends AgentMessage { @IsArray() @ValidateNested({ each: true }) @IsInstance(Attachment, { each: true }) - public requestPresentationsAttach!: Attachment[] + public requestAttachments!: Attachment[] + + public getRequestAttachmentById(id: string): Attachment | undefined { + return this.requestAttachments.find((attachment) => attachment.id === id) + } } diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/index.ts b/packages/core/src/modules/proofs/protocol/v2/messages/index.ts index 8b0c4a005d..515b0afb9c 100644 --- a/packages/core/src/modules/proofs/protocol/v2/messages/index.ts +++ b/packages/core/src/modules/proofs/protocol/v2/messages/index.ts @@ -1,5 +1,5 @@ export * from './V2PresentationAckMessage' export * from './V2PresentationMessage' export * from './V2PresentationProblemReportMessage' -export * from './V2ProposalPresentationMessage' +export * from './V2ProposePresentationMessage' export * from './V2RequestPresentationMessage' diff --git a/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts b/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts index f145703dff..30d236a1ac 100644 --- a/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts +++ b/packages/core/src/modules/proofs/repository/ProofExchangeRecord.ts @@ -82,6 +82,14 @@ export class ProofExchangeRecord extends BaseRecord ): Promise { @@ -367,6 +367,15 @@ export class W3cCredentialService { const result = await this.w3cCredentialRepository.findSingleByQuery(agentContext, query) return result?.credential } + public getProofTypeByVerificationMethodType(verificationMethodType: string): string { + const suite = this.signatureSuiteRegistry.getByVerificationMethodType(verificationMethodType) + + if (!suite) { + throw new AriesFrameworkError(`No suite found for verification method type ${verificationMethodType}}`) + } + + return suite.proofType + } private getSignatureSuitesForCredential(agentContext: AgentContext, credential: W3cVerifiableCredential) { const WalletKeyPair = createWalletKeyPairClass(agentContext.wallet) diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index c339fbfb4e..d6f3eb266f 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -297,10 +297,7 @@ describe('W3cCredentialService', () => { const result = await w3cCredentialService.verifyPresentation(agentContext, { presentation: vp, - proofType: 'Ed25519Signature2018', challenge: '7bf32d0b-39d4-41f3-96b6-45de52988e4c', - verificationMethod: - 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', }) expect(result.verified).toBe(true) diff --git a/packages/core/src/modules/vc/__tests__/contexts/citizenship_v2.ts b/packages/core/src/modules/vc/__tests__/contexts/citizenship_v2.ts new file mode 100644 index 0000000000..667571bea2 --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/citizenship_v2.ts @@ -0,0 +1,45 @@ +export const CITIZENSHIP_V2 = { + '@context': { + '@version': 1.1, + '@protected': true, + name: 'http://schema.org/name', + description: 'http://schema.org/description', + identifier: 'http://schema.org/identifier', + image: { '@id': 'http://schema.org/image', '@type': '@id' }, + PermanentResidentCard: { + '@id': 'https://w3id.org/citizenship#PermanentResidentCard', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + description: 'http://schema.org/description', + name: 'http://schema.org/name', + identifier: 'http://schema.org/identifier', + image: { '@id': 'http://schema.org/image', '@type': '@id' }, + }, + }, + PermanentResident: { + '@id': 'https://w3id.org/citizenship#PermanentResident', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + ctzn: 'https://w3id.org/citizenship#', + schema: 'http://schema.org/', + xsd: 'http://www.w3.org/2001/XMLSchema#', + birthCountry: 'ctzn:birthCountry', + birthDate: { '@id': 'schema:birthDate', '@type': 'xsd:dateTime' }, + commuterClassification: 'ctzn:commuterClassification', + familyName: 'schema:familyName', + gender: 'schema:gender', + givenName: 'schema:givenName', + lprCategory: 'ctzn:lprCategory', + lprNumber: 'ctzn:lprNumber', + residentSince: { '@id': 'ctzn:residentSince', '@type': 'xsd:dateTime' }, + }, + }, + Person: 'http://schema.org/Person', + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/index.ts b/packages/core/src/modules/vc/__tests__/contexts/index.ts index c66801c24a..0d5bfff11a 100644 --- a/packages/core/src/modules/vc/__tests__/contexts/index.ts +++ b/packages/core/src/modules/vc/__tests__/contexts/index.ts @@ -8,4 +8,6 @@ export * from './schema_org' export * from './security_v1' export * from './security_v2' export * from './security_v3_unstable' +export * from './submission' export * from './vaccination_v1' +export * from './vaccination_v2' diff --git a/packages/core/src/modules/vc/__tests__/contexts/submission.ts b/packages/core/src/modules/vc/__tests__/contexts/submission.ts new file mode 100644 index 0000000000..4df5ca9b4f --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/submission.ts @@ -0,0 +1,15 @@ +export const PRESENTATION_SUBMISSION = { + '@context': { + '@version': 1.1, + PresentationSubmission: { + '@id': 'https://identity.foundation/presentation-exchange/#presentation-submission', + '@context': { + '@version': 1.1, + presentation_submission: { + '@id': 'https://identity.foundation/presentation-exchange/#presentation-submission', + '@type': '@json', + }, + }, + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/contexts/vaccination_v2.ts b/packages/core/src/modules/vc/__tests__/contexts/vaccination_v2.ts new file mode 100644 index 0000000000..483c87134b --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/contexts/vaccination_v2.ts @@ -0,0 +1,88 @@ +export const VACCINATION_V2 = { + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + description: 'http://schema.org/description', + identifier: 'http://schema.org/identifier', + name: 'http://schema.org/name', + image: 'http://schema.org/image', + VaccinationCertificate: { + '@id': 'https://w3id.org/vaccination#VaccinationCertificate', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + description: 'http://schema.org/description', + identifier: 'http://schema.org/identifier', + name: 'http://schema.org/name', + image: 'http://schema.org/image', + }, + }, + VaccinationEvent: { + '@id': 'https://w3id.org/vaccination#VaccinationEvent', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + administeringCentre: 'https://w3id.org/vaccination#administeringCentre', + batchNumber: 'https://w3id.org/vaccination#batchNumber', + countryOfVaccination: 'https://w3id.org/vaccination#countryOfVaccination', + dateOfVaccination: { + '@id': 'https://w3id.org/vaccination#dateOfVaccination', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + healthProfessional: 'https://w3id.org/vaccination#healthProfessional', + nextVaccinationDate: { + '@id': 'https://w3id.org/vaccination#nextVaccinationDate', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + order: 'https://w3id.org/vaccination#order', + recipient: { + '@id': 'https://w3id.org/vaccination#recipient', + '@type': 'https://w3id.org/vaccination#VaccineRecipient', + }, + vaccine: { + '@id': 'https://w3id.org/vaccination#VaccineEventVaccine', + '@type': 'https://w3id.org/vaccination#Vaccine', + }, + }, + }, + VaccineRecipient: { + '@id': 'https://w3id.org/vaccination#VaccineRecipient', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + birthDate: { + '@id': 'http://schema.org/birthDate', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime', + }, + familyName: 'http://schema.org/familyName', + gender: 'http://schema.org/gender', + givenName: 'http://schema.org/givenName', + }, + }, + Vaccine: { + '@id': 'https://w3id.org/vaccination#Vaccine', + '@context': { + '@version': 1.1, + '@protected': true, + id: '@id', + type: '@type', + atcCode: 'https://w3id.org/vaccination#atc-code', + disease: 'https://w3id.org/vaccination#disease', + event: { + '@id': 'https://w3id.org/vaccination#VaccineRecipientVaccineEvent', + '@type': 'https://w3id.org/vaccination#VaccineEvent', + }, + marketingAuthorizationHolder: 'https://w3id.org/vaccination#marketingAuthorizationHolder', + medicinalProductName: 'https://w3id.org/vaccination#medicinalProductName', + }, + }, + }, +} diff --git a/packages/core/src/modules/vc/__tests__/documentLoader.ts b/packages/core/src/modules/vc/__tests__/documentLoader.ts index 4d7aa89f0d..adf72dba7f 100644 --- a/packages/core/src/modules/vc/__tests__/documentLoader.ts +++ b/packages/core/src/modules/vc/__tests__/documentLoader.ts @@ -4,9 +4,18 @@ import type { DocumentLoaderResult } from '../libraries/jsonld' import jsonld from '../libraries/jsonld' -import { BBS_V1, EXAMPLES_V1, ODRL, SCHEMA_ORG, VACCINATION_V1 } from './contexts' +import { + BBS_V1, + EXAMPLES_V1, + ODRL, + PRESENTATION_SUBMISSION, + SCHEMA_ORG, + VACCINATION_V1, + VACCINATION_V2, +} from './contexts' import { X25519_V1 } from './contexts/X25519_v1' import { CITIZENSHIP_V1 } from './contexts/citizenship_v1' +import { CITIZENSHIP_V2 } from './contexts/citizenship_v2' import { CREDENTIALS_V1 } from './contexts/credentials_v1' import { DID_V1 } from './contexts/did_v1' import { ED25519_V1 } from './contexts/ed25519_v1' @@ -95,9 +104,12 @@ export const DOCUMENTS = { 'https://www.w3.org/ns/did/v1': DID_V1, 'https://w3.org/ns/did/v1': DID_V1, 'https://w3id.org/citizenship/v1': CITIZENSHIP_V1, + 'https://w3id.org/citizenship/v2': CITIZENSHIP_V2, 'https://www.w3.org/ns/odrl.jsonld': ODRL, 'http://schema.org/': SCHEMA_ORG, 'https://w3id.org/vaccination/v1': VACCINATION_V1, + 'https://w3id.org/vaccination/v2': VACCINATION_V2, + 'https://identity.foundation/presentation-exchange/submission/v1': PRESENTATION_SUBMISSION, 'https://mattr.global/contexts/vc-extensions/v1': MATTR_VC_EXTENSION_V1, 'https://purl.imsglobal.org/spec/ob/v3p0/context.json': PURL_OB_V3P0, 'https://w3c-ccg.github.io/vc-status-rl-2020/contexts/vc-revocation-list-2020/v1.jsonld': VC_REVOCATION_LIST_2020, diff --git a/packages/core/src/modules/vc/__tests__/fixtures.ts b/packages/core/src/modules/vc/__tests__/fixtures.ts index 491a388f98..9e8a6caa16 100644 --- a/packages/core/src/modules/vc/__tests__/fixtures.ts +++ b/packages/core/src/modules/vc/__tests__/fixtures.ts @@ -13,6 +13,36 @@ export const Ed25519Signature2018Fixtures = { }, }, }, + TEST_LD_DOCUMENT_2: { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/security/suites/ed25519-2020/v1', + 'https://w3id.org/citizenship/v1', + ], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: '', + identifier: '83627465', + name: 'Permanent Resident Card', + description: 'Government of Example Permanent Resident Card.', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + }, + TEST_LD_DOCUMENT_SIGNED: { '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], type: ['VerifiableCredential', 'UniversityDegreeCredential'], diff --git a/packages/core/src/modules/vc/constants.ts b/packages/core/src/modules/vc/constants.ts index 6b298625df..b166244ebf 100644 --- a/packages/core/src/modules/vc/constants.ts +++ b/packages/core/src/modules/vc/constants.ts @@ -5,6 +5,7 @@ export const SECURITY_CONTEXT_URL = SECURITY_CONTEXT_V2_URL export const SECURITY_X25519_CONTEXT_URL = 'https://w3id.org/security/suites/x25519-2019/v1' export const DID_V1_CONTEXT_URL = 'https://www.w3.org/ns/did/v1' export const CREDENTIALS_CONTEXT_V1_URL = 'https://www.w3.org/2018/credentials/v1' +export const BANKACCOUNT_CONTEXT_V1_URL = 'https://www.w3.org/2018/bankaccount/v1' export const SECURITY_CONTEXT_BBS_URL = 'https://w3id.org/security/bbs/v1' export const CREDENTIALS_ISSUER_URL = 'https://www.w3.org/2018/credentials#issuer' export const SECURITY_PROOF_URL = 'https://w3id.org/security#proof' diff --git a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts index 042550fd8e..7851859949 100644 --- a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts +++ b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts @@ -39,8 +39,6 @@ export interface SignPresentationOptions { export interface VerifyPresentationOptions { presentation: W3cVerifiablePresentation - proofType: string - verificationMethod: string purpose?: ProofPurpose challenge?: string } diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index ad34a75a28..f4647d4891 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -129,7 +129,7 @@ export class UpdateAssistant = BaseAgent> { ) } - if (neededUpdates.length == 0) { + if (neededUpdates.length === 0) { this.agent.config.logger.info('No update needed. Agent storage is up to date.') return } diff --git a/packages/core/src/utils/__tests__/indyProofRequest.test.ts b/packages/core/src/utils/__tests__/indyProofRequest.test.ts deleted file mode 100644 index ef15b80f40..0000000000 --- a/packages/core/src/utils/__tests__/indyProofRequest.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { checkProofRequestForDuplicates } from '../indyProofRequest' - -import { - AriesFrameworkError, - AttributeFilter, - PredicateType, - ProofAttributeInfo, - ProofPredicateInfo, - ProofRequest, -} from '@aries-framework/core' - -describe('Present Proof', () => { - const credDefId = '9vPXgSpQJPkJEALbLXueBp:3:CL:57753:tag1' - const nonce = 'testtesttest12345' - - test('attribute names match', () => { - const attributes = { - age1: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - age2: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const proofRequest = new ProofRequest({ - name: 'proof-request', - version: '1.0', - nonce, - requestedAttributes: attributes, - }) - - expect(() => checkProofRequestForDuplicates(proofRequest)).not.toThrow() - }) - - test('attribute names match with predicates name', () => { - const attributes = { - attrib: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - predicate: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const proofRequest = new ProofRequest({ - name: 'proof-request', - version: '1.0', - nonce, - requestedAttributes: attributes, - requestedPredicates: predicates, - }) - - expect(() => checkProofRequestForDuplicates(proofRequest)).toThrowError(AriesFrameworkError) - }) -}) diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index c0dfd135df..8875930e15 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -14,3 +14,4 @@ export * from './type' export * from './indyIdentifiers' export * from './deepEquality' export * from './objectEquality' +export * from './MessageValidator' diff --git a/packages/core/src/utils/indyProofRequest.ts b/packages/core/src/utils/indyProofRequest.ts index 853bf4f742..df52b72cdc 100644 --- a/packages/core/src/utils/indyProofRequest.ts +++ b/packages/core/src/utils/indyProofRequest.ts @@ -25,7 +25,7 @@ function assertNoDuplicates(predicates: string[], attributeNames: string[]) { } // TODO: This is still not ideal. The requested groups can specify different credentials using restrictions. -export function checkProofRequestForDuplicates(proofRequest: ProofRequest) { +export function assertNoDuplicateGroupsNamesInProofRequest(proofRequest: ProofRequest) { const attributes = attributeNamesToArray(proofRequest) const predicates = predicateNamesToArray(proofRequest) assertNoDuplicates(predicates, attributes) diff --git a/packages/core/src/utils/version.ts b/packages/core/src/utils/version.ts index 82a9597909..241ccbd838 100644 --- a/packages/core/src/utils/version.ts +++ b/packages/core/src/utils/version.ts @@ -7,8 +7,8 @@ export function parseVersionString(version: VersionString): Version { export function isFirstVersionHigherThanSecond(first: Version, second: Version) { return ( first[0] > second[0] || - (first[0] == second[0] && first[1] > second[1]) || - (first[0] == second[0] && first[1] == second[1] && first[2] > second[2]) + (first[0] === second[0] && first[1] > second[1]) || + (first[0] === second[0] && first[1] === second[1] && first[2] > second[2]) ) } diff --git a/packages/core/tests/generic-records.test.ts b/packages/core/tests/generic-records.test.ts index efe7455e2f..627fcb6540 100644 --- a/packages/core/tests/generic-records.test.ts +++ b/packages/core/tests/generic-records.test.ts @@ -56,11 +56,11 @@ describe('genericRecords', () => { test('get generic-record specific record', async () => { //Create genericRecord message const savedRecords1 = await aliceAgent.genericRecords.findAllByQuery({ myTag: 'foobar1' }) - expect(savedRecords1?.length == 1).toBe(true) + expect(savedRecords1?.length === 1).toBe(true) expect(savedRecords1[0].content).toEqual({ foo: 42 }) const savedRecords2 = await aliceAgent.genericRecords.findAllByQuery({ myTag: 'foobar2' }) - expect(savedRecords2.length == 2).toBe(true) + expect(savedRecords2.length === 2).toBe(true) expect(savedRecords2[0].content).toEqual({ foo: 'Some data saved' }) }) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 597cc6bda7..e8fd1f8a29 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -17,7 +17,7 @@ import type { import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' -import type { ProofAttributeInfo, ProofPredicateInfo } from '../src/modules/proofs/formats/indy/models' +import type { ProofAttributeInfo, ProofPredicateInfoOptions } from '../src/modules/proofs/formats/indy/models' import type { AutoAcceptProof } from '../src/modules/proofs/models/ProofAutoAcceptType' import type { Awaited, WalletConfig } from '../src/types' import type { CredDef, Schema } from 'indy-sdk' @@ -53,7 +53,6 @@ import { DidExchangeState, HandshakeProtocol, InjectionSymbols, - LogLevel, ProofEventTypes, } from '../src' import { Key, KeyType } from '../src/crypto' @@ -68,11 +67,7 @@ import { OutOfBandInvitation } from '../src/modules/oob/messages' import { OutOfBandRecord } from '../src/modules/oob/repository' import { PredicateType } from '../src/modules/proofs/formats/indy/models' import { ProofState } from '../src/modules/proofs/models/ProofState' -import { - PresentationPreview, - PresentationPreviewAttribute, - PresentationPreviewPredicate, -} from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import { V1PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import { customDocumentLoader } from '../src/modules/vc/__tests__/documentLoader' import { KeyDerivationMethod } from '../src/types' import { LinkedAttachment } from '../src/utils/LinkedAttachment' @@ -117,7 +112,7 @@ export function getAgentOptions = + subject instanceof ReplaySubject ? subject.asObservable() : subject return firstValueFrom( observable.pipe( filter((e) => previousState === undefined || e.payload.previousState === previousState), @@ -605,71 +601,24 @@ export async function issueCredential({ } } -export async function issueConnectionLessCredential({ - issuerAgent, - holderAgent, - credentialTemplate, -}: { - issuerAgent: Agent - holderAgent: Agent - credentialTemplate: IndyOfferCredentialFormat -}) { - const issuerReplay = new ReplaySubject() - const holderReplay = new ReplaySubject() - - issuerAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(issuerReplay) - holderAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(holderReplay) - - // eslint-disable-next-line prefer-const - let { credentialRecord: issuerCredentialRecord, message } = await issuerAgent.credentials.createOffer({ - comment: 'V1 Out of Band offer', - protocolVersion: 'v1', - credentialFormats: { - indy: { - attributes: credentialTemplate.attributes, - credentialDefinitionId: credentialTemplate.credentialDefinitionId, - }, - }, - autoAcceptCredential: AutoAcceptCredential.ContentApproved, - }) - - const { message: offerMessage } = await issuerAgent.oob.createLegacyConnectionlessInvitation({ - recordId: issuerCredentialRecord.id, - domain: 'https://example.org', - message, - }) - - await holderAgent.receiveMessage(offerMessage.toJSON()) - - let holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { - threadId: issuerCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - const acceptOfferOptions: AcceptCredentialOfferOptions = { - credentialRecordId: holderCredentialRecord.id, - autoAcceptCredential: AutoAcceptCredential.ContentApproved, - } - - await holderAgent.credentials.acceptOffer(acceptOfferOptions) - - holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { - threadId: issuerCredentialRecord.threadId, - state: CredentialState.Done, - }) - - issuerCredentialRecord = await waitForCredentialRecordSubject(issuerReplay, { - threadId: issuerCredentialRecord.threadId, - state: CredentialState.Done, - }) +/** + * Returns mock of function with correct type annotations according to original function `fn`. + * It can be used also for class methods. + * + * @param fn function you want to mock + * @returns mock function with type annotations + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function mockFunction any>(fn: T): jest.MockedFunction { + return fn as jest.MockedFunction +} - return { - issuerCredential: issuerCredentialRecord, - holderCredential: holderCredentialRecord, - } +/** + * Set a property using a getter value on a mocked oject. + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export function mockProperty(object: T, property: K, value: T[K]) { + Object.defineProperty(object, property, { get: () => value }) } export async function presentProof({ @@ -683,7 +632,7 @@ export async function presentProof({ holderAgent: Agent presentationTemplate: { attributes?: Record - predicates?: Record + predicates?: Record } }) { const verifierReplay = new ReplaySubject() @@ -704,7 +653,6 @@ export async function presentProof({ requestedAttributes: attributes, requestedPredicates: predicates, version: '1.0', - nonce: '947121108704767252195123', }, }, protocolVersion: 'v2', @@ -712,11 +660,8 @@ export async function presentProof({ let holderRecord = await holderProofExchangeRecordPromise - const requestedCredentials = await holderAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await holderAgent.proofs.selectCredentialsForRequest({ proofRecordId: holderRecord.id, - config: { - filterByPresentationPreview: true, - }, }) const verifierProofExchangeRecordPromise = waitForProofExchangeRecordSubject(verifierReplay, { @@ -739,7 +684,7 @@ export async function presentProof({ state: ProofState.Done, }) - verifierRecord = await verifierAgent.proofs.acceptPresentation(verifierRecord.id) + verifierRecord = await verifierAgent.proofs.acceptPresentation({ proofRecordId: verifierRecord.id }) holderRecord = await holderProofExchangeRecordPromise return { @@ -748,26 +693,6 @@ export async function presentProof({ } } -/** - * Returns mock of function with correct type annotations according to original function `fn`. - * It can be used also for class methods. - * - * @param fn function you want to mock - * @returns mock function with type annotations - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function mockFunction any>(fn: T): jest.MockedFunction { - return fn as jest.MockedFunction -} - -/** - * Set a property using a getter value on a mocked oject. - */ -// eslint-disable-next-line @typescript-eslint/ban-types -export function mockProperty(object: T, property: K, value: T[K]) { - Object.defineProperty(object, property, { get: () => value }) -} - // Helper type to get the type of the agents (with the custom modules) for the credential tests export type CredentialTestsAgent = Awaited>['aliceAgent'] export async function setupCredentialTests( @@ -857,12 +782,12 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto const unique = uuid().substring(0, 4) - const faberAgentOptions = getAgentOptions(`${faberName}-${unique}`, { + const faberAgentOptions = getAgentOptions(`${faberName} - ${unique}`, { autoAcceptProofs, endpoints: ['rxjs:faber'], }) - const aliceAgentOptions = getAgentOptions(`${aliceName}-${unique}`, { + const aliceAgentOptions = getAgentOptions(`${aliceName} - ${unique}`, { autoAcceptProofs, endpoints: ['rxjs:alice'], }) @@ -893,26 +818,26 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto const faberConnection = agentAConnection const aliceConnection = agentBConnection - const presentationPreview = new PresentationPreview({ + const presentationPreview = new V1PresentationPreview({ attributes: [ - new PresentationPreviewAttribute({ + { name: 'name', credentialDefinitionId: definition.id, referent: '0', value: 'John', - }), - new PresentationPreviewAttribute({ + }, + { name: 'image_0', credentialDefinitionId: definition.id, - }), + }, ], predicates: [ - new PresentationPreviewPredicate({ + { name: 'age', credentialDefinitionId: definition.id, predicate: PredicateType.GreaterThanOrEqualTo, threshold: 50, - }), + }, ], }) diff --git a/packages/core/tests/logger.ts b/packages/core/tests/logger.ts index 46c7066acc..cff2e39586 100644 --- a/packages/core/tests/logger.ts +++ b/packages/core/tests/logger.ts @@ -14,7 +14,7 @@ function logToTransport(logObject: ILogObject) { } export class TestLogger extends BaseLogger { - private logger: Logger + public readonly logger: Logger // Map our log levels to tslog levels private tsLogLevelMap = { @@ -27,29 +27,40 @@ export class TestLogger extends BaseLogger { [LogLevel.fatal]: 'fatal', } as const - public constructor(logLevel: LogLevel, name?: string) { + public static fromLogger(logger: TestLogger, name?: string) { + return new TestLogger(logger.logLevel, name, logger.logger) + } + + public constructor(logLevel: LogLevel, name?: string, logger?: Logger) { super(logLevel) - this.logger = new Logger({ - name, - minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelMap[this.logLevel], - ignoreStackLevels: 5, - attachedTransports: [ - { - transportLogger: { - silly: logToTransport, - debug: logToTransport, - trace: logToTransport, - info: logToTransport, - warn: logToTransport, - error: logToTransport, - fatal: logToTransport, + if (logger) { + this.logger = logger.getChildLogger({ + name, + minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelMap[this.logLevel], + }) + } else { + this.logger = new Logger({ + name, + minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelMap[this.logLevel], + ignoreStackLevels: 5, + attachedTransports: [ + { + transportLogger: { + silly: logToTransport, + debug: logToTransport, + trace: logToTransport, + info: logToTransport, + warn: logToTransport, + error: logToTransport, + fatal: logToTransport, + }, + // always log to file + minLevel: 'silly', }, - // always log to file - minLevel: 'silly', - }, - ], - }) + ], + }) + } } private log(level: Exclude, message: string, data?: Record): void { @@ -93,6 +104,6 @@ export class TestLogger extends BaseLogger { } } -const testLogger = new TestLogger(LogLevel.error) +const testLogger = new TestLogger(LogLevel.off) export default testLogger diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 4df883e972..94161fd838 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { CreateOfferOptions, DefaultCredentialProtocols } from '../src/modules/credentials' +import type { CreateCredentialOfferOptions, DefaultCredentialProtocols } from '../src/modules/credentials' import type { AgentMessage, AgentMessageReceivedEvent } from '@aries-framework/core' import { Subject } from 'rxjs' @@ -58,7 +58,7 @@ describe('out of band', () => { let faberAgent: Agent let aliceAgent: Agent - let credentialTemplate: CreateOfferOptions + let credentialTemplate: CreateCredentialOfferOptions beforeAll(async () => { const faberMessages = new Subject() diff --git a/packages/core/tests/proofs-sub-protocol.test.ts b/packages/core/tests/proofs-sub-protocol.test.ts index 271bd089c3..45fa30e713 100644 --- a/packages/core/tests/proofs-sub-protocol.test.ts +++ b/packages/core/tests/proofs-sub-protocol.test.ts @@ -1,5 +1,5 @@ import type { Agent, ConnectionRecord, ProofExchangeRecord } from '../src' -import type { PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' +import type { V1PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' import type { CredDefId } from 'indy-sdk' import { @@ -21,7 +21,7 @@ describe('Present Proof Subprotocol', () => { let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord let aliceProofExchangeRecord: ProofExchangeRecord - let presentationPreview: PresentationPreview + let presentationPreview: V1PresentationPreview beforeAll(async () => { testLogger.test('Initializing the agents') @@ -57,7 +57,6 @@ describe('Present Proof Subprotocol', () => { indy: { name: 'abc', version: '1.0', - nonce: '947121108704767252195126', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, }, @@ -87,11 +86,8 @@ describe('Present Proof Subprotocol', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, @@ -107,7 +103,7 @@ describe('Present Proof Subprotocol', () => { // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') @@ -161,7 +157,6 @@ describe('Present Proof Subprotocol', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -181,11 +176,8 @@ describe('Present Proof Subprotocol', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, @@ -202,7 +194,7 @@ describe('Present Proof Subprotocol', () => { // Faber accepts the presentation testLogger.test('Faber accept the presentation from Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she receives a presentation acknowledgement testLogger.test('Alice waits for acceptance by Faber') @@ -232,7 +224,6 @@ describe('Present Proof Subprotocol', () => { indy: { name: 'abc', version: '1.0', - nonce: '947121108704767252195126', attributes: presentationPreview.attributes, predicates: presentationPreview.predicates, }, @@ -262,11 +253,8 @@ describe('Present Proof Subprotocol', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, @@ -282,7 +270,7 @@ describe('Present Proof Subprotocol', () => { // Faber accepts the presentation provided by Alice testLogger.test('Faber accepts the presentation provided by Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she received a presentation acknowledgement testLogger.test('Alice waits until she receives a presentation acknowledgement') @@ -336,7 +324,6 @@ describe('Present Proof Subprotocol', () => { indy: { name: 'proof-request', version: '1.0', - nonce: '1298236324864', requestedAttributes: attributes, requestedPredicates: predicates, }, @@ -356,11 +343,8 @@ describe('Present Proof Subprotocol', () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.autoSelectCredentialsForProofRequest({ + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, - config: { - filterByPresentationPreview: true, - }, }) await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, @@ -377,7 +361,7 @@ describe('Present Proof Subprotocol', () => { // Faber accepts the presentation testLogger.test('Faber accept the presentation from Alice') - await faberAgent.proofs.acceptPresentation(faberProofExchangeRecord.id) + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits until she receives a presentation acknowledgement testLogger.test('Alice waits for acceptance by Faber') diff --git a/packages/openid4vc-client/tests/fixtures.ts b/packages/openid4vc-client/tests/fixtures.ts index 44936b9169..e739859415 100644 --- a/packages/openid4vc-client/tests/fixtures.ts +++ b/packages/openid4vc-client/tests/fixtures.ts @@ -64,7 +64,7 @@ export const getMetadataResponse = { }, } -export const aquireAccessTokenResponse = { +export const acquireAccessTokenResponse = { access_token: '7nikUotMQefxn7oRX56R7MDNE7KJTGfwGjOkHzGaUIG', expires_in: 3600, scope: 'OpenBadgeCredential', diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index fe2d5b1dbc..8b01c14c17 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -1,6 +1,6 @@ import type { KeyDidCreateOptions } from '@aries-framework/core' -import { Agent, KeyType, LogLevel, W3cCredentialRecord, W3cVcModule } from '@aries-framework/core' +import { Agent, KeyType, W3cCredentialRecord, W3cVcModule } from '@aries-framework/core' import nock, { cleanAll, enableNetConnect } from 'nock' import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' @@ -9,9 +9,7 @@ import { getAgentOptions } from '../../core/tests/helpers' import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' -import { TestLogger } from '../../core/tests/logger' - -import { aquireAccessTokenResponse, credentialRequestResponse, getMetadataResponse } from './fixtures' +import { acquireAccessTokenResponse, credentialRequestResponse, getMetadataResponse } from './fixtures' describe('OpenId4VcClient', () => { let agent: Agent<{ @@ -22,9 +20,7 @@ describe('OpenId4VcClient', () => { beforeEach(async () => { const agentOptions = getAgentOptions( 'OpenId4VcClient Agent', - { - logger: new TestLogger(LogLevel.test), - }, + {}, { openId4VcClient: new OpenId4VcClientModule(), w3cVc: new W3cVcModule({ @@ -62,7 +58,7 @@ describe('OpenId4VcClient', () => { .reply(200, getMetadataResponse) // setup access token response - httpMock.post('/oidc/v1/auth/token').reply(200, aquireAccessTokenResponse) + httpMock.post('/oidc/v1/auth/token').reply(200, acquireAccessTokenResponse) // setup credential request response httpMock.post('/oidc/v1/auth/credential').reply(200, credentialRequestResponse) diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index 86fb6dfe83..0f4c07e6da 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -3,16 +3,7 @@ import type { Agent } from '@aries-framework/core' import { sleep } from '../packages/core/src/utils/sleep' import { issueCredential, makeConnection, prepareForIssuance, presentProof } from '../packages/core/tests/helpers' -import { - V1CredentialPreview, - AttributeFilter, - CredentialState, - MediationState, - PredicateType, - ProofAttributeInfo, - ProofPredicateInfo, - ProofState, -} from '@aries-framework/core' +import { V1CredentialPreview, CredentialState, MediationState, ProofState } from '@aries-framework/core' export async function e2eTest({ mediatorAgent, @@ -63,9 +54,9 @@ export async function e2eTest({ // Present Proof from recipient to sender const definitionRestriction = [ - new AttributeFilter({ + { credentialDefinitionId: definition.id, - }), + }, ] const { holderProof, verifierProof } = await presentProof({ verifierAgent: senderAgent, @@ -73,18 +64,18 @@ export async function e2eTest({ verifierConnectionId: senderRecipientConnection.id, presentationTemplate: { attributes: { - name: new ProofAttributeInfo({ + name: { name: 'name', restrictions: definitionRestriction, - }), + }, }, predicates: { - olderThan21: new ProofPredicateInfo({ + olderThan21: { name: 'age', restrictions: definitionRestriction, - predicateType: PredicateType.LessThan, + predicateType: '<=', predicateValue: 20000712, - }), + }, }, }, }) diff --git a/yarn.lock b/yarn.lock index 0f3abfb2f7..0a86c85b57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6103,11 +6103,6 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -husky@^7.0.1: - version "7.0.4" - resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" - integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== - iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" From 231145f173a5c829e6be8e248f77182eafaac701 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 13 Feb 2023 10:22:44 -0300 Subject: [PATCH 037/139] chore: make askar, anoncreds(-rs), indy-vdr packages public (#1292) Signed-off-by: Ariel Gentile --- packages/anoncreds-rs/package.json | 1 - packages/anoncreds/package.json | 1 - packages/askar/package.json | 1 - packages/indy-vdr/package.json | 1 - 4 files changed, 4 deletions(-) diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index d60aa4f4ca..af35fc561c 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -3,7 +3,6 @@ "main": "build/index", "types": "build/index", "version": "0.3.3", - "private": true, "files": [ "build" ], diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index de4e294a54..75c6d2d6a4 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -6,7 +6,6 @@ "files": [ "build" ], - "private": true, "license": "Apache-2.0", "publishConfig": { "access": "public" diff --git a/packages/askar/package.json b/packages/askar/package.json index 5ed1b8b150..1f7935175a 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -3,7 +3,6 @@ "main": "build/index", "types": "build/index", "version": "0.3.3", - "private": true, "files": [ "build" ], diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index e73cfd7a83..418d681c36 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -3,7 +3,6 @@ "main": "build/index", "types": "build/index", "version": "0.3.3", - "private": true, "files": [ "build" ], From dfb3eafbe6a226becb0dc908206f227f45e22308 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Mon, 13 Feb 2023 16:23:41 +0100 Subject: [PATCH 038/139] build(indy-sdk): set private to false (#1293) Signed-off-by: Karim Stekelenburg --- packages/indy-sdk/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index e29cfe6020..d996bcd771 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -3,7 +3,6 @@ "main": "build/index", "types": "build/index", "version": "0.3.3", - "private": true, "files": [ "build" ], From c72fd7416f2c1bc0497a84036e16adfa80585e49 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 13 Feb 2023 18:40:44 +0100 Subject: [PATCH 039/139] feat(anoncreds): legacy indy proof format service (#1283) Signed-off-by: Timo Glastra --- .../src/services/AnonCredsRsHolderService.ts | 20 +- .../services/AnonCredsRsVerifierService.ts | 9 +- .../AnonCredsRsHolderService.test.ts | 10 +- .../__tests__/AnonCredsRsServices.test.ts | 17 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 89 +- packages/anoncreds/package.json | 4 +- .../src/formats/AnonCredsCredentialFormat.ts | 30 +- .../src/formats/AnonCredsProofFormat.ts | 89 ++ .../src/formats/LegacyIndyCredentialFormat.ts | 33 +- .../LegacyIndyCredentialFormatService.ts | 50 +- .../src/formats/LegacyIndyProofFormat.ts | 38 + .../formats/LegacyIndyProofFormatService.ts | 802 ++++++++++++++++++ ...ts => legacy-indy-format-services.test.ts} | 80 +- packages/anoncreds/src/formats/index.ts | 7 + packages/anoncreds/src/index.ts | 5 +- .../src/models/AnonCredsCredentialProposal.ts | 111 +++ .../src/models/AnonCredsProofRequest.ts | 83 ++ .../src/models/AnonCredsRequestedAttribute.ts | 39 + .../src/models/AnonCredsRequestedPredicate.ts | 53 ++ .../src/models/AnonCredsRestriction.ts | 139 +++ .../src/models/AnonCredsRevocationInterval.ts | 18 + .../__tests__/AnonCredsRestriction.test.ts | 80 ++ packages/anoncreds/src/models/exchange.ts | 42 +- packages/anoncreds/src/models/internal.ts | 10 +- .../services/AnonCredsHolderServiceOptions.ts | 6 +- .../src/services/AnonCredsVerifierService.ts | 3 +- .../AnonCredsVerifierServiceOptions.ts | 2 +- .../utils/__tests__/areRequestsEqual.test.ts | 419 +++++++++ .../src/utils/__tests__/credential.test.ts | 8 +- .../__tests__/hasDuplicateGroupNames.test.ts | 70 ++ .../__tests__/revocationInterval.test.ts | 37 + .../sortRequestedCredentialsMatches.test.ts | 57 ++ .../anoncreds/src/utils/areRequestsEqual.ts | 156 ++++ .../src/utils/createRequestFromPreview.ts | 89 ++ packages/anoncreds/src/utils/credential.ts | 17 +- .../src/utils/hasDuplicateGroupNames.ts | 23 + packages/anoncreds/src/utils/index.ts | 8 + packages/anoncreds/src/utils/isMap.ts | 19 + .../anoncreds/src/utils/revocationInterval.ts | 17 + .../utils/sortRequestedCredentialsMatches.ts | 33 + packages/anoncreds/src/utils/tails.ts | 57 ++ packages/askar/src/utils/askarWalletConfig.ts | 4 +- packages/core/src/index.ts | 2 +- .../formats/CredentialFormatServiceOptions.ts | 6 +- .../models/CredentialPreviewAttribute.ts | 2 +- .../protocol/v1/V1CredentialProtocol.ts | 10 +- .../v1/messages/V1CredentialPreview.ts | 2 +- .../v2/messages/V2CredentialPreview.ts | 2 +- .../core/src/modules/proofs/models/index.ts | 1 + packages/core/src/storage/FileSystem.ts | 8 +- .../services/IndySdkHolderService.ts | 28 +- .../services/IndySdkRevocationService.ts | 16 +- .../services/IndySdkVerifierService.ts | 11 +- packages/node/package.json | 1 + packages/node/src/NodeFileSystem.ts | 26 +- packages/node/tests/NodeFileSystem.test.ts | 29 + packages/node/tests/__fixtures__/tailsFile | Bin 0 -> 65666 bytes .../react-native/src/ReactNativeFileSystem.ts | 22 +- 58 files changed, 2854 insertions(+), 195 deletions(-) create mode 100644 packages/anoncreds/src/formats/AnonCredsProofFormat.ts create mode 100644 packages/anoncreds/src/formats/LegacyIndyProofFormat.ts create mode 100644 packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts rename packages/anoncreds/src/formats/__tests__/{LegacyIndyCredentialFormatService.test.ts => legacy-indy-format-services.test.ts} (72%) create mode 100644 packages/anoncreds/src/formats/index.ts create mode 100644 packages/anoncreds/src/models/AnonCredsCredentialProposal.ts create mode 100644 packages/anoncreds/src/models/AnonCredsProofRequest.ts create mode 100644 packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts create mode 100644 packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts create mode 100644 packages/anoncreds/src/models/AnonCredsRestriction.ts create mode 100644 packages/anoncreds/src/models/AnonCredsRevocationInterval.ts create mode 100644 packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts create mode 100644 packages/anoncreds/src/utils/__tests__/areRequestsEqual.test.ts create mode 100644 packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts create mode 100644 packages/anoncreds/src/utils/__tests__/revocationInterval.test.ts create mode 100644 packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts create mode 100644 packages/anoncreds/src/utils/areRequestsEqual.ts create mode 100644 packages/anoncreds/src/utils/createRequestFromPreview.ts create mode 100644 packages/anoncreds/src/utils/hasDuplicateGroupNames.ts create mode 100644 packages/anoncreds/src/utils/index.ts create mode 100644 packages/anoncreds/src/utils/isMap.ts create mode 100644 packages/anoncreds/src/utils/revocationInterval.ts create mode 100644 packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts create mode 100644 packages/anoncreds/src/utils/tails.ts create mode 100644 packages/node/tests/__fixtures__/tailsFile diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index e0c84fd7b1..5f6530c58a 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -12,9 +12,9 @@ import type { CreateLinkSecretOptions, CreateLinkSecretReturn, AnonCredsProofRequestRestriction, - AnonCredsRequestedAttribute, - AnonCredsRequestedPredicate, AnonCredsCredential, + AnonCredsRequestedAttributeMatch, + AnonCredsRequestedPredicateMatch, } from '@aries-framework/anoncreds' import type { AgentContext, Query, SimpleQuery } from '@aries-framework/core' import type { CredentialEntry, CredentialProve } from '@hyperledger/anoncreds-shared' @@ -63,7 +63,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { } public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise { - const { credentialDefinitions, proofRequest, requestedCredentials, schemas } = options + const { credentialDefinitions, proofRequest, selectedCredentials, schemas } = options try { const rsCredentialDefinitions: Record = {} @@ -82,7 +82,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const retrievedCredentials = new Map() const credentialEntryFromAttribute = async ( - attribute: AnonCredsRequestedAttribute | AnonCredsRequestedPredicate + attribute: AnonCredsRequestedAttributeMatch | AnonCredsRequestedPredicateMatch ): Promise<{ linkSecretId: string; credentialEntry: CredentialEntry }> => { let credentialRecord = retrievedCredentials.get(attribute.credentialId) if (!credentialRecord) { @@ -136,15 +136,15 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const credentials: { linkSecretId: string; credentialEntry: CredentialEntry }[] = [] let entryIndex = 0 - for (const referent in requestedCredentials.requestedAttributes) { - const attribute = requestedCredentials.requestedAttributes[referent] + for (const referent in selectedCredentials.attributes) { + const attribute = selectedCredentials.attributes[referent] credentials.push(await credentialEntryFromAttribute(attribute)) credentialsProve.push({ entryIndex, isPredicate: false, referent, reveal: attribute.revealed }) entryIndex = entryIndex + 1 } - for (const referent in requestedCredentials.requestedPredicates) { - const predicate = requestedCredentials.requestedPredicates[referent] + for (const referent in selectedCredentials.predicates) { + const predicate = selectedCredentials.predicates[referent] credentials.push(await credentialEntryFromAttribute(predicate)) credentialsProve.push({ entryIndex, isPredicate: true, referent, reveal: true }) entryIndex = entryIndex + 1 @@ -170,7 +170,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { presentationRequest: PresentationRequest.load(JSON.stringify(proofRequest)), credentials: credentials.map((entry) => entry.credentialEntry), credentialsProve, - selfAttest: requestedCredentials.selfAttestedAttributes, + selfAttest: selectedCredentials.selfAttestedAttributes, masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), }) @@ -179,7 +179,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { agentContext.config.logger.error(`Error creating AnonCreds Proof`, { error, proofRequest, - requestedCredentials, + selectedCredentials, }) throw new AnonCredsRsError(`Error creating proof: ${error}`, { cause: error }) } diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts index 96030d44ba..be4952d632 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts @@ -1,4 +1,5 @@ import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' import { injectable } from '@aries-framework/core' import { @@ -14,8 +15,8 @@ import { AnonCredsRsError } from '../errors/AnonCredsRsError' @injectable() export class AnonCredsRsVerifierService implements AnonCredsVerifierService { - public async verifyProof(options: VerifyProofOptions): Promise { - const { credentialDefinitions, proof, proofRequest, revocationStates, schemas } = options + public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise { + const { credentialDefinitions, proof, proofRequest, revocationRegistries, schemas } = options try { const presentation = Presentation.load(JSON.stringify(proof)) @@ -33,8 +34,8 @@ export class AnonCredsRsVerifierService implements AnonCredsVerifierService { const revocationRegistryDefinitions: Record = {} const lists = [] - for (const revocationRegistryDefinitionId in revocationStates) { - const { definition, revocationStatusLists } = options.revocationStates[revocationRegistryDefinitionId] + for (const revocationRegistryDefinitionId in revocationRegistries) { + const { definition, revocationStatusLists } = options.revocationRegistries[revocationRegistryDefinitionId] revocationRegistryDefinitions[revocationRegistryDefinitionId] = RevocationRegistryDefinition.load( JSON.stringify(definition) diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index f0585f6ffb..bdfac8c48a 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -1,10 +1,10 @@ import type { AnonCredsCredentialDefinition, AnonCredsProofRequest, - AnonCredsRequestedCredentials, AnonCredsRevocationStatusList, AnonCredsCredential, AnonCredsSchema, + AnonCredsSelectedCredentials, } from '@aries-framework/anoncreds' import { @@ -191,15 +191,15 @@ describe('AnonCredsRsHolderService', () => { revocationRegistryDefinitionId: 'phonerevregid:uri', }) - const requestedCredentials: AnonCredsRequestedCredentials = { + const selectedCredentials: AnonCredsSelectedCredentials = { selfAttestedAttributes: { attr5_referent: 'football' }, - requestedAttributes: { + attributes: { attr1_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, attr2_referent: { credentialId: 'phoneCredId', credentialInfo: phoneCredentialInfo, revealed: true }, attr3_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, attr4_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true }, }, - requestedPredicates: { + predicates: { predicate1_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo }, }, } @@ -246,7 +246,7 @@ describe('AnonCredsRsHolderService', () => { 'phonecreddef:uri': phoneCredentialDefinition as AnonCredsCredentialDefinition, }, proofRequest, - requestedCredentials, + selectedCredentials, schemas: { 'phoneschema:uri': { attrNames: ['phoneNumber'], issuerId: 'issuer:uri', name: 'phoneschema', version: '1' }, 'personschema:uri': { diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index 3e23f27eb0..f881d22fa3 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -20,7 +20,7 @@ import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' -import { encode } from '../../../../anoncreds/src/utils/credential' +import { encodeCredentialValue } from '../../../../anoncreds/src/utils/credential' import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { AnonCredsRsHolderService } from '../AnonCredsRsHolderService' @@ -145,7 +145,10 @@ describe('AnonCredsRsServices', () => { const { credential } = await anonCredsIssuerService.createCredential(agentContext, { credentialOffer, credentialRequest: credentialRequestState.credentialRequest, - credentialValues: { name: { raw: 'John', encoded: encode('John') }, age: { raw: '25', encoded: encode('25') } }, + credentialValues: { + name: { raw: 'John', encoded: encodeCredentialValue('John') }, + age: { raw: '25', encoded: encodeCredentialValue('25') }, + }, }) const credentialId = 'holderCredentialId' @@ -197,12 +200,12 @@ describe('AnonCredsRsServices', () => { const proof = await anonCredsHolderService.createProof(agentContext, { credentialDefinitions: { [credentialDefinitionState.credentialDefinitionId]: credentialDefinition }, proofRequest, - requestedCredentials: { - requestedAttributes: { + selectedCredentials: { + attributes: { attr1_referent: { credentialId, credentialInfo, revealed: true }, attr2_referent: { credentialId, credentialInfo, revealed: true }, }, - requestedPredicates: { + predicates: { predicate1_referent: { credentialId, credentialInfo }, }, selfAttestedAttributes: {}, @@ -211,12 +214,12 @@ describe('AnonCredsRsServices', () => { revocationRegistries: {}, }) - const verifiedProof = await anonCredsVerifierService.verifyProof({ + const verifiedProof = await anonCredsVerifierService.verifyProof(agentContext, { credentialDefinitions: { [credentialDefinitionState.credentialDefinitionId]: credentialDefinition }, proof, proofRequest, schemas: { [schemaState.schemaId]: schema }, - revocationStates: {}, + revocationRegistries: {}, }) expect(verifiedProof).toBeTruthy() diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index fc2ce9ec87..b201b8c31e 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -1,10 +1,11 @@ +import type { Wallet } from '@aries-framework/core' + import { AnonCredsModuleConfig, LegacyIndyCredentialFormatService, AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, AnonCredsVerifierServiceSymbol, - AnonCredsRegistryService, AnonCredsSchemaRecord, AnonCredsSchemaRepository, AnonCredsCredentialDefinitionRepository, @@ -15,16 +16,20 @@ import { AnonCredsKeyCorrectnessProofRecord, AnonCredsLinkSecretRepository, AnonCredsLinkSecretRecord, + LegacyIndyProofFormatService, } from '@aries-framework/anoncreds' import { CredentialState, CredentialExchangeRecord, CredentialPreviewAttribute, InjectionSymbols, + ProofState, + ProofExchangeRecord, } from '@aries-framework/core' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' +import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderService' @@ -36,11 +41,13 @@ const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], }) -const agentConfig = getAgentConfig('LegacyIndyCredentialFormatService') +const agentConfig = getAgentConfig('LegacyIndyCredentialFormatService using anoncreds-rs') const anonCredsVerifierService = new AnonCredsRsVerifierService() const anonCredsHolderService = new AnonCredsRsHolderService() const anonCredsIssuerService = new AnonCredsRsIssuerService() +const wallet = { generateNonce: () => Promise.resolve('947121108704767252195123') } as Wallet + const inMemoryStorageService = new InMemoryStorageService() const agentContext = getAgentContext({ registerInstances: [ @@ -54,15 +61,17 @@ const agentContext = getAgentContext({ [AnonCredsModuleConfig, anonCredsModuleConfig], ], agentConfig, + wallet, }) const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() +const legacyIndyProofFormatService = new LegacyIndyProofFormatService() -describe('LegacyIndyCredentialFormatService using anoncreds-rs', () => { - test('issuance flow starting from proposal without negotiation and without revocation', async () => { - // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) - const indyDid = 'TL1EaPFCZ8Si5aUrqScBDt' +// This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) +const indyDid = 'TL1EaPFCZ8Si5aUrqScBDt' +describe('Legacy indy format services using anoncreds-rs', () => { + test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { const schema = await anonCredsIssuerService.createSchema(agentContext, { attrNames: ['name', 'age'], issuerId: indyDid, @@ -70,7 +79,7 @@ describe('LegacyIndyCredentialFormatService using anoncreds-rs', () => { version: '1.0.0', }) - const { schemaState, schemaMetadata } = await registry.registerSchema(agentContext, { + const { schemaState } = await registry.registerSchema(agentContext, { schema, options: {}, }) @@ -273,5 +282,71 @@ describe('LegacyIndyCredentialFormatService using anoncreds-rs', () => { credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }, }) + + const holderProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalSent, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + const verifierProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalReceived, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + + const { attachment: proofProposalAttachment } = await legacyIndyProofFormatService.createProposal(agentContext, { + proofFormats: { + indy: { + attributes: [ + { + name: 'name', + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + value: 'John', + referent: '1', + }, + ], + predicates: [ + { + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], + name: 'Proof Request', + version: '1.0', + }, + }, + proofRecord: holderProofRecord, + }) + + await legacyIndyProofFormatService.processProposal(agentContext, { + attachment: proofProposalAttachment, + proofRecord: verifierProofRecord, + }) + + const { attachment: proofRequestAttachment } = await legacyIndyProofFormatService.acceptProposal(agentContext, { + proofRecord: verifierProofRecord, + proposalAttachment: proofProposalAttachment, + }) + + await legacyIndyProofFormatService.processRequest(agentContext, { + attachment: proofRequestAttachment, + proofRecord: holderProofRecord, + }) + + const { attachment: proofAttachment } = await legacyIndyProofFormatService.acceptRequest(agentContext, { + proofRecord: holderProofRecord, + requestAttachment: proofRequestAttachment, + proposalAttachment: proofProposalAttachment, + }) + + const isValid = await legacyIndyProofFormatService.processPresentation(agentContext, { + attachment: proofAttachment, + proofRecord: verifierProofRecord, + requestAttachment: proofRequestAttachment, + }) + + expect(isValid).toBe(true) }) }) diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 75c6d2d6a4..27ddffa7d6 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -26,7 +26,9 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/node": "0.3.3", - "bn.js": "^5.2.1" + "bn.js": "^5.2.1", + "class-transformer": "0.5.1", + "class-validator": "0.13.1" }, "devDependencies": { "indy-sdk": "^1.16.0-dev-1636", diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts index e08109f56f..dba5361a41 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts @@ -1,6 +1,21 @@ import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models' import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachment } from '@aries-framework/core' +export interface AnonCredsCredentialProposalFormat { + schema_issuer_id?: string + schema_name?: string + schema_version?: string + schema_id?: string + + cred_def_id?: string + issuer_id?: string + + // TODO: we don't necessarily need to include these in the AnonCreds Format RFC + // as it's a new one and we can just forbid the use of legacy properties + schema_issuer_did?: string + issuer_did?: string +} + /** * This defines the module payload for calling CredentialsApi.createProposal * or CredentialsApi.negotiateOffer @@ -70,20 +85,7 @@ export interface AnonCredsCredentialFormat extends CredentialFormat { // Format data is based on RFC 0592 // https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments formatData: { - proposal: { - schema_issuer_id?: string - schema_name?: string - schema_version?: string - schema_id?: string - - cred_def_id?: string - issuer_id?: string - - // TODO: we don't necessarily need to include these in the AnonCreds Format RFC - // as it's a new one and we can just forbid the use of legacy properties - schema_issuer_did?: string - issuer_did?: string - } + proposal: AnonCredsCredentialProposalFormat offer: AnonCredsCredentialOffer request: AnonCredsCredentialRequest credential: AnonCredsCredential diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormat.ts b/packages/anoncreds/src/formats/AnonCredsProofFormat.ts new file mode 100644 index 0000000000..2bfeb689dc --- /dev/null +++ b/packages/anoncreds/src/formats/AnonCredsProofFormat.ts @@ -0,0 +1,89 @@ +import type { + AnonCredsNonRevokedInterval, + AnonCredsPredicateType, + AnonCredsProof, + AnonCredsProofRequest, + AnonCredsRequestedAttribute, + AnonCredsRequestedAttributeMatch, + AnonCredsRequestedPredicate, + AnonCredsRequestedPredicateMatch, + AnonCredsSelectedCredentials, +} from '../models' +import type { ProofFormat } from '@aries-framework/core' + +export interface AnonCredsPresentationPreviewAttribute { + name: string + credentialDefinitionId?: string + mimeType?: string + value?: string + referent?: string +} + +export interface AnonCredsPresentationPreviewPredicate { + name: string + credentialDefinitionId: string + predicate: AnonCredsPredicateType + threshold: number +} + +/** + * Interface for creating an anoncreds proof proposal. + */ +export interface AnonCredsProposeProofFormat { + name?: string + version?: string + attributes?: AnonCredsPresentationPreviewAttribute[] + predicates?: AnonCredsPresentationPreviewPredicate[] +} + +/** + * Interface for creating an anoncreds proof request. + */ +export interface AnonCredsRequestProofFormat { + name: string + version: string + nonRevoked?: AnonCredsNonRevokedInterval + requestedAttributes?: Record + requestedPredicates?: Record +} + +/** + * Interface for getting credentials for an indy proof request. + */ +export interface AnonCredsCredentialsForProofRequest { + attributes: Record + predicates: Record +} + +export interface AnonCredsGetCredentialsForProofRequestOptions { + filterByNonRevocationRequirements?: boolean +} + +export interface AnonCredsProofFormat extends ProofFormat { + formatKey: 'anoncreds' + + proofFormats: { + createProposal: AnonCredsProposeProofFormat + acceptProposal: { + name?: string + version?: string + } + createRequest: AnonCredsRequestProofFormat + acceptRequest: AnonCredsSelectedCredentials + + getCredentialsForRequest: { + input: AnonCredsGetCredentialsForProofRequestOptions + output: AnonCredsCredentialsForProofRequest + } + selectCredentialsForRequest: { + input: AnonCredsGetCredentialsForProofRequestOptions + output: AnonCredsSelectedCredentials + } + } + + formatData: { + proposal: AnonCredsProofRequest + request: AnonCredsProofRequest + presentation: AnonCredsProof + } +} diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts index ce9be1e3eb..78342fe833 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts @@ -2,10 +2,18 @@ import type { AnonCredsAcceptOfferFormat, AnonCredsAcceptProposalFormat, AnonCredsAcceptRequestFormat, + AnonCredsCredentialProposalFormat, AnonCredsOfferCredentialFormat, + AnonCredsProposeCredentialFormat, } from './AnonCredsCredentialFormat' import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models' -import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachment } from '@aries-framework/core' +import type { CredentialFormat } from '@aries-framework/core' + +// Legacy indy credential proposal doesn't support _id properties +export type LegacyIndyCredentialProposalFormat = Omit< + AnonCredsCredentialProposalFormat, + 'schema_issuer_id' | 'issuer_id' +> /** * This defines the module payload for calling CredentialsApi.createProposal @@ -13,18 +21,7 @@ import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachm * * NOTE: This doesn't include the `issuerId` and `schemaIssuerId` properties that are present in the newer format. */ -export interface LegacyIndyProposeCredentialFormat { - schemaIssuerDid?: string - schemaId?: string - schemaName?: string - schemaVersion?: string - - credentialDefinitionId?: string - issuerDid?: string - - attributes?: CredentialPreviewAttributeOptions[] - linkedAttachments?: LinkedAttachment[] -} +type LegacyIndyProposeCredentialFormat = Omit export interface LegacyIndyCredentialRequest extends AnonCredsCredentialRequest { // prover_did is optional in AnonCreds credential request, but required in legacy format @@ -51,15 +48,7 @@ export interface LegacyIndyCredentialFormat extends CredentialFormat { // Format data is based on RFC 0592 // https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments formatData: { - proposal: { - schema_name?: string - schema_issuer_did?: string - schema_version?: string - schema_id?: string - - cred_def_id?: string - issuer_did?: string - } + proposal: LegacyIndyCredentialProposalFormat offer: AnonCredsCredentialOffer request: LegacyIndyCredentialRequest credential: AnonCredsCredential diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 7b2dbf3b72..93e2151870 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -1,4 +1,4 @@ -import type { LegacyIndyCredentialFormat } from './LegacyIndyCredentialFormat' +import type { LegacyIndyCredentialFormat, LegacyIndyCredentialProposalFormat } from './LegacyIndyCredentialFormat' import type { AnonCredsCredential, AnonCredsCredentialOffer, @@ -30,21 +30,19 @@ import type { } from '@aries-framework/core' import { + MessageValidator, CredentialFormatSpec, AriesFrameworkError, - IndyCredPropose, - JsonTransformer, Attachment, - CredentialPreviewAttribute, - AttachmentData, JsonEncoder, utils, - MessageValidator, CredentialProblemReportError, CredentialProblemReportReason, + JsonTransformer, } from '@aries-framework/core' import { AnonCredsError } from '../error' +import { AnonCredsCredentialProposal } from '../models/AnonCredsCredentialProposal' import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' import { @@ -96,8 +94,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic // The easiest way is to destructure and use the spread operator. But that leaves the other properties unused // eslint-disable-next-line @typescript-eslint/no-unused-vars const { attributes, linkedAttachments, ...indyCredentialProposal } = indyFormat - - const proposal = new IndyCredPropose(indyCredentialProposal) + const proposal = new AnonCredsCredentialProposal(indyCredentialProposal) try { MessageValidator.validateSync(proposal) @@ -105,8 +102,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic throw new AriesFrameworkError(`Invalid proposal supplied: ${indyCredentialProposal} in Indy Format Service`) } - const proposalJson = JsonTransformer.toJSON(proposal) - const attachment = this.getFormatData(proposalJson, format.attachmentId) + const attachment = this.getFormatData(JsonTransformer.toJSON(proposal), format.attachmentId) const { previewAttributes } = this.getCredentialLinkedAttachments( indyFormat.attributes, @@ -128,8 +124,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic ): Promise { const proposalJson = attachment.getDataAsJson() - // fromJSON also validates - JsonTransformer.fromJSON(proposalJson, IndyCredPropose) + JsonTransformer.fromJSON(proposalJson, AnonCredsCredentialProposal) } public async acceptProposal( @@ -143,9 +138,8 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic ): Promise { const indyFormat = credentialFormats?.indy - const credentialProposal = JsonTransformer.fromJSON(proposalAttachment.getDataAsJson(), IndyCredPropose) - - const credentialDefinitionId = indyFormat?.credentialDefinitionId ?? credentialProposal.credentialDefinitionId + const proposalJson = proposalAttachment.getDataAsJson() + const credentialDefinitionId = indyFormat?.credentialDefinitionId ?? proposalJson.cred_def_id const attributes = indyFormat?.attributes ?? credentialRecord.credentialAttributes @@ -463,30 +457,26 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic agentContext: AgentContext, { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondProposalOptions ) { - const credentialProposalJson = proposalAttachment.getDataAsJson() - const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) - - const credentialOfferJson = offerAttachment.getDataAsJson() + const proposalJson = proposalAttachment.getDataAsJson() + const offerJson = offerAttachment.getDataAsJson() // We want to make sure the credential definition matches. // TODO: If no credential definition is present on the proposal, we could check whether the other fields // of the proposal match with the credential definition id. - return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id + return proposalJson.cred_def_id === offerJson.cred_def_id } public async shouldAutoRespondToOffer( agentContext: AgentContext, { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondOfferOptions ) { - const credentialProposalJson = proposalAttachment.getDataAsJson() - const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) - - const credentialOfferJson = offerAttachment.getDataAsJson() + const proposalJson = proposalAttachment.getDataAsJson() + const offerJson = offerAttachment.getDataAsJson() // We want to make sure the credential definition matches. // TODO: If no credential definition is present on the proposal, we could check whether the other fields // of the proposal match with the credential definition id. - return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id + return proposalJson.cred_def_id === offerJson.cred_def_id } public async shouldAutoRespondToRequest( @@ -566,7 +556,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic private async assertPreviewAttributesMatchSchemaAttributes( agentContext: AgentContext, offer: AnonCredsCredentialOffer, - attributes: CredentialPreviewAttribute[] + attributes: CredentialPreviewAttributeOptions[] ): Promise { const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) const registry = registryService.getRegistryForIdentifier(agentContext, offer.schema_id) @@ -594,13 +584,13 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic linkedAttachments?: LinkedAttachment[] ): { attachments?: Attachment[] - previewAttributes?: CredentialPreviewAttribute[] + previewAttributes?: CredentialPreviewAttributeOptions[] } { if (!linkedAttachments && !attributes) { return {} } - let previewAttributes = attributes?.map((attribute) => new CredentialPreviewAttribute(attribute)) ?? [] + let previewAttributes = attributes ?? [] let attachments: Attachment[] | undefined if (linkedAttachments) { @@ -624,9 +614,9 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const attachment = new Attachment({ id, mimeType: 'application/json', - data: new AttachmentData({ + data: { base64: JsonEncoder.toBase64(data), - }), + }, }) return attachment diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts new file mode 100644 index 0000000000..c2dfc2cf0d --- /dev/null +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts @@ -0,0 +1,38 @@ +import type { + AnonCredsProposeProofFormat, + AnonCredsRequestProofFormat, + AnonCredsGetCredentialsForProofRequestOptions, + AnonCredsCredentialsForProofRequest, +} from './AnonCredsProofFormat' +import type { AnonCredsProof, AnonCredsProofRequest, AnonCredsSelectedCredentials } from '../models' +import type { ProofFormat } from '@aries-framework/core' + +export interface LegacyIndyProofFormat extends ProofFormat { + formatKey: 'indy' + + proofFormats: { + createProposal: AnonCredsProposeProofFormat + acceptProposal: { + name?: string + version?: string + } + createRequest: AnonCredsRequestProofFormat + acceptRequest: AnonCredsSelectedCredentials + + getCredentialsForRequest: { + input: AnonCredsGetCredentialsForProofRequestOptions + output: AnonCredsCredentialsForProofRequest + } + selectCredentialsForRequest: { + input: AnonCredsGetCredentialsForProofRequestOptions + output: AnonCredsSelectedCredentials + } + } + + formatData: { + // TODO: Custom restrictions to remove `_id` from restrictions? + proposal: AnonCredsProofRequest + request: AnonCredsProofRequest + presentation: AnonCredsProof + } +} diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts new file mode 100644 index 0000000000..7cf5b18786 --- /dev/null +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -0,0 +1,802 @@ +import type { + AnonCredsCredentialsForProofRequest, + AnonCredsGetCredentialsForProofRequestOptions, +} from './AnonCredsProofFormat' +import type { LegacyIndyProofFormat } from './LegacyIndyProofFormat' +import type { + AnonCredsCredentialDefinition, + AnonCredsCredentialInfo, + AnonCredsProof, + AnonCredsRequestedAttribute, + AnonCredsRequestedAttributeMatch, + AnonCredsRequestedPredicate, + AnonCredsRequestedPredicateMatch, + AnonCredsSchema, + AnonCredsSelectedCredentials, + AnonCredsProofRequest, +} from '../models' +import type { + AnonCredsHolderService, + AnonCredsVerifierService, + CreateProofOptions, + GetCredentialsForProofRequestReturn, + VerifyProofOptions, +} from '../services' +import type { + ProofFormatService, + AgentContext, + ProofFormatCreateReturn, + FormatCreateRequestOptions, + ProofFormatCreateProposalOptions, + ProofFormatProcessOptions, + ProofFormatAcceptProposalOptions, + ProofFormatAcceptRequestOptions, + ProofFormatProcessPresentationOptions, + ProofFormatGetCredentialsForRequestOptions, + ProofFormatGetCredentialsForRequestReturn, + ProofFormatSelectCredentialsForRequestOptions, + ProofFormatSelectCredentialsForRequestReturn, + ProofFormatAutoRespondProposalOptions, + ProofFormatAutoRespondRequestOptions, + IndyGetCredentialsForProofRequestOptions, +} from '@aries-framework/core' + +import { + AriesFrameworkError, + Attachment, + AttachmentData, + JsonEncoder, + ProofFormatSpec, + JsonTransformer, +} from '@aries-framework/core' + +import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../models/AnonCredsProofRequest' +import { AnonCredsVerifierServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' +import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' +import { + sortRequestedCredentialsMatches, + createRequestFromPreview, + hasDuplicateGroupsNamesInProofRequest, + areAnonCredsProofRequestsEqual, + assertRevocationInterval, + downloadTailsFile, + checkValidCredentialValueEncoding, + encodeCredentialValue, +} from '../utils' + +const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' +const V2_INDY_PRESENTATION_REQUEST = 'hlindy/proof-req@v2.0' +const V2_INDY_PRESENTATION = 'hlindy/proof@v2.0' + +export class LegacyIndyProofFormatService implements ProofFormatService { + public readonly formatKey = 'indy' as const + + public async createProposal( + agentContext: AgentContext, + { attachmentId, proofFormats }: ProofFormatCreateProposalOptions + ): Promise { + const format = new ProofFormatSpec({ + format: V2_INDY_PRESENTATION_PROPOSAL, + attachmentId, + }) + + const indyFormat = proofFormats.indy + if (!indyFormat) { + throw Error('Missing indy format to create proposal attachment format') + } + + const proofRequest = createRequestFromPreview({ + attributes: indyFormat.attributes ?? [], + predicates: indyFormat.predicates ?? [], + name: indyFormat.name ?? 'Proof request', + version: indyFormat.version ?? '1.0', + nonce: await agentContext.wallet.generateNonce(), + }) + const attachment = this.getFormatData(proofRequest, format.attachmentId) + + return { attachment, format } + } + + public async processProposal(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const proposalJson = attachment.getDataAsJson() + + // fromJson also validates + JsonTransformer.fromJSON(proposalJson, AnonCredsProofRequestClass) + + // Assert attribute and predicate (group) names do not match + if (hasDuplicateGroupsNamesInProofRequest(proposalJson)) { + throw new AriesFrameworkError('Attribute and predicate (group) names must be unique in proof request') + } + } + + public async acceptProposal( + agentContext: AgentContext, + { proposalAttachment, attachmentId }: ProofFormatAcceptProposalOptions + ): Promise { + const format = new ProofFormatSpec({ + format: V2_INDY_PRESENTATION_REQUEST, + attachmentId, + }) + + const proposalJson = proposalAttachment.getDataAsJson() + + const request = { + ...proposalJson, + // We never want to reuse the nonce from the proposal, as this will allow replay attacks + nonce: await agentContext.wallet.generateNonce(), + } + + const attachment = this.getFormatData(request, format.attachmentId) + + return { attachment, format } + } + + public async createRequest( + agentContext: AgentContext, + { attachmentId, proofFormats }: FormatCreateRequestOptions + ): Promise { + const format = new ProofFormatSpec({ + format: V2_INDY_PRESENTATION_REQUEST, + attachmentId, + }) + + const indyFormat = proofFormats.indy + if (!indyFormat) { + throw Error('Missing indy format in create request attachment format') + } + + const request = { + name: indyFormat.name, + version: indyFormat.version, + nonce: await agentContext.wallet.generateNonce(), + requested_attributes: indyFormat.requestedAttributes ?? {}, + requested_predicates: indyFormat.requestedPredicates ?? {}, + non_revoked: indyFormat.nonRevoked, + } satisfies AnonCredsProofRequest + + // Validate to make sure user provided correct input + if (hasDuplicateGroupsNamesInProofRequest(request)) { + throw new AriesFrameworkError('Attribute and predicate (group) names must be unique in proof request') + } + + const attachment = this.getFormatData(request, format.attachmentId) + + return { attachment, format } + } + + public async processRequest(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const requestJson = attachment.getDataAsJson() + + // fromJson also validates + JsonTransformer.fromJSON(requestJson, AnonCredsProofRequestClass) + + // Assert attribute and predicate (group) names do not match + if (hasDuplicateGroupsNamesInProofRequest(requestJson)) { + throw new AriesFrameworkError('Attribute and predicate (group) names must be unique in proof request') + } + } + + public async acceptRequest( + agentContext: AgentContext, + { proofFormats, requestAttachment, attachmentId }: ProofFormatAcceptRequestOptions + ): Promise { + const format = new ProofFormatSpec({ + format: V2_INDY_PRESENTATION, + attachmentId, + }) + const requestJson = requestAttachment.getDataAsJson() + + const indyFormat = proofFormats?.indy + + const selectedCredentials = + indyFormat ?? + (await this._selectCredentialsForRequest(agentContext, requestJson, { + filterByNonRevocationRequirements: true, + })) + + const proof = await this.createProof(agentContext, requestJson, selectedCredentials) + const attachment = this.getFormatData(proof, format.attachmentId) + + return { + attachment, + format, + } + } + + public async processPresentation( + agentContext: AgentContext, + { requestAttachment, attachment }: ProofFormatProcessPresentationOptions + ): Promise { + const verifierService = + agentContext.dependencyManager.resolve(AnonCredsVerifierServiceSymbol) + + const proofRequestJson = requestAttachment.getDataAsJson() + + // NOTE: we don't do validation here, as this is handled by the AnonCreds implementation, however + // this can lead to confusing error messages. We should consider doing validation here as well. + // Defining a class-transformer/class-validator class seems a bit overkill, and the usage of interfaces + // for the anoncreds package keeps things simple. Maybe we can try to use something like zod to validate + const proofJson = attachment.getDataAsJson() + + for (const [referent, attribute] of Object.entries(proofJson.requested_proof.revealed_attrs)) { + if (!checkValidCredentialValueEncoding(attribute.raw, attribute.encoded)) { + throw new AriesFrameworkError( + `The encoded value for '${referent}' is invalid. ` + + `Expected '${encodeCredentialValue(attribute.raw)}'. ` + + `Actual '${attribute.encoded}'` + ) + } + } + + for (const [, attributeGroup] of Object.entries(proofJson.requested_proof.revealed_attr_groups ?? {})) { + for (const [attributeName, attribute] of Object.entries(attributeGroup.values)) { + if (!checkValidCredentialValueEncoding(attribute.raw, attribute.encoded)) { + throw new AriesFrameworkError( + `The encoded value for '${attributeName}' is invalid. ` + + `Expected '${encodeCredentialValue(attribute.raw)}'. ` + + `Actual '${attribute.encoded}'` + ) + } + } + } + + // TODO: pre verify proof json + // I'm not 100% sure how much indy does. Also if it checks whether the proof requests matches the proof + // @see https://github.com/hyperledger/aries-cloudagent-python/blob/master/aries_cloudagent/indy/sdk/verifier.py#L79-L164 + + const schemas = await this.getSchemas(agentContext, new Set(proofJson.identifiers.map((i) => i.schema_id))) + const credentialDefinitions = await this.getCredentialDefinitions( + agentContext, + new Set(proofJson.identifiers.map((i) => i.cred_def_id)) + ) + + const revocationRegistries = await this.getRevocationRegistriesForProof(agentContext, proofJson) + + return await verifierService.verifyProof(agentContext, { + proofRequest: proofRequestJson, + proof: proofJson, + schemas, + credentialDefinitions, + revocationRegistries, + }) + } + + public async getCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment, proofFormats }: ProofFormatGetCredentialsForRequestOptions + ): Promise> { + const proofRequestJson = requestAttachment.getDataAsJson() + + // Set default values + const { filterByNonRevocationRequirements = true } = proofFormats?.indy ?? {} + + const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequestJson, { + filterByNonRevocationRequirements, + }) + + return credentialsForRequest + } + + public async selectCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment, proofFormats }: ProofFormatSelectCredentialsForRequestOptions + ): Promise> { + const proofRequestJson = requestAttachment.getDataAsJson() + + // Set default values + const { filterByNonRevocationRequirements = true } = proofFormats?.indy ?? {} + + const selectedCredentials = this._selectCredentialsForRequest(agentContext, proofRequestJson, { + filterByNonRevocationRequirements, + }) + + return selectedCredentials + } + + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + { proposalAttachment, requestAttachment }: ProofFormatAutoRespondProposalOptions + ): Promise { + const proposalJson = proposalAttachment.getDataAsJson() + const requestJson = requestAttachment.getDataAsJson() + + const areRequestsEqual = areAnonCredsProofRequestsEqual(proposalJson, requestJson) + agentContext.config.logger.debug(`AnonCreds request and proposal are are equal: ${areRequestsEqual}`, { + proposalJson, + requestJson, + }) + + return areRequestsEqual + } + + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + { proposalAttachment, requestAttachment }: ProofFormatAutoRespondRequestOptions + ): Promise { + const proposalJson = proposalAttachment.getDataAsJson() + const requestJson = requestAttachment.getDataAsJson() + + return areAnonCredsProofRequestsEqual(proposalJson, requestJson) + } + + public async shouldAutoRespondToPresentation(): Promise { + // The presentation is already verified in processPresentation, so we can just return true here. + // It's only an ack, so it's just that we received the presentation. + return true + } + + public supportsFormat(formatIdentifier: string): boolean { + const supportedFormats = [V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST, V2_INDY_PRESENTATION] + return supportedFormats.includes(formatIdentifier) + } + + private async _getCredentialsForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + options: IndyGetCredentialsForProofRequestOptions + ): Promise { + const credentialsForProofRequest: AnonCredsCredentialsForProofRequest = { + attributes: {}, + predicates: {}, + } + + for (const [referent, requestedAttribute] of Object.entries(proofRequest.requested_attributes)) { + const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) + + credentialsForProofRequest.attributes[referent] = sortRequestedCredentialsMatches( + await Promise.all( + credentials.map(async (credential) => { + const { isRevoked, timestamp } = await this.getRevocationStatus( + agentContext, + proofRequest, + requestedAttribute, + credential.credentialInfo + ) + + return { + credentialId: credential.credentialInfo.credentialId, + revealed: true, + credentialInfo: credential.credentialInfo, + timestamp, + revoked: isRevoked, + } satisfies AnonCredsRequestedAttributeMatch + }) + ) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter( + (r) => !r.revoked + ) + } + } + + for (const [referent, requestedPredicate] of Object.entries(proofRequest.requested_predicates)) { + const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) + + credentialsForProofRequest.predicates[referent] = sortRequestedCredentialsMatches( + await Promise.all( + credentials.map(async (credential) => { + const { isRevoked, timestamp } = await this.getRevocationStatus( + agentContext, + proofRequest, + requestedPredicate, + credential.credentialInfo + ) + + return { + credentialId: credential.credentialInfo.credentialId, + credentialInfo: credential.credentialInfo, + timestamp, + revoked: isRevoked, + } satisfies AnonCredsRequestedPredicateMatch + }) + ) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter( + (r) => !r.revoked + ) + } + } + + return credentialsForProofRequest + } + + private async _selectCredentialsForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + options: AnonCredsGetCredentialsForProofRequestOptions + ): Promise { + const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, options) + + const selectedCredentials: AnonCredsSelectedCredentials = { + attributes: {}, + predicates: {}, + selfAttestedAttributes: {}, + } + + Object.keys(credentialsForRequest.attributes).forEach((attributeName) => { + const attributeArray = credentialsForRequest.attributes[attributeName] + + if (attributeArray.length === 0) { + throw new AriesFrameworkError('Unable to automatically select requested attributes.') + } + + selectedCredentials.attributes[attributeName] = attributeArray[0] + }) + + Object.keys(credentialsForRequest.predicates).forEach((attributeName) => { + if (credentialsForRequest.predicates[attributeName].length === 0) { + throw new AriesFrameworkError('Unable to automatically select requested predicates.') + } else { + selectedCredentials.predicates[attributeName] = credentialsForRequest.predicates[attributeName][0] + } + }) + + return selectedCredentials + } + + private async getCredentialsForProofRequestReferent( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + attributeReferent: string + ): Promise { + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentials = await holderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent, + }) + + return credentials + } + + /** + * Build schemas object needed to create and verify proof objects. + * + * Creates object with `{ schemaId: AnonCredsSchema }` mapping + * + * @param schemaIds List of schema ids + * @returns Object containing schemas for specified schema ids + * + */ + private async getSchemas(agentContext: AgentContext, schemaIds: Set) { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + + const schemas: { [key: string]: AnonCredsSchema } = {} + + for (const schemaId of schemaIds) { + const schemaRegistry = registryService.getRegistryForIdentifier(agentContext, schemaId) + const schemaResult = await schemaRegistry.getSchema(agentContext, schemaId) + + if (!schemaResult.schema) { + throw new AriesFrameworkError(`Schema not found for id ${schemaId}: ${schemaResult.resolutionMetadata.message}`) + } + + schemas[schemaId] = schemaResult.schema + } + + return schemas + } + + /** + * Build credential definitions object needed to create and verify proof objects. + * + * Creates object with `{ credentialDefinitionId: AnonCredsCredentialDefinition }` mapping + * + * @param credentialDefinitionIds List of credential definition ids + * @returns Object containing credential definitions for specified credential definition ids + * + */ + private async getCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + + const credentialDefinitions: { [key: string]: AnonCredsCredentialDefinition } = {} + + for (const credentialDefinitionId of credentialDefinitionIds) { + const credentialDefinitionRegistry = registryService.getRegistryForIdentifier( + agentContext, + credentialDefinitionId + ) + + const credentialDefinitionResult = await credentialDefinitionRegistry.getCredentialDefinition( + agentContext, + credentialDefinitionId + ) + + if (!credentialDefinitionResult.credentialDefinition) { + throw new AriesFrameworkError( + `Credential definition not found for id ${credentialDefinitionId}: ${credentialDefinitionResult.resolutionMetadata.message}` + ) + } + + credentialDefinitions[credentialDefinitionId] = credentialDefinitionResult.credentialDefinition + } + + return credentialDefinitions + } + + private async getRevocationStatus( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + requestedItem: AnonCredsRequestedAttribute | AnonCredsRequestedPredicate, + credentialInfo: AnonCredsCredentialInfo + ) { + const requestNonRevoked = requestedItem.non_revoked ?? proofRequest.non_revoked + const credentialRevocationId = credentialInfo.credentialRevocationId + const revocationRegistryId = credentialInfo.revocationRegistryId + + // If revocation interval is not present or the credential is not revocable then we + // don't need to fetch the revocation status + if (!requestNonRevoked || !credentialRevocationId || !revocationRegistryId) { + return { isRevoked: undefined, timestamp: undefined } + } + + agentContext.config.logger.trace( + `Fetching credential revocation status for credential revocation id '${credentialRevocationId}' with revocation interval with from '${requestNonRevoked.from}' and to '${requestNonRevoked.to}'` + ) + + // Make sure the revocation interval follows best practices from Aries RFC 0441 + assertRevocationInterval(requestNonRevoked) + + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const registry = registryService.getRegistryForIdentifier(agentContext, revocationRegistryId) + + const revocationStatusResult = await registry.getRevocationStatusList( + agentContext, + revocationRegistryId, + requestNonRevoked.to ?? Date.now() + ) + + if (!revocationStatusResult.revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${revocationStatusResult.resolutionMetadata.message}` + ) + } + + // Item is revoked when the value at the index is 1 + const isRevoked = revocationStatusResult.revocationStatusList.revocationList[parseInt(credentialRevocationId)] === 1 + + agentContext.config.logger.trace( + `Credential with credential revocation index '${credentialRevocationId}' is ${ + isRevoked ? '' : 'not ' + }revoked with revocation interval with to '${requestNonRevoked.to}' & from '${requestNonRevoked.from}'` + ) + + return { + isRevoked, + timestamp: revocationStatusResult.revocationStatusList.timestamp, + } + } + + /** + * Create indy proof from a given proof request and requested credential object. + * + * @param proofRequest The proof request to create the proof for + * @param requestedCredentials The requested credentials object specifying which credentials to use for the proof + * @returns indy proof object + */ + private async createProof( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + selectedCredentials: AnonCredsSelectedCredentials + ): Promise { + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentialObjects = await Promise.all( + [...Object.values(selectedCredentials.attributes), ...Object.values(selectedCredentials.predicates)].map( + async (c) => c.credentialInfo ?? holderService.getCredential(agentContext, { credentialId: c.credentialId }) + ) + ) + + const schemas = await this.getSchemas(agentContext, new Set(credentialObjects.map((c) => c.schemaId))) + const credentialDefinitions = await this.getCredentialDefinitions( + agentContext, + new Set(credentialObjects.map((c) => c.credentialDefinitionId)) + ) + + const revocationRegistries = await this.getRevocationRegistriesForRequest( + agentContext, + proofRequest, + selectedCredentials + ) + + return await holderService.createProof(agentContext, { + proofRequest, + selectedCredentials, + schemas, + credentialDefinitions, + revocationRegistries, + }) + } + + private async getRevocationRegistriesForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + selectedCredentials: AnonCredsSelectedCredentials + ) { + const revocationRegistries: CreateProofOptions['revocationRegistries'] = {} + + try { + agentContext.config.logger.debug(`Retrieving revocation registries for proof request`, { + proofRequest, + selectedCredentials, + }) + + const referentCredentials = [] + + // Retrieve information for referents and push to single array + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.attributes)) { + referentCredentials.push({ + referent, + credentialInfo: selectedCredential.credentialInfo, + nonRevoked: proofRequest.requested_attributes[referent].non_revoked ?? proofRequest.non_revoked, + }) + } + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.predicates)) { + referentCredentials.push({ + referent, + credentialInfo: selectedCredential.credentialInfo, + nonRevoked: proofRequest.requested_predicates[referent].non_revoked ?? proofRequest.non_revoked, + }) + } + + for (const { referent, credentialInfo, nonRevoked } of referentCredentials) { + if (!credentialInfo) { + throw new AriesFrameworkError( + `Credential for referent '${referent} does not have credential info for revocation state creation` + ) + } + + // Prefer referent-specific revocation interval over global revocation interval + const credentialRevocationId = credentialInfo.credentialRevocationId + const revocationRegistryId = credentialInfo.revocationRegistryId + + // If revocation interval is present and the credential is revocable then create revocation state + if (nonRevoked && credentialRevocationId && revocationRegistryId) { + agentContext.config.logger.trace( + `Presentation is requesting proof of non revocation for referent '${referent}', creating revocation state for credential`, + { + nonRevoked, + credentialRevocationId, + revocationRegistryId, + } + ) + + // Make sure the revocation interval follows best practices from Aries RFC 0441 + assertRevocationInterval(nonRevoked) + + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, revocationRegistryId) + + // Fetch revocation registry definition if not in revocation registries list yet + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId + ) + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } + + const { tailsLocation, tailsHash } = revocationRegistryDefinition.value + const { tailsFilePath } = await downloadTailsFile(agentContext, tailsLocation, tailsHash) + + // const tails = await this.indyUtilitiesService.downloadTails(tailsHash, tailsLocation) + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + tailsFilePath, + revocationStatusLists: {}, + } + } + + // TODO: can we check if the revocation status list is already fetched? We don't know which timestamp the query will return. This + // should probably be solved using caching + // Fetch the revocation status list + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, nonRevoked.to ?? Date.now()) + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId].revocationStatusLists[revocationStatusList.timestamp] = + revocationStatusList + } + } + + agentContext.config.logger.debug(`Retrieved revocation registries for proof request`, { + revocationRegistries, + }) + + return revocationRegistries + } catch (error) { + agentContext.config.logger.error(`Error retrieving revocation registry for proof request`, { + error, + proofRequest, + selectedCredentials, + }) + + throw error + } + } + + private async getRevocationRegistriesForProof(agentContext: AgentContext, proof: AnonCredsProof) { + const revocationRegistries: VerifyProofOptions['revocationRegistries'] = {} + + for (const identifier of proof.identifiers) { + const revocationRegistryId = identifier.rev_reg_id + const timestamp = identifier.timestamp + + // Skip if no revocation registry id is present + if (!revocationRegistryId || !timestamp) continue + + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, revocationRegistryId) + + // Fetch revocation registry definition if not already fetched + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId + ) + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + revocationStatusLists: {}, + } + } + + // Fetch revocation status list by timestamp if not already fetched + if (!revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp]) { + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestamp) + + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp] = revocationStatusList + } + } + + return revocationRegistries + } + + /** + * Returns an object of type {@link Attachment} for use in credential exchange messages. + * It looks up the correct format identifier and encodes the data as a base64 attachment. + * + * @param data The data to include in the attach object + * @param id the attach id from the formats component of the message + */ + private getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ + id, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(data), + }), + }) + + return attachment + } +} diff --git a/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts similarity index 72% rename from packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts rename to packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 2449c81124..60359bb3ae 100644 --- a/packages/anoncreds/src/formats/__tests__/LegacyIndyCredentialFormatService.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -4,6 +4,8 @@ import { SigningProviderRegistry, KeyType, CredentialPreviewAttribute, + ProofExchangeRecord, + ProofState, } from '@aries-framework/core' import * as indySdk from 'indy-sdk' @@ -25,13 +27,14 @@ import { } from '../../services' import { AnonCredsRegistryService } from '../../services/registry/AnonCredsRegistryService' import { LegacyIndyCredentialFormatService } from '../LegacyIndyCredentialFormatService' +import { LegacyIndyProofFormatService } from '../LegacyIndyProofFormatService' const registry = new InMemoryAnonCredsRegistry() const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], }) -const agentConfig = getAgentConfig('LegacyIndyCredentialFormatServiceTest') +const agentConfig = getAgentConfig('LegacyIndyProofFormatServiceTest') const anonCredsRevocationService = new IndySdkRevocationService(indySdk) const anonCredsVerifierService = new IndySdkVerifierService(indySdk) const anonCredsHolderService = new IndySdkHolderService(anonCredsRevocationService, indySdk) @@ -50,8 +53,11 @@ const agentContext = getAgentContext({ }) const indyCredentialFormatService = new LegacyIndyCredentialFormatService() +const indyProofFormatService = new LegacyIndyProofFormatService() -describe('LegacyIndyCredentialFormatService', () => { +// We can split up these tests when we can use AnonCredsRS as a backend, but currently +// we need to have the link secrets etc in the wallet which is not so easy to do with Indy +describe('Legacy indy format services', () => { beforeEach(async () => { await wallet.createAndOpen(agentConfig.walletConfig) }) @@ -60,8 +66,8 @@ describe('LegacyIndyCredentialFormatService', () => { await wallet.delete() }) - test('issuance flow starting from proposal without negotiation and without revocation', async () => { - // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) + test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { + // This is just so we don't have to register an actual indy did (as we don't have the indy did registrar configured) const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) const indyDid = indyDidFromPublicKeyBase58(key.publicKeyBase58) @@ -220,5 +226,71 @@ describe('LegacyIndyCredentialFormatService', () => { credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }, }) + + const holderProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalSent, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + const verifierProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalReceived, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + + const { attachment: proofProposalAttachment } = await indyProofFormatService.createProposal(agentContext, { + proofFormats: { + indy: { + attributes: [ + { + name: 'name', + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + value: 'John', + referent: '1', + }, + ], + predicates: [ + { + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], + name: 'Proof Request', + version: '1.0', + }, + }, + proofRecord: holderProofRecord, + }) + + await indyProofFormatService.processProposal(agentContext, { + attachment: proofProposalAttachment, + proofRecord: verifierProofRecord, + }) + + const { attachment: proofRequestAttachment } = await indyProofFormatService.acceptProposal(agentContext, { + proofRecord: verifierProofRecord, + proposalAttachment: proofProposalAttachment, + }) + + await indyProofFormatService.processRequest(agentContext, { + attachment: proofRequestAttachment, + proofRecord: holderProofRecord, + }) + + const { attachment: proofAttachment } = await indyProofFormatService.acceptRequest(agentContext, { + proofRecord: holderProofRecord, + requestAttachment: proofRequestAttachment, + proposalAttachment: proofProposalAttachment, + }) + + const isValid = await indyProofFormatService.processPresentation(agentContext, { + attachment: proofAttachment, + proofRecord: verifierProofRecord, + requestAttachment: proofRequestAttachment, + }) + + expect(isValid).toBe(true) }) }) diff --git a/packages/anoncreds/src/formats/index.ts b/packages/anoncreds/src/formats/index.ts new file mode 100644 index 0000000000..25f0a81917 --- /dev/null +++ b/packages/anoncreds/src/formats/index.ts @@ -0,0 +1,7 @@ +export * from './AnonCredsCredentialFormat' +export * from './LegacyIndyCredentialFormat' +export { LegacyIndyCredentialFormatService } from './LegacyIndyCredentialFormatService' + +export * from './AnonCredsProofFormat' +export * from './LegacyIndyProofFormat' +export { LegacyIndyProofFormatService } from './LegacyIndyProofFormatService' diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 9ef264f501..ced98385f2 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -2,8 +2,9 @@ export * from './models' export * from './services' export * from './error' export * from './repository' +export * from './formats' + export { AnonCredsModule } from './AnonCredsModule' export { AnonCredsModuleConfig, AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' export { AnonCredsApi } from './AnonCredsApi' -export { LegacyIndyCredentialFormatService } from './formats/LegacyIndyCredentialFormatService' -export { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' +export { AnonCredsCreateLinkSecretOptions } from './AnonCredsApiOptions' diff --git a/packages/anoncreds/src/models/AnonCredsCredentialProposal.ts b/packages/anoncreds/src/models/AnonCredsCredentialProposal.ts new file mode 100644 index 0000000000..928c26b5d5 --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsCredentialProposal.ts @@ -0,0 +1,111 @@ +import { Expose } from 'class-transformer' +import { IsOptional, IsString } from 'class-validator' + +export interface AnonCredsCredentialProposalOptions { + /** + * @deprecated Use `schemaIssuerId` instead. Only valid for legacy indy identifiers. + */ + schemaIssuerDid?: string + schemaIssuerId?: string + + schemaId?: string + schemaName?: string + schemaVersion?: string + credentialDefinitionId?: string + + /** + * @deprecated Use `issuerId` instead. Only valid for legacy indy identifiers. + */ + issuerDid?: string + issuerId?: string +} + +/** + * Class representing an AnonCreds credential proposal as defined in Aries RFC 0592 (and soon the new AnonCreds RFC) + */ +export class AnonCredsCredentialProposal { + public constructor(options: AnonCredsCredentialProposalOptions) { + if (options) { + this.schemaIssuerDid = options.schemaIssuerDid + this.schemaIssuerId = options.schemaIssuerId + this.schemaId = options.schemaId + this.schemaName = options.schemaName + this.schemaVersion = options.schemaVersion + this.credentialDefinitionId = options.credentialDefinitionId + this.issuerDid = options.issuerDid + this.issuerId = options.issuerId + } + } + + /** + * Filter to request credential based on a particular Schema issuer DID. + * + * May only be used with legacy indy identifiers + * + * @deprecated Use schemaIssuerId instead + */ + @Expose({ name: 'schema_issuer_did' }) + @IsString() + @IsOptional() + public schemaIssuerDid?: string + + /** + * Filter to request credential based on a particular Schema issuer DID. + */ + @Expose({ name: 'schema_issuer_id' }) + @IsString() + @IsOptional() + public schemaIssuerId?: string + + /** + * Filter to request credential based on a particular Schema. + */ + @Expose({ name: 'schema_id' }) + @IsString() + @IsOptional() + public schemaId?: string + + /** + * Filter to request credential based on a schema name. + */ + @Expose({ name: 'schema_name' }) + @IsString() + @IsOptional() + public schemaName?: string + + /** + * Filter to request credential based on a schema version. + */ + @Expose({ name: 'schema_version' }) + @IsString() + @IsOptional() + public schemaVersion?: string + + /** + * Filter to request credential based on a particular Credential Definition. + */ + @Expose({ name: 'cred_def_id' }) + @IsString() + @IsOptional() + public credentialDefinitionId?: string + + /** + * Filter to request a credential issued by the owner of a particular DID. + * + * May only be used with legacy indy identifiers + * + * @deprecated Use issuerId instead + */ + @Expose({ name: 'issuer_did' }) + @IsString() + @IsOptional() + public issuerDid?: string + + /** + * Filter to request a credential issued by the owner of a particular DID. + */ + @Expose({ name: 'issuer_id' }) + @IsString() + @IsOptional() + public issuerId?: string +} diff --git a/packages/anoncreds/src/models/AnonCredsProofRequest.ts b/packages/anoncreds/src/models/AnonCredsProofRequest.ts new file mode 100644 index 0000000000..34abfe3030 --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsProofRequest.ts @@ -0,0 +1,83 @@ +import type { AnonCredsRequestedPredicateOptions } from './AnonCredsRequestedPredicate' + +import { IndyRevocationInterval } from '@aries-framework/core' +import { Expose, Type } from 'class-transformer' +import { IsIn, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { IsMap } from '../utils' + +import { AnonCredsRequestedAttribute } from './AnonCredsRequestedAttribute' +import { AnonCredsRequestedPredicate } from './AnonCredsRequestedPredicate' +import { AnonCredsRevocationInterval } from './AnonCredsRevocationInterval' + +export interface AnonCredsProofRequestOptions { + name: string + version: string + nonce: string + nonRevoked?: AnonCredsRevocationInterval + ver?: '1.0' | '2.0' + requestedAttributes?: Record + requestedPredicates?: Record +} + +/** + * Proof Request for AnonCreds based proof format + */ +export class AnonCredsProofRequest { + public constructor(options: AnonCredsProofRequestOptions) { + if (options) { + this.name = options.name + this.version = options.version + this.nonce = options.nonce + + this.requestedAttributes = new Map( + Object.entries(options.requestedAttributes ?? {}).map(([key, attribute]) => [ + key, + new AnonCredsRequestedAttribute(attribute), + ]) + ) + + this.requestedPredicates = new Map( + Object.entries(options.requestedPredicates ?? {}).map(([key, predicate]) => [ + key, + new AnonCredsRequestedPredicate(predicate), + ]) + ) + + this.nonRevoked = options.nonRevoked ? new AnonCredsRevocationInterval(options.nonRevoked) : undefined + this.ver = options.ver + } + } + + @IsString() + public name!: string + + @IsString() + public version!: string + + @IsString() + public nonce!: string + + @Expose({ name: 'requested_attributes' }) + @IsMap() + @ValidateNested({ each: true }) + @Type(() => AnonCredsRequestedAttribute) + public requestedAttributes!: Map + + @Expose({ name: 'requested_predicates' }) + @IsMap() + @ValidateNested({ each: true }) + @Type(() => AnonCredsRequestedPredicate) + public requestedPredicates!: Map + + @Expose({ name: 'non_revoked' }) + @ValidateNested() + @Type(() => IndyRevocationInterval) + @IsOptional() + @IsInstance(IndyRevocationInterval) + public nonRevoked?: IndyRevocationInterval + + @IsIn(['1.0', '2.0']) + @IsOptional() + public ver?: '1.0' | '2.0' +} diff --git a/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts b/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts new file mode 100644 index 0000000000..806f5f422b --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts @@ -0,0 +1,39 @@ +import { Expose, Type } from 'class-transformer' +import { ArrayNotEmpty, IsArray, IsInstance, IsOptional, IsString, ValidateIf, ValidateNested } from 'class-validator' + +import { AnonCredsRestriction, AnonCredsRestrictionTransformer } from './AnonCredsRestriction' +import { AnonCredsRevocationInterval } from './AnonCredsRevocationInterval' + +export class AnonCredsRequestedAttribute { + public constructor(options: AnonCredsRequestedAttribute) { + if (options) { + this.name = options.name + this.names = options.names + this.nonRevoked = options.nonRevoked ? new AnonCredsRevocationInterval(options.nonRevoked) : undefined + this.restrictions = options.restrictions?.map((r) => new AnonCredsRestriction(r)) + } + } + + @IsString() + @ValidateIf((o: AnonCredsRequestedAttribute) => o.names === undefined) + public name?: string + + @IsArray() + @IsString({ each: true }) + @ValidateIf((o: AnonCredsRequestedAttribute) => o.name === undefined) + @ArrayNotEmpty() + public names?: string[] + + @Expose({ name: 'non_revoked' }) + @ValidateNested() + @IsInstance(AnonCredsRevocationInterval) + @Type(() => AnonCredsRevocationInterval) + @IsOptional() + public nonRevoked?: AnonCredsRevocationInterval + + @ValidateNested({ each: true }) + @Type(() => AnonCredsRestriction) + @IsOptional() + @AnonCredsRestrictionTransformer() + public restrictions?: AnonCredsRestriction[] +} diff --git a/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts b/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts new file mode 100644 index 0000000000..5f9f99ebc0 --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts @@ -0,0 +1,53 @@ +import { Expose, Type } from 'class-transformer' +import { IsArray, IsIn, IsInstance, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' + +import { AnonCredsPredicateType, anonCredsPredicateType } from '../models' + +import { AnonCredsRestriction, AnonCredsRestrictionTransformer } from './AnonCredsRestriction' +import { AnonCredsRevocationInterval } from './AnonCredsRevocationInterval' + +export interface AnonCredsRequestedPredicateOptions { + name: string + // Also allow string value of the enum as input, to make it easier to use in the API + predicateType: AnonCredsPredicateType + predicateValue: number + nonRevoked?: AnonCredsRevocationInterval + restrictions?: AnonCredsRestriction[] +} + +export class AnonCredsRequestedPredicate { + public constructor(options: AnonCredsRequestedPredicateOptions) { + if (options) { + this.name = options.name + this.nonRevoked = options.nonRevoked ? new AnonCredsRevocationInterval(options.nonRevoked) : undefined + this.restrictions = options.restrictions?.map((r) => new AnonCredsRestriction(r)) + this.predicateType = options.predicateType as AnonCredsPredicateType + this.predicateValue = options.predicateValue + } + } + + @IsString() + public name!: string + + @Expose({ name: 'p_type' }) + @IsIn(anonCredsPredicateType) + public predicateType!: AnonCredsPredicateType + + @Expose({ name: 'p_value' }) + @IsInt() + public predicateValue!: number + + @Expose({ name: 'non_revoked' }) + @ValidateNested() + @Type(() => AnonCredsRevocationInterval) + @IsOptional() + @IsInstance(AnonCredsRevocationInterval) + public nonRevoked?: AnonCredsRevocationInterval + + @ValidateNested({ each: true }) + @Type(() => AnonCredsRestriction) + @IsOptional() + @IsArray() + @AnonCredsRestrictionTransformer() + public restrictions?: AnonCredsRestriction[] +} diff --git a/packages/anoncreds/src/models/AnonCredsRestriction.ts b/packages/anoncreds/src/models/AnonCredsRestriction.ts new file mode 100644 index 0000000000..def1fc70a2 --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsRestriction.ts @@ -0,0 +1,139 @@ +import { Exclude, Expose, Transform, TransformationType } from 'class-transformer' +import { IsOptional, IsString } from 'class-validator' + +export class AnonCredsRestriction { + public constructor(options: AnonCredsRestriction) { + if (options) { + this.schemaId = options.schemaId + this.schemaIssuerDid = options.schemaIssuerDid + this.schemaIssuerId = options.schemaIssuerId + this.schemaName = options.schemaName + this.schemaVersion = options.schemaVersion + this.issuerDid = options.issuerDid + this.issuerId = options.issuerId + this.credentialDefinitionId = options.credentialDefinitionId + this.attributeMarkers = options.attributeMarkers + this.attributeValues = options.attributeValues + } + } + + @Expose({ name: 'schema_id' }) + @IsOptional() + @IsString() + public schemaId?: string + + @Expose({ name: 'schema_issuer_did' }) + @IsOptional() + @IsString() + public schemaIssuerDid?: string + + @Expose({ name: 'schema_issuer_id' }) + @IsOptional() + @IsString() + public schemaIssuerId?: string + + @Expose({ name: 'schema_name' }) + @IsOptional() + @IsString() + public schemaName?: string + + @Expose({ name: 'schema_version' }) + @IsOptional() + @IsString() + public schemaVersion?: string + + @Expose({ name: 'issuer_did' }) + @IsOptional() + @IsString() + public issuerDid?: string + + @Expose({ name: 'issuer_id' }) + @IsOptional() + @IsString() + public issuerId?: string + + @Expose({ name: 'cred_def_id' }) + @IsOptional() + @IsString() + public credentialDefinitionId?: string + + @Exclude() + public attributeMarkers: Record = {} + + @Exclude() + public attributeValues: Record = {} +} + +/** + * Decorator that transforms attribute values and attribute markers. + * + * It will transform between the following JSON structure: + * ```json + * { + * "attr::test_prop::value": "test_value" + * "attr::test_prop::marker": "1 + * } + * ``` + * + * And the following AnonCredsRestriction: + * ```json + * { + * "attributeValues": { + * "test_prop": "test_value" + * }, + * "attributeMarkers": { + * "test_prop": true + * } + * } + * ``` + * + * @example + * class Example { + * AttributeFilterTransformer() + * public restrictions!: AnonCredsRestriction[] + * } + */ +export function AnonCredsRestrictionTransformer() { + return Transform(({ value: restrictions, type }) => { + switch (type) { + case TransformationType.CLASS_TO_PLAIN: + if (restrictions && Array.isArray(restrictions)) { + for (const restriction of restrictions) { + const r = restriction as AnonCredsRestriction + + for (const [attributeName, attributeValue] of Object.entries(r.attributeValues)) { + restriction[`attr::${attributeName}::value`] = attributeValue + } + + for (const [attributeName] of Object.entries(r.attributeMarkers)) { + restriction[`attr::${attributeName}::marker`] = '1' + } + } + } + + return restrictions + + case TransformationType.PLAIN_TO_CLASS: + if (restrictions && Array.isArray(restrictions)) { + for (const restriction of restrictions) { + const r = restriction as AnonCredsRestriction + + for (const [attributeName, attributeValue] of Object.entries(r)) { + const match = new RegExp('^attr::([^:]+)::(value|marker)$').exec(attributeName) + + if (match && match[2] === 'marker' && attributeValue === '1') { + r.attributeMarkers[match[1]] = true + delete restriction[attributeName] + } else if (match && match[2] === 'value') { + r.attributeValues[match[1]] = attributeValue + delete restriction[attributeName] + } + } + } + } + return restrictions + default: + return restrictions + } + }) +} diff --git a/packages/anoncreds/src/models/AnonCredsRevocationInterval.ts b/packages/anoncreds/src/models/AnonCredsRevocationInterval.ts new file mode 100644 index 0000000000..0ae0160616 --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsRevocationInterval.ts @@ -0,0 +1,18 @@ +import { IsInt, IsOptional } from 'class-validator' + +export class AnonCredsRevocationInterval { + public constructor(options: AnonCredsRevocationInterval) { + if (options) { + this.from = options.from + this.to = options.to + } + } + + @IsInt() + @IsOptional() + public from?: number + + @IsInt() + @IsOptional() + public to?: number +} diff --git a/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts b/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts new file mode 100644 index 0000000000..a3d02ab549 --- /dev/null +++ b/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts @@ -0,0 +1,80 @@ +import { JsonTransformer } from '@aries-framework/core' +import { Type } from 'class-transformer' +import { IsArray } from 'class-validator' + +import { AnonCredsRestriction, AnonCredsRestrictionTransformer } from '../AnonCredsRestriction' + +// We need to add the transformer class to the wrapper +class Wrapper { + public constructor(options: Wrapper) { + if (options) { + this.restrictions = options.restrictions + } + } + + @Type(() => AnonCredsRestriction) + @IsArray() + @AnonCredsRestrictionTransformer() + public restrictions!: AnonCredsRestriction[] +} + +describe('AnonCredsRestriction', () => { + test('parses attribute values and markers', () => { + const anonCredsRestrictions = JsonTransformer.fromJSON( + { + restrictions: [ + { + 'attr::test_prop::value': 'test_value', + 'attr::test_prop2::value': 'test_value2', + 'attr::test_prop::marker': '1', + 'attr::test_prop2::marker': '1', + }, + ], + }, + Wrapper + ) + + expect(anonCredsRestrictions).toEqual({ + restrictions: [ + { + attributeValues: { + test_prop: 'test_value', + test_prop2: 'test_value2', + }, + attributeMarkers: { + test_prop: true, + test_prop2: true, + }, + }, + ], + }) + }) + + test('transforms attributeValues and attributeMarkers to json', () => { + const restrictions = new Wrapper({ + restrictions: [ + new AnonCredsRestriction({ + attributeMarkers: { + test_prop: true, + test_prop2: true, + }, + attributeValues: { + test_prop: 'test_value', + test_prop2: 'test_value2', + }, + }), + ], + }) + + expect(JsonTransformer.toJSON(restrictions)).toMatchObject({ + restrictions: [ + { + 'attr::test_prop::value': 'test_value', + 'attr::test_prop2::value': 'test_value2', + 'attr::test_prop::marker': '1', + 'attr::test_prop2::marker': '1', + }, + ], + }) + }) +}) diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index b0e960afb8..7ec87b9ec7 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -1,3 +1,6 @@ +export const anonCredsPredicateType = ['>=', '>', '<=', '<'] as const +export type AnonCredsPredicateType = (typeof anonCredsPredicateType)[number] + export interface AnonCredsProofRequestRestriction { schema_id?: string schema_issuer_id?: string @@ -62,7 +65,8 @@ export interface AnonCredsProof { encoded: string } > - revealed_attr_groups: Record< + // revealed_attr_groups is only defined if there's a requested attribute using `names` + revealed_attr_groups?: Record< string, { sub_proof_index: number @@ -93,29 +97,27 @@ export interface AnonCredsProof { }> } +export interface AnonCredsRequestedAttribute { + name?: string + names?: string[] + restrictions?: AnonCredsProofRequestRestriction[] + non_revoked?: AnonCredsNonRevokedInterval +} + +export interface AnonCredsRequestedPredicate { + name: string + p_type: AnonCredsPredicateType + p_value: number + restrictions?: AnonCredsProofRequestRestriction[] + non_revoked?: AnonCredsNonRevokedInterval +} + export interface AnonCredsProofRequest { name: string version: string nonce: string - requested_attributes: Record< - string, - { - name?: string - names?: string[] - restrictions?: AnonCredsProofRequestRestriction[] - non_revoked?: AnonCredsNonRevokedInterval - } - > - requested_predicates: Record< - string, - { - name: string - p_type: '>=' | '>' | '<=' | '<' - p_value: number - restrictions?: AnonCredsProofRequestRestriction[] - non_revoked?: AnonCredsNonRevokedInterval - } - > + requested_attributes: Record + requested_predicates: Record non_revoked?: AnonCredsNonRevokedInterval ver?: '1.0' | '2.0' } diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index 27d476ebb3..39452f736a 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -9,7 +9,7 @@ export interface AnonCredsCredentialInfo { credentialRevocationId?: string | undefined } -export interface AnonCredsRequestedAttribute { +export interface AnonCredsRequestedAttributeMatch { credentialId: string timestamp?: number revealed: boolean @@ -17,16 +17,16 @@ export interface AnonCredsRequestedAttribute { revoked?: boolean } -export interface AnonCredsRequestedPredicate { +export interface AnonCredsRequestedPredicateMatch { credentialId: string timestamp?: number credentialInfo: AnonCredsCredentialInfo revoked?: boolean } -export interface AnonCredsRequestedCredentials { - requestedAttributes?: Record - requestedPredicates?: Record +export interface AnonCredsSelectedCredentials { + attributes: Record + predicates: Record selfAttestedAttributes: Record } diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index 747e3fcfed..6ed4db9f4a 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -1,7 +1,7 @@ import type { AnonCredsCredentialInfo, AnonCredsCredentialRequestMetadata, - AnonCredsRequestedCredentials, + AnonCredsSelectedCredentials, } from '../models' import type { AnonCredsCredential, @@ -24,7 +24,7 @@ export interface AnonCredsAttributeInfo { export interface CreateProofOptions { proofRequest: AnonCredsProofRequest - requestedCredentials: AnonCredsRequestedCredentials + selectedCredentials: AnonCredsSelectedCredentials schemas: { [schemaId: string]: AnonCredsSchema } @@ -37,7 +37,7 @@ export interface CreateProofOptions { tailsFilePath: string definition: AnonCredsRevocationRegistryDefinition revocationStatusLists: { - [timestamp: string]: AnonCredsRevocationStatusList + [timestamp: number]: AnonCredsRevocationStatusList } } } diff --git a/packages/anoncreds/src/services/AnonCredsVerifierService.ts b/packages/anoncreds/src/services/AnonCredsVerifierService.ts index 00e2a5670d..f0ffdf1e91 100644 --- a/packages/anoncreds/src/services/AnonCredsVerifierService.ts +++ b/packages/anoncreds/src/services/AnonCredsVerifierService.ts @@ -1,9 +1,10 @@ import type { VerifyProofOptions } from './AnonCredsVerifierServiceOptions' +import type { AgentContext } from '@aries-framework/core' export const AnonCredsVerifierServiceSymbol = Symbol('AnonCredsVerifierService') export interface AnonCredsVerifierService { // TODO: do we want to extend the return type with more info besides a boolean. // If the value is false it would be nice to have some extra contexts about why it failed - verifyProof(options: VerifyProofOptions): Promise + verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise } diff --git a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts index 85593764af..1bdd959f15 100644 --- a/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsVerifierServiceOptions.ts @@ -15,7 +15,7 @@ export interface VerifyProofOptions { credentialDefinitions: { [credentialDefinitionId: string]: AnonCredsCredentialDefinition } - revocationStates: { + revocationRegistries: { [revocationRegistryDefinitionId: string]: { definition: AnonCredsRevocationRegistryDefinition // NOTE: the verifier only needs the accumulator, not the whole state of the revocation registry diff --git a/packages/anoncreds/src/utils/__tests__/areRequestsEqual.test.ts b/packages/anoncreds/src/utils/__tests__/areRequestsEqual.test.ts new file mode 100644 index 0000000000..51f9c3317e --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/areRequestsEqual.test.ts @@ -0,0 +1,419 @@ +import type { AnonCredsProofRequest } from '../../models' + +import { areAnonCredsProofRequestsEqual } from '../areRequestsEqual' + +const proofRequest = { + name: 'Proof Request', + version: '1.0.0', + nonce: 'nonce', + ver: '1.0', + non_revoked: {}, + requested_attributes: { + a: { + names: ['name1', 'name2'], + restrictions: [ + { + cred_def_id: 'cred_def_id1', + }, + { + schema_id: 'schema_id', + }, + ], + }, + }, + requested_predicates: { + p: { + name: 'Hello', + p_type: '<', + p_value: 10, + restrictions: [ + { + cred_def_id: 'string2', + }, + { + cred_def_id: 'string', + }, + ], + }, + }, +} satisfies AnonCredsProofRequest + +describe('util | areAnonCredsProofRequestsEqual', () => { + test('does not compare name, ver, version and nonce', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + name: 'Proof Request 2', + version: '2.0.0', + nonce: 'nonce2', + ver: '2.0', + }) + ).toBe(true) + }) + + test('check top level non_revocation interval', () => { + // empty object is semantically equal to undefined + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + non_revoked: {}, + }) + ).toBe(true) + + // properties inside object are different + expect( + areAnonCredsProofRequestsEqual( + { + ...proofRequest, + non_revoked: { + to: 5, + }, + }, + { + ...proofRequest, + non_revoked: { + from: 5, + }, + } + ) + ).toBe(false) + + // One has non_revoked, other doesn't + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + non_revoked: { + from: 5, + }, + }) + ).toBe(false) + }) + + test('ignores attribute group name differences', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + b: proofRequest.requested_attributes.a, + }, + }) + ).toBe(true) + }) + + test('ignores attribute restriction order', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: [...proofRequest.requested_attributes.a.restrictions].reverse(), + }, + }, + }) + ).toBe(true) + }) + + test('ignores attribute restriction undefined vs empty array', () => { + expect( + areAnonCredsProofRequestsEqual( + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: undefined, + }, + }, + }, + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: [], + }, + }, + } + ) + ).toBe(true) + }) + + test('ignores attribute names order', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + names: ['name2', 'name1'], + }, + }, + }) + ).toBe(true) + }) + + test('checks attribute non_revocation interval', () => { + // empty object is semantically equal to undefined + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: {}, + }, + }, + }) + ).toBe(true) + + // properties inside object are different + expect( + areAnonCredsProofRequestsEqual( + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: { + to: 5, + }, + }, + }, + }, + { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: { + from: 5, + }, + }, + }, + } + ) + ).toBe(false) + + // One has non_revoked, other doesn't + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + non_revoked: { + from: 5, + }, + }, + }, + }) + ).toBe(false) + }) + + test('checks attribute restriction differences', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + restrictions: [ + { + cred_def_id: 'cred_def_id1', + }, + { + cred_def_id: 'cred_def_id2', + }, + ], + }, + }, + }) + ).toBe(false) + }) + + test('checks attribute name differences', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + names: ['name3'], + }, + }, + }) + ).toBe(false) + + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + a: { + ...proofRequest.requested_attributes.a, + name: 'name3', + names: undefined, + }, + }, + }) + ).toBe(false) + }) + + test('ignores predicate group name differences', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + a: proofRequest.requested_predicates.p, + }, + }) + ).toBe(true) + }) + + test('ignores predicate restriction order', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: [...proofRequest.requested_predicates.p.restrictions].reverse(), + }, + }, + }) + ).toBe(true) + }) + + test('ignores predicate restriction undefined vs empty array', () => { + expect( + areAnonCredsProofRequestsEqual( + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: undefined, + }, + }, + }, + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: [], + }, + }, + } + ) + ).toBe(true) + }) + + test('checks predicate restriction differences', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_attributes: { + p: { + ...proofRequest.requested_predicates.p, + restrictions: [ + { + cred_def_id: 'cred_def_id1', + }, + { + cred_def_id: 'cred_def_id2', + }, + ], + }, + }, + }) + ).toBe(false) + }) + + test('checks predicate name differences', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + name: 'name3', + }, + }, + }) + ).toBe(false) + }) + + test('checks predicate non_revocation interval', () => { + // empty object is semantically equal to undefined + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: {}, + }, + }, + }) + ).toBe(true) + + // properties inside object are different + expect( + areAnonCredsProofRequestsEqual( + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: { + to: 5, + }, + }, + }, + }, + { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: { + from: 5, + }, + }, + }, + } + ) + ).toBe(false) + + // One has non_revoked, other doesn't + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + non_revoked: { + from: 5, + }, + }, + }, + }) + ).toBe(false) + }) + + test('checks predicate p_type and p_value', () => { + expect( + areAnonCredsProofRequestsEqual(proofRequest, { + ...proofRequest, + requested_predicates: { + p: { + ...proofRequest.requested_predicates.p, + p_type: '<', + p_value: 134134, + }, + }, + }) + ).toBe(false) + }) +}) diff --git a/packages/anoncreds/src/utils/__tests__/credential.test.ts b/packages/anoncreds/src/utils/__tests__/credential.test.ts index 0b81afe881..f75598bb3c 100644 --- a/packages/anoncreds/src/utils/__tests__/credential.test.ts +++ b/packages/anoncreds/src/utils/__tests__/credential.test.ts @@ -1,6 +1,10 @@ import { CredentialPreviewAttribute } from '@aries-framework/core' -import { assertCredentialValuesMatch, checkValidEncoding, convertAttributesToCredentialValues } from '../credential' +import { + assertCredentialValuesMatch, + checkValidCredentialValueEncoding, + convertAttributesToCredentialValues, +} from '../credential' /** * Sample test cases for encoding/decoding of verifiable credential claims - Aries RFCs 0036 and 0037 @@ -219,7 +223,7 @@ describe('Utils | Credentials', () => { ) test.each(testEntries)('returns true for valid encoding %s', (_, raw, encoded) => { - expect(checkValidEncoding(raw, encoded)).toEqual(true) + expect(checkValidCredentialValueEncoding(raw, encoded)).toEqual(true) }) }) }) diff --git a/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts b/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts new file mode 100644 index 0000000000..4e7bab2ddd --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts @@ -0,0 +1,70 @@ +import type { AnonCredsProofRequest } from '../../models' + +import { hasDuplicateGroupsNamesInProofRequest } from '../hasDuplicateGroupNames' + +const credentialDefinitionId = '9vPXgSpQJPkJEALbLXueBp:3:CL:57753:tag1' + +describe('util | hasDuplicateGroupsNamesInProofRequest', () => { + describe('assertNoDuplicateGroupsNamesInProofRequest', () => { + test('attribute names match', () => { + const proofRequest = { + name: 'proof-request', + version: '1.0', + nonce: 'testtesttest12345', + requested_attributes: { + age1: { + name: 'age', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + age2: { + name: 'age', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: {}, + } satisfies AnonCredsProofRequest + + expect(hasDuplicateGroupsNamesInProofRequest(proofRequest)).toBe(false) + }) + + test('attribute names match with predicates name', () => { + const proofRequest = { + name: 'proof-request', + version: '1.0', + nonce: 'testtesttest12345', + requested_attributes: { + attrib: { + name: 'age', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + predicate: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + } satisfies AnonCredsProofRequest + + expect(hasDuplicateGroupsNamesInProofRequest(proofRequest)).toBe(true) + }) + }) +}) diff --git a/packages/anoncreds/src/utils/__tests__/revocationInterval.test.ts b/packages/anoncreds/src/utils/__tests__/revocationInterval.test.ts new file mode 100644 index 0000000000..c95e8c70f6 --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/revocationInterval.test.ts @@ -0,0 +1,37 @@ +import { assertRevocationInterval } from '../../utils' + +describe('assertRevocationInterval', () => { + test("throws if no 'to' value is specified", () => { + expect(() => + assertRevocationInterval({ + from: 10, + }) + ).toThrow() + }) + + test("throws if a 'from' value is specified and it is different from 'to'", () => { + expect(() => + assertRevocationInterval({ + to: 5, + from: 10, + }) + ).toThrow() + }) + + test('does not throw if only to is provided', () => { + expect(() => + assertRevocationInterval({ + to: 5, + }) + ).not.toThrow() + }) + + test('does not throw if from and to are equal', () => { + expect(() => + assertRevocationInterval({ + to: 10, + from: 10, + }) + ).not.toThrow() + }) +}) diff --git a/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts b/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts new file mode 100644 index 0000000000..0bd658a646 --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/sortRequestedCredentialsMatches.test.ts @@ -0,0 +1,57 @@ +import type { AnonCredsCredentialInfo, AnonCredsRequestedAttributeMatch } from '../../models' + +import { sortRequestedCredentialsMatches } from '../sortRequestedCredentialsMatches' + +const credentialInfo = {} as unknown as AnonCredsCredentialInfo + +const credentials: AnonCredsRequestedAttributeMatch[] = [ + { + credentialId: '1', + revealed: true, + revoked: true, + credentialInfo, + }, + { + credentialId: '2', + revealed: true, + revoked: undefined, + credentialInfo, + }, + { + credentialId: '3', + revealed: true, + revoked: false, + credentialInfo, + }, + { + credentialId: '4', + revealed: true, + revoked: false, + credentialInfo, + }, + { + credentialId: '5', + revealed: true, + revoked: true, + credentialInfo, + }, + { + credentialId: '6', + revealed: true, + revoked: undefined, + credentialInfo, + }, +] + +describe('sortRequestedCredentialsMatches', () => { + test('sorts the credentials', () => { + expect(sortRequestedCredentialsMatches(credentials)).toEqual([ + credentials[1], + credentials[5], + credentials[2], + credentials[3], + credentials[0], + credentials[4], + ]) + }) +}) diff --git a/packages/anoncreds/src/utils/areRequestsEqual.ts b/packages/anoncreds/src/utils/areRequestsEqual.ts new file mode 100644 index 0000000000..759312cf87 --- /dev/null +++ b/packages/anoncreds/src/utils/areRequestsEqual.ts @@ -0,0 +1,156 @@ +import type { AnonCredsNonRevokedInterval, AnonCredsProofRequest, AnonCredsProofRequestRestriction } from '../models' + +// Copied from the core package so we don't have to export these silly utils. We should probably move these to a separate package. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function areObjectsEqual(a: any, b: any): boolean { + if (typeof a == 'object' && a != null && typeof b == 'object' && b != null) { + if (Object.keys(a).length !== Object.keys(b).length) return false + for (const key in a) { + if (!(key in b) || !areObjectsEqual(a[key], b[key])) { + return false + } + } + for (const key in b) { + if (!(key in a) || !areObjectsEqual(b[key], a[key])) { + return false + } + } + return true + } else { + return a === b + } +} + +/** + * Checks whether two `names` arrays are equal. The order of the names doesn't matter. + */ +function areNamesEqual(namesA: string[] | undefined, namesB: string[] | undefined) { + if (namesA === undefined) return namesB === undefined || namesB.length === 0 + if (namesB === undefined) return namesA.length === 0 + + // Check if there are any duplicates + if (new Set(namesA).size !== namesA.length || new Set(namesB).size !== namesB.length) return false + + // Check if the number of names is equal between A & B + if (namesA.length !== namesB.length) return false + + return namesA.every((a) => namesB.includes(a)) +} + +/** + * Checks whether two proof requests are semantically equal. The `name`, `version` and `nonce`, `ver` fields are ignored. + * In addition the group names don't have to be the same between the different requests. + */ +export function areAnonCredsProofRequestsEqual( + requestA: AnonCredsProofRequest, + requestB: AnonCredsProofRequest +): boolean { + // Check if the top-level non-revocation interval is equal + if (!isNonRevokedEqual(requestA.non_revoked, requestB.non_revoked)) return false + + const attributeAList = Object.values(requestA.requested_attributes) + const attributeBList = Object.values(requestB.requested_attributes) + + // Check if the number of attribute groups is equal in both requests + if (attributeAList.length !== attributeBList.length) return false + + // Check if all attribute groups in A are also in B + const attributesMatch = attributeAList.every((a) => { + // find an attribute in B that matches this attribute + const bIndex = attributeBList.findIndex((b) => { + return ( + b.name === a.name && + areNamesEqual(a.names, b.names) && + isNonRevokedEqual(a.non_revoked, b.non_revoked) && + areRestrictionsEqual(a.restrictions, b.restrictions) + ) + }) + + // Match found + if (bIndex !== -1) { + attributeBList.splice(bIndex, 1) + return true + } + + // Match not found + return false + }) + + if (!attributesMatch) return false + + const predicatesA = Object.values(requestA.requested_predicates) + const predicatesB = Object.values(requestB.requested_predicates) + + if (predicatesA.length !== predicatesB.length) return false + const predicatesMatch = predicatesA.every((a) => { + // find a predicate in B that matches this predicate + const bIndex = predicatesB.findIndex((b) => { + return ( + a.name === b.name && + a.p_type === b.p_type && + a.p_value === b.p_value && + isNonRevokedEqual(a.non_revoked, b.non_revoked) && + areRestrictionsEqual(a.restrictions, b.restrictions) + ) + }) + + if (bIndex !== -1) { + predicatesB.splice(bIndex, 1) + return true + } + + return false + }) + + if (!predicatesMatch) return false + + return true +} + +/** + * Checks whether two non-revocation intervals are semantically equal. They are considered equal if: + * - Both are undefined + * - Both are empty objects + * - One if undefined and the other is an empty object + * - Both have the same from and to values + */ +function isNonRevokedEqual( + nonRevokedA: AnonCredsNonRevokedInterval | undefined, + nonRevokedB: AnonCredsNonRevokedInterval | undefined +) { + // Having an empty non-revoked object is the same as not having one + if (nonRevokedA === undefined) + return nonRevokedB === undefined || (nonRevokedB.from === undefined && nonRevokedB.to === undefined) + if (nonRevokedB === undefined) return nonRevokedA.from === undefined && nonRevokedA.to === undefined + + return nonRevokedA.from === nonRevokedB.from && nonRevokedA.to === nonRevokedB.to +} + +/** + * Check if two restriction lists are equal. The order of the restrictions does not matter. + */ +function areRestrictionsEqual( + restrictionsA: AnonCredsProofRequestRestriction[] | undefined, + restrictionsB: AnonCredsProofRequestRestriction[] | undefined +) { + // Having an undefined restrictions property or an empty array is the same + if (restrictionsA === undefined) return restrictionsB === undefined || restrictionsB.length === 0 + if (restrictionsB === undefined) return restrictionsA.length === 0 + + // Clone array to not modify input object + const bList = [...restrictionsB] + + // Check if all restrictions in A are also in B + return restrictionsA.every((a) => { + const bIndex = restrictionsB.findIndex((b) => areObjectsEqual(a, b)) + + // Match found + if (bIndex !== -1) { + bList.splice(bIndex, 1) + return true + } + + // Match not found + return false + }) +} diff --git a/packages/anoncreds/src/utils/createRequestFromPreview.ts b/packages/anoncreds/src/utils/createRequestFromPreview.ts new file mode 100644 index 0000000000..d1738d000e --- /dev/null +++ b/packages/anoncreds/src/utils/createRequestFromPreview.ts @@ -0,0 +1,89 @@ +import type { + AnonCredsPresentationPreviewAttribute, + AnonCredsPresentationPreviewPredicate, +} from '../formats/AnonCredsProofFormat' +import type { AnonCredsProofRequest } from '../models' + +import { utils } from '@aries-framework/core' + +export function createRequestFromPreview({ + name, + version, + nonce, + attributes, + predicates, +}: { + name: string + version: string + nonce: string + attributes: AnonCredsPresentationPreviewAttribute[] + predicates: AnonCredsPresentationPreviewPredicate[] +}): AnonCredsProofRequest { + const proofRequest: AnonCredsProofRequest = { + name, + version, + nonce, + requested_attributes: {}, + requested_predicates: {}, + } + + /** + * Create mapping of attributes by referent. This required the + * attributes to come from the same credential. + * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#referent + * + * { + * "referent1": [Attribute1, Attribute2], + * "referent2": [Attribute3] + * } + */ + const attributesByReferent: Record = {} + for (const proposedAttributes of attributes ?? []) { + const referent = proposedAttributes.referent ?? utils.uuid() + + const referentAttributes = attributesByReferent[referent] + + // Referent key already exist, add to list + if (referentAttributes) { + referentAttributes.push(proposedAttributes) + } + + // Referent key does not exist yet, create new entry + else { + attributesByReferent[referent] = [proposedAttributes] + } + } + + // Transform attributes by referent to requested attributes + for (const [referent, proposedAttributes] of Object.entries(attributesByReferent)) { + // Either attributeName or attributeNames will be undefined + const attributeName = proposedAttributes.length == 1 ? proposedAttributes[0].name : undefined + const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map((a) => a.name) : undefined + + proofRequest.requested_attributes[referent] = { + name: attributeName, + names: attributeNames, + restrictions: [ + { + cred_def_id: proposedAttributes[0].credentialDefinitionId, + }, + ], + } + } + + // Transform proposed predicates to requested predicates + for (const proposedPredicate of predicates ?? []) { + proofRequest.requested_predicates[utils.uuid()] = { + name: proposedPredicate.name, + p_type: proposedPredicate.predicate, + p_value: proposedPredicate.threshold, + restrictions: [ + { + cred_def_id: proposedPredicate.credentialDefinitionId, + }, + ], + } + } + + return proofRequest +} diff --git a/packages/anoncreds/src/utils/credential.ts b/packages/anoncreds/src/utils/credential.ts index 6310270980..eee27cccab 100644 --- a/packages/anoncreds/src/utils/credential.ts +++ b/packages/anoncreds/src/utils/credential.ts @@ -1,7 +1,7 @@ import type { AnonCredsSchema, AnonCredsCredentialValues } from '../models' import type { CredentialPreviewAttributeOptions, LinkedAttachment } from '@aries-framework/core' -import { CredentialPreviewAttribute, AriesFrameworkError, Hasher, encodeAttachment } from '@aries-framework/core' +import { AriesFrameworkError, Hasher, encodeAttachment } from '@aries-framework/core' import BigNumber from 'bn.js' const isString = (value: unknown): value is string => typeof value === 'string' @@ -34,7 +34,7 @@ export function convertAttributesToCredentialValues( return { [attribute.name]: { raw: attribute.value, - encoded: encode(attribute.value), + encoded: encodeCredentialValue(attribute.value), }, ...credentialValues, } @@ -109,8 +109,8 @@ export function assertCredentialValuesMatch( * @see https://github.com/hyperledger/aries-framework-dotnet/blob/a18bef91e5b9e4a1892818df7408e2383c642dfa/src/Hyperledger.Aries/Utils/CredentialUtils.cs#L78-L89 * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials */ -export function checkValidEncoding(raw: unknown, encoded: string) { - return encoded === encode(raw) +export function checkValidCredentialValueEncoding(raw: unknown, encoded: string) { + return encoded === encodeCredentialValue(raw) } /** @@ -123,7 +123,7 @@ export function checkValidEncoding(raw: unknown, encoded: string) { * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0036-issue-credential/README.md#encoding-claims-for-indy-based-verifiable-credentials */ -export function encode(value: unknown) { +export function encodeCredentialValue(value: unknown) { const isEmpty = (value: unknown) => isString(value) && value === '' // If bool return bool as number string @@ -153,7 +153,7 @@ export function encode(value: unknown) { return new BigNumber(Hasher.hash(Buffer.from(value as string), 'sha2-256')).toString() } -export function assertAttributesMatch(schema: AnonCredsSchema, attributes: CredentialPreviewAttribute[]) { +export function assertAttributesMatch(schema: AnonCredsSchema, attributes: CredentialPreviewAttributeOptions[]) { const schemaAttributes = schema.attrNames const credAttributes = attributes.map((a) => a.name) @@ -178,7 +178,7 @@ export function assertAttributesMatch(schema: AnonCredsSchema, attributes: Crede * */ export function createAndLinkAttachmentsToPreview( attachments: LinkedAttachment[], - previewAttributes: CredentialPreviewAttribute[] + previewAttributes: CredentialPreviewAttributeOptions[] ) { const credentialPreviewAttributeNames = previewAttributes.map((attribute) => attribute.name) const newPreviewAttributes = [...previewAttributes] @@ -187,12 +187,11 @@ export function createAndLinkAttachmentsToPreview( if (credentialPreviewAttributeNames.includes(linkedAttachment.attributeName)) { throw new AriesFrameworkError(`linkedAttachment ${linkedAttachment.attributeName} already exists in the preview`) } else { - const credentialPreviewAttribute = new CredentialPreviewAttribute({ + newPreviewAttributes.push({ name: linkedAttachment.attributeName, mimeType: linkedAttachment.attachment.mimeType, value: encodeAttachment(linkedAttachment.attachment), }) - newPreviewAttributes.push(credentialPreviewAttribute) } }) diff --git a/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts b/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts new file mode 100644 index 0000000000..f4915fe6fc --- /dev/null +++ b/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts @@ -0,0 +1,23 @@ +import type { AnonCredsProofRequest } from '../models' + +function attributeNamesToArray(proofRequest: AnonCredsProofRequest) { + // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array + // containing all attribute names from the requested attributes. + return Object.values(proofRequest.requested_attributes).reduce( + (names, a) => [...names, ...(a.name ? [a.name] : a.names ? a.names : [])], + [] + ) +} + +function predicateNamesToArray(proofRequest: AnonCredsProofRequest) { + return Array.from(new Set(Object.values(proofRequest.requested_predicates).map((a) => a.name))) +} + +// TODO: This is still not ideal. The requested groups can specify different credentials using restrictions. +export function hasDuplicateGroupsNamesInProofRequest(proofRequest: AnonCredsProofRequest) { + const attributes = attributeNamesToArray(proofRequest) + const predicates = predicateNamesToArray(proofRequest) + + const duplicates = predicates.find((item) => attributes.indexOf(item) !== -1) + return duplicates !== undefined +} diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts new file mode 100644 index 0000000000..a140e13cfb --- /dev/null +++ b/packages/anoncreds/src/utils/index.ts @@ -0,0 +1,8 @@ +export { createRequestFromPreview } from './createRequestFromPreview' +export { sortRequestedCredentialsMatches } from './sortRequestedCredentialsMatches' +export { hasDuplicateGroupsNamesInProofRequest } from './hasDuplicateGroupNames' +export { areAnonCredsProofRequestsEqual } from './areRequestsEqual' +export { downloadTailsFile } from './tails' +export { assertRevocationInterval } from './revocationInterval' +export { encodeCredentialValue, checkValidCredentialValueEncoding } from './credential' +export { IsMap } from './isMap' diff --git a/packages/anoncreds/src/utils/isMap.ts b/packages/anoncreds/src/utils/isMap.ts new file mode 100644 index 0000000000..1ee81fe4a4 --- /dev/null +++ b/packages/anoncreds/src/utils/isMap.ts @@ -0,0 +1,19 @@ +import type { ValidationOptions } from 'class-validator' + +import { ValidateBy, buildMessage } from 'class-validator' + +/** + * Checks if a given value is a Map + */ +export function IsMap(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'isMap', + validator: { + validate: (value: unknown): boolean => value instanceof Map, + defaultMessage: buildMessage((eachPrefix) => eachPrefix + '$property must be a Map', validationOptions), + }, + }, + validationOptions + ) +} diff --git a/packages/anoncreds/src/utils/revocationInterval.ts b/packages/anoncreds/src/utils/revocationInterval.ts new file mode 100644 index 0000000000..caf40b93c1 --- /dev/null +++ b/packages/anoncreds/src/utils/revocationInterval.ts @@ -0,0 +1,17 @@ +import type { AnonCredsNonRevokedInterval } from '../models' + +import { AriesFrameworkError } from '@aries-framework/core' + +// TODO: Add Test +// Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints +export function assertRevocationInterval(nonRevokedInterval: AnonCredsNonRevokedInterval) { + if (!nonRevokedInterval.to) { + throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) + } + + if ((nonRevokedInterval.from || nonRevokedInterval.from === 0) && nonRevokedInterval.to !== nonRevokedInterval.from) { + throw new AriesFrameworkError( + `Presentation requests proof of non-revocation with an interval from: '${nonRevokedInterval.from}' that does not match the interval to: '${nonRevokedInterval.to}', as specified in Aries RFC 0441` + ) + } +} diff --git a/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts b/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts new file mode 100644 index 0000000000..1d190c7e31 --- /dev/null +++ b/packages/anoncreds/src/utils/sortRequestedCredentialsMatches.ts @@ -0,0 +1,33 @@ +import type { AnonCredsRequestedAttributeMatch, AnonCredsRequestedPredicateMatch } from '../models' + +/** + * Sort requested attributes and predicates by `revoked` status. The order is: + * - first credentials with `revoked` set to undefined, this means no revocation status is needed for the credentials + * - then credentials with `revoked` set to false, this means the credentials are not revoked + * - then credentials with `revoked` set to true, this means the credentials are revoked + */ +export function sortRequestedCredentialsMatches< + Requested extends Array | Array +>(credentials: Requested) { + const staySame = 0 + const credentialGoUp = -1 + const credentialGoDown = 1 + + // Clone as sort is in place + const credentialsClone = [...credentials] + + return credentialsClone.sort((credential, compareTo) => { + // Nothing needs to happen if values are the same + if (credential.revoked === compareTo.revoked) return staySame + + // Undefined always is at the top + if (credential.revoked === undefined) return credentialGoUp + if (compareTo.revoked === undefined) return credentialGoDown + + // Then revoked + if (credential.revoked === false) return credentialGoUp + + // It means that compareTo is false and credential is true + return credentialGoDown + }) +} diff --git a/packages/anoncreds/src/utils/tails.ts b/packages/anoncreds/src/utils/tails.ts new file mode 100644 index 0000000000..9ae29aa8e4 --- /dev/null +++ b/packages/anoncreds/src/utils/tails.ts @@ -0,0 +1,57 @@ +import type { AgentContext, FileSystem } from '@aries-framework/core' + +import { TypedArrayEncoder, InjectionSymbols } from '@aries-framework/core' + +const getTailsFilePath = (basePath: string, tailsHash: string) => `${basePath}/afj/anoncreds/tails/${tailsHash}` + +export function tailsFileExists(agentContext: AgentContext, tailsHash: string): Promise { + const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) + const tailsFilePath = getTailsFilePath(fileSystem.basePath, tailsHash) + + return fileSystem.exists(tailsFilePath) +} + +export async function downloadTailsFile( + agentContext: AgentContext, + tailsLocation: string, + tailsHashBase58: string +): Promise<{ + tailsFilePath: string +}> { + const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) + + try { + agentContext.config.logger.debug( + `Checking to see if tails file for URL ${tailsLocation} has been stored in the FileSystem` + ) + + // hash is used as file identifier + const tailsExists = await tailsFileExists(agentContext, tailsHashBase58) + const tailsFilePath = getTailsFilePath(fileSystem.basePath, tailsHashBase58) + agentContext.config.logger.debug( + `Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${tailsFilePath}` + ) + + if (!tailsExists) { + agentContext.config.logger.debug(`Retrieving tails file from URL ${tailsLocation}`) + + // download file and verify hash + await fileSystem.downloadToFile(tailsLocation, tailsFilePath, { + verifyHash: { + algorithm: 'sha256', + hash: TypedArrayEncoder.fromBase58(tailsHashBase58), + }, + }) + agentContext.config.logger.debug(`Saved tails file to FileSystem at path ${tailsFilePath}`) + } + + return { + tailsFilePath, + } + } catch (error) { + agentContext.config.logger.error(`Error while retrieving tails file from URL ${tailsLocation}`, { + error, + }) + throw error + } +} diff --git a/packages/askar/src/utils/askarWalletConfig.ts b/packages/askar/src/utils/askarWalletConfig.ts index dcf1d15ab1..2337988f26 100644 --- a/packages/askar/src/utils/askarWalletConfig.ts +++ b/packages/askar/src/utils/askarWalletConfig.ts @@ -9,13 +9,13 @@ export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod?: KeyDer return undefined } - const correspondanceTable = { + const correspondenceTable = { [KeyDerivationMethod.Raw]: StoreKeyMethod.Raw, [KeyDerivationMethod.Argon2IInt]: `${StoreKeyMethod.Kdf}:argon2i:int`, [KeyDerivationMethod.Argon2IMod]: `${StoreKeyMethod.Kdf}:argon2i:mod`, } - return correspondanceTable[keyDerivationMethod] as StoreKeyMethod + return correspondenceTable[keyDerivationMethod] as StoreKeyMethod } export const uriFromWalletConfig = (walletConfig: WalletConfig, basePath: string): { uri: string; path?: string } => { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 91d22659c4..42cf5984b1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -28,7 +28,7 @@ export type { WalletExportImportConfig, } from './types' export { DidCommMimeType, KeyDerivationMethod } from './types' -export type { FileSystem } from './storage/FileSystem' +export type { FileSystem, DownloadToFileOptions } from './storage/FileSystem' export * from './storage/BaseRecord' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts index 2d79961ebb..9e438c6d1c 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts @@ -2,7 +2,7 @@ import type { CredentialFormat, CredentialFormatPayload } from './CredentialForm import type { CredentialFormatService } from './CredentialFormatService' import type { Attachment } from '../../../decorators/attachment/Attachment' import type { CredentialFormatSpec } from '../models/CredentialFormatSpec' -import type { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' +import type { CredentialPreviewAttributeOptions } from '../models/CredentialPreviewAttribute' import type { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' /** @@ -72,7 +72,7 @@ export interface CredentialFormatAcceptProposalOptions { @@ -90,7 +90,7 @@ export interface CredentialFormatAcceptOfferOptions } export interface CredentialFormatCreateOfferReturn extends CredentialFormatCreateReturn { - previewAttributes?: CredentialPreviewAttribute[] + previewAttributes?: CredentialPreviewAttributeOptions[] } export interface CredentialFormatCreateRequestOptions { diff --git a/packages/core/src/modules/credentials/models/CredentialPreviewAttribute.ts b/packages/core/src/modules/credentials/models/CredentialPreviewAttribute.ts index 89c3397b09..0f341785c4 100644 --- a/packages/core/src/modules/credentials/models/CredentialPreviewAttribute.ts +++ b/packages/core/src/modules/credentials/models/CredentialPreviewAttribute.ts @@ -35,5 +35,5 @@ export class CredentialPreviewAttribute { } export interface CredentialPreviewOptions { - attributes: CredentialPreviewAttribute[] + attributes: CredentialPreviewAttributeOptions[] } diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts index 59338c7835..6ee07a7588 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts @@ -177,7 +177,7 @@ export class V1CredentialProtocol associatedRecordId: credentialRecord.id, }) - credentialRecord.credentialAttributes = previewAttributes + credentialRecord.credentialAttributes = credentialProposal?.attributes await credentialRepository.save(agentContext, credentialRecord) this.emitStateChangedEvent(agentContext, credentialRecord, null) @@ -336,7 +336,7 @@ export class V1CredentialProtocol message.setThread({ threadId: credentialRecord.threadId }) - credentialRecord.credentialAttributes = previewAttributes + credentialRecord.credentialAttributes = message.credentialPreview.attributes credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential await this.updateState(agentContext, credentialRecord, CredentialState.OfferSent) @@ -393,7 +393,7 @@ export class V1CredentialProtocol }) message.setThread({ threadId: credentialRecord.threadId }) - credentialRecord.credentialAttributes = previewAttributes + credentialRecord.credentialAttributes = message.credentialPreview.attributes credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential await this.updateState(agentContext, credentialRecord, CredentialState.OfferSent) @@ -472,7 +472,7 @@ export class V1CredentialProtocol role: DidCommMessageRole.Sender, }) - credentialRecord.credentialAttributes = previewAttributes + credentialRecord.credentialAttributes = message.credentialPreview.attributes await credentialRepository.save(agentContext, credentialRecord) this.emitStateChangedEvent(agentContext, credentialRecord, null) @@ -707,7 +707,7 @@ export class V1CredentialProtocol }) // Update record - credentialRecord.credentialAttributes = previewAttributes + credentialRecord.credentialAttributes = message.credentialPreview?.attributes credentialRecord.linkedAttachments = linkedAttachments?.map((attachment) => attachment.attachment) credentialRecord.autoAcceptCredential = autoAcceptCredential ?? credentialRecord.autoAcceptCredential await this.updateState(agentContext, credentialRecord, CredentialState.ProposalSent) diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts index 9fe8aa5fc3..da44d37618 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts +++ b/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts @@ -17,7 +17,7 @@ import { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAtt export class V1CredentialPreview { public constructor(options: CredentialPreviewOptions) { if (options) { - this.attributes = options.attributes + this.attributes = options.attributes.map((a) => new CredentialPreviewAttribute(a)) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts index d566faa1a0..ea78448593 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2CredentialPreview.ts @@ -17,7 +17,7 @@ import { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAtt export class V2CredentialPreview { public constructor(options: CredentialPreviewOptions) { if (options) { - this.attributes = options.attributes + this.attributes = options.attributes.map((a) => new CredentialPreviewAttribute(a)) } } diff --git a/packages/core/src/modules/proofs/models/index.ts b/packages/core/src/modules/proofs/models/index.ts index 9e20094e5e..9dec0e697a 100644 --- a/packages/core/src/modules/proofs/models/index.ts +++ b/packages/core/src/modules/proofs/models/index.ts @@ -1,2 +1,3 @@ export * from './ProofAutoAcceptType' export * from './ProofState' +export * from './ProofFormatSpec' diff --git a/packages/core/src/storage/FileSystem.ts b/packages/core/src/storage/FileSystem.ts index b724e68158..c5996e78b2 100644 --- a/packages/core/src/storage/FileSystem.ts +++ b/packages/core/src/storage/FileSystem.ts @@ -1,3 +1,9 @@ +import type { Buffer } from '../utils/buffer' + +export interface DownloadToFileOptions { + verifyHash?: { algorithm: 'sha256'; hash: Buffer } +} + export interface FileSystem { readonly basePath: string @@ -5,5 +11,5 @@ export interface FileSystem { createDirectory(path: string): Promise write(path: string, data: string): Promise read(path: string): Promise - downloadToFile(url: string, path: string): Promise + downloadToFile(url: string, path: string, options?: DownloadToFileOptions): Promise } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 2e6e63ccc0..d5e82deea7 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -9,7 +9,7 @@ import type { StoreCredentialOptions, GetCredentialsForProofRequestOptions, GetCredentialsForProofRequestReturn, - AnonCredsRequestedCredentials, + AnonCredsSelectedCredentials, AnonCredsCredentialRequestMetadata, CreateLinkSecretOptions, CreateLinkSecretReturn, @@ -76,7 +76,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { } public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise { - const { credentialDefinitions, proofRequest, requestedCredentials, schemas } = options + const { credentialDefinitions, proofRequest, selectedCredentials, schemas } = options assertIndySdkWallet(agentContext.wallet) @@ -85,7 +85,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { const indyRevocationStates: RevStates = await this.indyRevocationService.createRevocationState( agentContext, proofRequest, - requestedCredentials, + selectedCredentials, options.revocationRegistries ) @@ -117,7 +117,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { const indyProof = await this.indySdk.proverCreateProof( agentContext.wallet.handle, proofRequest as IndyProofRequest, - this.parseRequestedCredentials(requestedCredentials), + this.parseSelectedCredentials(selectedCredentials), agentContext.wallet.masterSecretId, indySchemas, indyCredentialDefinitions, @@ -133,7 +133,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { agentContext.config.logger.error(`Error creating Indy Proof`, { error, proofRequest, - requestedCredentials, + selectedCredentials, }) throw isIndyError(error) ? new IndySdkError(error) : error @@ -338,27 +338,27 @@ export class IndySdkHolderService implements AnonCredsHolderService { } /** - * Converts a public api form of {@link RequestedCredentials} interface into a format {@link Indy.IndyRequestedCredentials} that Indy SDK expects. + * Converts a public api form of {@link AnonCredsSelectedCredentials} interface into a format {@link Indy.IndyRequestedCredentials} that Indy SDK expects. **/ - private parseRequestedCredentials(requestedCredentials: AnonCredsRequestedCredentials): IndyRequestedCredentials { + private parseSelectedCredentials(selectedCredentials: AnonCredsSelectedCredentials): IndyRequestedCredentials { const indyRequestedCredentials: IndyRequestedCredentials = { requested_attributes: {}, requested_predicates: {}, self_attested_attributes: {}, } - for (const groupName in requestedCredentials.requestedAttributes) { + for (const groupName in selectedCredentials.attributes) { indyRequestedCredentials.requested_attributes[groupName] = { - cred_id: requestedCredentials.requestedAttributes[groupName].credentialId, - revealed: requestedCredentials.requestedAttributes[groupName].revealed, - timestamp: requestedCredentials.requestedAttributes[groupName].timestamp, + cred_id: selectedCredentials.attributes[groupName].credentialId, + revealed: selectedCredentials.attributes[groupName].revealed, + timestamp: selectedCredentials.attributes[groupName].timestamp, } } - for (const groupName in requestedCredentials.requestedPredicates) { + for (const groupName in selectedCredentials.predicates) { indyRequestedCredentials.requested_predicates[groupName] = { - cred_id: requestedCredentials.requestedPredicates[groupName].credentialId, - timestamp: requestedCredentials.requestedPredicates[groupName].timestamp, + cred_id: selectedCredentials.predicates[groupName].credentialId, + timestamp: selectedCredentials.predicates[groupName].timestamp, } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts index 30f78bcbff..ed2572dee7 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts @@ -2,7 +2,7 @@ import type { AnonCredsRevocationRegistryDefinition, AnonCredsRevocationStatusList, AnonCredsProofRequest, - AnonCredsRequestedCredentials, + AnonCredsSelectedCredentials, AnonCredsCredentialInfo, AnonCredsNonRevokedInterval, } from '@aries-framework/anoncreds' @@ -44,7 +44,7 @@ export class IndySdkRevocationService { public async createRevocationState( agentContext: AgentContext, proofRequest: AnonCredsProofRequest, - requestedCredentials: AnonCredsRequestedCredentials, + selectedCredentials: AnonCredsSelectedCredentials, revocationRegistries: { [revocationRegistryDefinitionId: string]: { // Tails is already downloaded @@ -59,7 +59,7 @@ export class IndySdkRevocationService { try { agentContext.config.logger.debug(`Creating Revocation State(s) for proof request`, { proofRequest, - requestedCredentials, + selectedCredentials, }) const indyRevocationStates: RevStates = {} const referentCredentials: Array<{ @@ -70,18 +70,18 @@ export class IndySdkRevocationService { }> = [] //Retrieve information for referents and push to single array - for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedAttributes ?? {})) { + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.attributes ?? {})) { referentCredentials.push({ referent, - credentialInfo: requestedCredential.credentialInfo, + credentialInfo: selectedCredential.credentialInfo, type: RequestReferentType.Attribute, referentRevocationInterval: proofRequest.requested_attributes[referent].non_revoked, }) } - for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedPredicates ?? {})) { + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.predicates ?? {})) { referentCredentials.push({ referent, - credentialInfo: requestedCredential.credentialInfo, + credentialInfo: selectedCredential.credentialInfo, type: RequestReferentType.Predicate, referentRevocationInterval: proofRequest.requested_predicates[referent].non_revoked, }) @@ -138,7 +138,7 @@ export class IndySdkRevocationService { agentContext.config.logger.error(`Error creating Indy Revocation State for Proof Request`, { error, proofRequest, - requestedCredentials, + selectedCredentials, }) throw isIndyError(error) ? new IndySdkError(error) : error diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts index 3e76fc6bc9..e4e4cb1d2d 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -1,5 +1,6 @@ import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' -import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs, IndyProofRequest } from 'indy-sdk' +import type { AgentContext } from '@aries-framework/core' +import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs, IndyProofRequest, IndyProof } from 'indy-sdk' import { inject, injectable } from '@aries-framework/core' @@ -21,7 +22,7 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { this.indySdk = indySdk } - public async verifyProof(options: VerifyProofOptions): Promise { + public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise { try { // The AnonCredsSchema doesn't contain the seqNo anymore. However, the indy credential definition id // does contain the seqNo, so we can extract it from the credential definition id. @@ -53,8 +54,8 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { const indyRevocationDefinitions: RevocRegDefs = {} const indyRevocationRegistries: RevRegs = {} - for (const revocationRegistryDefinitionId in options.revocationStates) { - const { definition, revocationStatusLists } = options.revocationStates[revocationRegistryDefinitionId] + for (const revocationRegistryDefinitionId in options.revocationRegistries) { + const { definition, revocationStatusLists } = options.revocationRegistries[revocationRegistryDefinitionId] indyRevocationDefinitions[revocationRegistryDefinitionId] = indySdkRevocationRegistryDefinitionFromAnonCreds( revocationRegistryDefinitionId, definition @@ -74,7 +75,7 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { return await this.indySdk.verifierVerifyProof( options.proofRequest as IndyProofRequest, - options.proof, + options.proof as IndyProof, indySchemas, indyCredentialDefinitions, indyRevocationDefinitions, diff --git a/packages/node/package.json b/packages/node/package.json index 665472421c..30ffadd1f6 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -43,6 +43,7 @@ "@types/node-fetch": "^2.5.10", "@types/ref-napi": "^3.0.4", "@types/ws": "^7.4.6", + "nock": "^13.3.0", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/node/src/NodeFileSystem.ts b/packages/node/src/NodeFileSystem.ts index 240440d64c..a5caf0d070 100644 --- a/packages/node/src/NodeFileSystem.ts +++ b/packages/node/src/NodeFileSystem.ts @@ -1,5 +1,7 @@ -import type { FileSystem } from '@aries-framework/core' +import type { DownloadToFileOptions, FileSystem } from '@aries-framework/core' +import { AriesFrameworkError, TypedArrayEncoder } from '@aries-framework/core' +import { createHash } from 'crypto' import fs, { promises } from 'fs' import http from 'http' import https from 'https' @@ -44,13 +46,14 @@ export class NodeFileSystem implements FileSystem { return readFile(path, { encoding: 'utf-8' }) } - public async downloadToFile(url: string, path: string) { + public async downloadToFile(url: string, path: string, options: DownloadToFileOptions) { const httpMethod = url.startsWith('https') ? https : http // Make sure parent directories exist await promises.mkdir(dirname(path), { recursive: true }) const file = fs.createWriteStream(path) + const hash = options.verifyHash ? createHash('sha256') : undefined return new Promise((resolve, reject) => { httpMethod @@ -60,9 +63,26 @@ export class NodeFileSystem implements FileSystem { reject(`Unable to download file from url: ${url}. Response status was ${response.statusCode}`) } + hash && response.pipe(hash) response.pipe(file) - file.on('finish', () => { + file.on('finish', async () => { file.close() + + if (hash && options.verifyHash?.hash) { + hash.end() + const digest = hash.digest() + if (digest.compare(options.verifyHash.hash) !== 0) { + await fs.promises.unlink(path) + + reject( + new AriesFrameworkError( + `Hash of downloaded file does not match expected hash. Expected: ${ + options.verifyHash.hash + }, Actual: ${TypedArrayEncoder.toUtf8String(digest)})}` + ) + ) + } + } resolve() }) }) diff --git a/packages/node/tests/NodeFileSystem.test.ts b/packages/node/tests/NodeFileSystem.test.ts index e242b43cdd..f031ee32e5 100644 --- a/packages/node/tests/NodeFileSystem.test.ts +++ b/packages/node/tests/NodeFileSystem.test.ts @@ -1,13 +1,42 @@ +import { TypedArrayEncoder } from '@aries-framework/core' +import nock, { cleanAll, enableNetConnect } from 'nock' +import path from 'path' + import { NodeFileSystem } from '../src/NodeFileSystem' describe('@aries-framework/file-system-node', () => { describe('NodeFileSystem', () => { const fileSystem = new NodeFileSystem() + afterAll(() => { + cleanAll() + enableNetConnect() + }) + describe('exists()', () => { it('should return false if the pash does not exist', () => { return expect(fileSystem.exists('some-random-path')).resolves.toBe(false) }) }) + + describe('downloadToFile()', () => { + test('should verify the hash', async () => { + // Mock tails file + nock('https://tails.prod.absa.africa') + .get('/api/public/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p') + .replyWithFile(200, path.join(__dirname, '__fixtures__/tailsFile')) + + await fileSystem.downloadToFile( + 'https://tails.prod.absa.africa/api/public/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p', + `${fileSystem.basePath}/afj/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p`, + { + verifyHash: { + algorithm: 'sha256', + hash: TypedArrayEncoder.fromBase58('4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p'), + }, + } + ) + }) + }) }) }) diff --git a/packages/node/tests/__fixtures__/tailsFile b/packages/node/tests/__fixtures__/tailsFile new file mode 100644 index 0000000000000000000000000000000000000000..73f04718605544e6cbb4c49181306e356efa12c3 GIT binary patch literal 65666 zcmV(tKuDUepj0~g+<6Wf7WvDI z^GQSBmK)5pUB|np892s;rgJQZ6Ib>n#4-nBHv}~fu* ztq>ryW<^tcj8!vJk#Bs?@^WNDvj{c>%Z_{iG#C!m4F@&y#y5eIZZUPH$bDrj0RU>Z zcTJEI)|@A-hplGjRTmPqdB*q6Jy}8x%#WXW+So}EzK3^(I%>W8r;;Zd$`d>KyrUS4 zPgr?ib3eW=Dq&a08HG{)%@Vm@;je4m4HE$K4g_UgwXXyd;%9VEheH3_`C#s8FIGI( z7ZahjFBTT#z$TD7VNz5^dE4A23ln+y%<#MFa(ODE=uVWfngTVjXEn}7%I6!c2f}>J zY&%aQsitsZ@WA;Fwnr~-uMZYo24D8)HRN`F*fl|d8U7B^K6ebYBtq|$NE;9pIYDfI z9(xbJH)B}3EFzm>#vd#k9X0$lisloB4toZ%AFLNtc4cl*z0v=Kh#nz&Z8`=B95I5e zP|lZ3cgoAXJwXi#Tp>q@kb!{MMGUCSI3KB7yuHq_i^Vr?c^9h^TzH!%3&8=_iWGdl z<0K{i9bQ8}=m-K%l=&!>4@3K_x5-ccjrdI@?_zLHVw>5Al>#A&kH}|*rNY0k3l%llV5&+moLp?JrZ-=1mWC&dW<7`*@@~v3xq^vV7d)#X~mQ` zN%OFtw---axd$>Y@2aW69*d4m2&G!w;8li_dDo;3N#ppO9tZ?t3mSzDf*u$6Jxv!>->%tIB>D;hn)Q3M^; zG0_VfM}N`@q0+0y^?01Ku3{g4tMV?bV2#w-y#V5!s1_>jF=oSd>a8Vqt-8i+{}=7# zH@7k30h}{o+YG4*b9}bm4O#4oT2a~Pw(Y_l>|7bx=>L#6ek*B7mdYHbsH1n24g$7$Hzt zv<*Pw3?v^dmwPqmq|&<~^V9ntk{)eM80U7`v4_%#;|aBX-5QUhp^^T^56P#RQiFFV zU{oPBX-NM*DdQ955;|(T=?mioBk6RJg@T%_@xOmkitM|!?RPUl&lm-4QKBh_b^)9Q zr}I(OGHdKVx%{`YS$s4K0xlNo6ScY+@9m@f3IoOXzm0mzu|wdQsQ?6fw3FVqkJPRB zaus>WH*bETwqnbxV|?XdX;{D*-0TwOj(@dM@duDdJ3u)|Clt!ZtxA)?3}&1(`ovQ3X#S%sU|9 z8_m?zj1DOq$$$nM7~tv~xJ)@X;z9a%?@ZiR_+!iq@WYc!ITZ!ThO1`u8&wRaO>~Y& z>YY+!Muzoi2e81ly%za$MG|cgwgrMjk*jmZouf_&pb-*K zfVU!0z+>W1x+1mWQJCc`9jL~k&EzkN7%Ps^;~&S6Fbp*21wnx~M3R%^5dJXojk92< zfCX?n;3>-aDI0E)XBxXS+|enJ`{&KO)MEUtKH|}b>gZTTon4}9kq{rueZR+&XAUEn z%cj{F@VE5EShP3+_&Jl+WL~Wi9U9T%`z^{b!4$uwc`Gs4TvdvM%@0$>+z_qJNmX+m zP8*qD%P{{EN5w)z&X@M<*gg^Cd2iRIO@ecsh&l-off$CBj97kSicTfEW0WN2e|2bq zI}9PU1KBT$z0r)lA`CIgKBky3EePx{jc)O7DopgZ;`Hh@0FyUkhx#Dcz#fI3Jmd;x zxkBWCZcMWY@(*9RNWUkf@!V4oAFVOSvJ5}HO8{7|D!kF33vx(*C9gRxpgp0dXQslb zH8o{M{0}R-Cm|Ab?BiU9^WC0YwRjEg^(u>j9amd!;^{u)Hc$SMcuyO@$0MLp?j?-^$rTYf7Dx**rtnNJ|t^QE&AN~`5eOOY$m0C@8~IuAQq zW77+&nRqgJ03NLQk!q-A7_strVF!)iQv*4mLF}$QRx1ehX_)?#9iGN1 zod|8LFI)?Z{{LW5Wzl+L3J9eWdAAcrc1=lt{@lZ<1jMQQhOBs++Ys1q9q5T8hrJ5j8+o%(Ja`bItOs=SM@-@|u3!*MtMvL+VTS zJWj7c))@Mn^yQDsD%-N8t9r@lWkvKEFznNhi7ps3U-61DKoVIgq=wMoznv>{GpVXq zsETcE^f+>u8uLyX8-f@X3kSf%GB3z|Aq|B7iEk(=MK1Arxa71XG-!S%+LY17 zt02u>1cr1ugM-ae?A3_}lu$$Da}h$h__|>YDD)>PfEih7t9Na3+0!m6i8F*!u%li| ziI2x^*kx{m;zq@!PX{F)jkiP#S~Au#ygUgoATe8=lNILgF1cLrNRDai@J8M2wa~axL`SAP!Ilm zck5`T657HA8L`iwkXhP(V&lwXtGBE&eqkGl9}>pGp%7u+9qs|ETpZAA3>AQ)rZ8aVQoK zqmfnjS~4P!Z5AiSvmr6W*$$kbbQ-FA%Uz$jr_b2$_2sjhaIwtF79!U8e0?DFYUj0> zjWL$8(C69lxxZZTUPyJ}nQ-M?+ZCA#;QIbdJ|#_&P-ib*$8B$i_#S_D+cVLI;AhWK zs0SyfNk0U0B*aTxAtpC0yzXMM8d-0O`Ft2bX|eKg2oy+w%H0dC7;G^rl#G^$f_T4J z^%F_p+*BF$CX%);?h<=xh0P0Hz4K)AZZ20O0~)e)$|M_DIJAE1HFaumLxY68YiSDhA#xD??*= z1P~V%dP!(6hwj_NmmM3$W*g|*Jl5LLS5-EAaaY<@S<~%k(Oc=ZCW0|VSUCuch71mo zSKf>b_kgBN1_rmDt1xe+wZ25gT(%4XP2>(KgA-+Af@l0AsiC7&Ek5?}Ok%QK+BcGa zu%r0FS3w(z2_GY25i^c8-W?mmTbc?GdvpL8>I4$`H=h|{AdrYbJ{=;rCD<2=t!rXR zq2x_^=WtDur>KizT*IYN``_st_z(|k!+8Y^5~&Wd8CeZc=qbh8(eFrtT|sIk0?;%s zCKaPIWA80f*R=<5*_(7>dKI2WLXz@{aZUV)3OQzDY$DF?ns=(bQ)u|9UmsohxXb@U z>qBIQjy$aq^%tx#FB&{c`?TAoTD93Sd5rIvNGgvU&G#j&)(-MnaF|mC zRxFMjj^RjbFmoWG{y2Te0Th|fz|Tqkz!)`P8f4qZ-_Yad`S6G{@IwgmKd)Lja&k|R z6o+#aE*|dd#?B}zol2Wv+}1sW4S0j~XdY}LkXICWA8sWeYazp*)mH1NjxAf!8;`2^ zD>t!jh&JbQX}X9M=Xag3)*%!pZURo_2C-P$Ix)@yp#2KOK@?!Vh_?DgU8mAZ%^z+O zz<+H!HEWb!u=HfG;BTw|WKP{5JHE(Uj7bY09szppkm#v{lqEMCA9mK;bp6sJbMjxQ z(O!=Plr{D}=mp=fJmQR0Owg6_V+}9k4u?TH6vy@6{oDq33v({Ym{am4a@xu!y%eOwT zgCp4|c+gtE_IPaeh6r>OzM>*xlGX7cq)s92rHzRXCYtd>0knOU&6t)qJ{AJ-f2C@5 zQ{T3`rR@pM3;ShZ?r|iCdgZ0x0PjxD0}a95zabgNcXM$3wt9(%SigF5Cc^1=bf-{f zC_S0DoFEl;hL$hbWp32uAR94FPtc?j2Dn2uF)-WXb@m2fNFd-G*NPoS@|k)XNQd!c z>-HnJG|%IJhZVCVNXqIRhYlzgZ0$vWih#_;XdS^-4*)tDU?DPs8H(3-$zrSb=?SX3 zPETT=>DdatdAvSCx-hC!!7i{+GCZp|-$R|J@5mtP-*M(B^{ha95|-o zhc`D7K@0fZ4neu8t_aDT9_oFaB9l<#j1INP0G3V9*m(j3y>a&D=VP$j=n|Ufk;&WB zTuWfVAd|MEmv36KE&l*=0U=k+n|Is_TM^{B$2g^!97&|1QIZqa%(vYE8Qq!k2<1S< z*A={Ff*cq~*-{?!rZl~aTH*+exkA@Bkh!RtWh@G}3{*l;x*RV5KSr*l@9+^m^9K!M zL_uRr)BA}(vdeV-=~28K_8DG*AAH)A*_YGR$I>%f<*3$(4DL4zXg z5EB`J(0n@AeiAWF&?ltLoO}i@yu287-^|$m*~O4vtR2hY3F7DStvfzB-W2)=(X=Pp zBe-7#s1&i`W23k-AQecG=!D;8Aa0QzNwd&wph>I;bz%^+G4m&^(9fZo=G13~p?uy!SPr7WWsrX=?mi6oNMIh*DZe%;ISjEeIB zmZ{$F>^~kUl+qVAZA~OEn;c7Z|3}n1zk?YK6!dLV7TZ;e>hx`Nr{1EQ3G&dC76pQW z4S+L8dcv19&WF$Qp(`{A(%04)o!*qK>>Hylu>JixsmzaN0FP=-(^gq25zPAv`Yd=b12ChBY_ zb|2P$ncose2b;$N(iQeT9@62;p#}d{B6Y|ei&`vWogol%1R`nnH`H!#tV$g~SPVwr zv^~DZ_vrO5rS6T!3=ct6I3n@kTmTWsS(Qp`l7rPu`5*LhH)NrEawBoK8y{v=KEBYp zwf9!zxRWh5)bZb!x0p}KBQ$uWyHEpsCl5Oq-)o!{VT_`>sYl&!R7uaowQM{lNZj8_*)%Uj&*wYWJH% z%;e)sKH}60=SaPjlaksO9}Ar~%1LJcez-$1hR&KnYe@3REy)%JNnjNJ4ib?&2MVN} z+byi`?h{fE#Yfosu|V~a{2_G_G6tCHa$AW~mk`=A2HtTrR#8|&$zyUD*`2m>eC%N;v8%>P)aH@Zk1*h=nKeE7K0;ZCDhZs zH!|F#nZZo#*TaJ+NyWMd^g1GRZ3$>G9oRO5vu@&AsQ>krF?Cyys9kpye&ky0ZZ9M8 zV;byGo9nRk7Mer$@DkqX?#H(A|4Mx}%rQTJ2_-`CrwsM|fl;tzdpAEBDu@!t9~*29 zw>Q&eE$^8Wtw=7~uoO!>;pJ!|^El>YgJyl)Aac3qL%Op}oLCGq{+kMe#1M>UW}H1x zt_Vz3R|QaPtdPFny8O7X(e&8Ub$gp092D?PMrYZ@Q#7Iz>z({tk0DA;A}L?FL8)FG zSVAE+fF!TDvT7nfp%?#GEI3qHyVH23=bA#iXHH?-8^o{X9)%0NJS^fC zXRjSps9}pkMxoKa@N<3~yC6Jb^R@fG%4qaYk^98+Wt^FEcX$fW)%K7Zv}$12iVio% zEW-b7bU9sgD9pvW_9JgLi>JKB zrWIzTn%e`1G7rq9k8zG^yY^=Vu2Y|kvMtjl;{;dk)*`TO-QD7DRu-})e@sWjHl#!4 zH{DRf(8KZBw%x#c&)+NN0_PE8Wg4#1q@esaZ$zacK9VjOEy~DsW-M)U zw!6PiF>7t@@{|*~t^`Qt7{pL@)d`3;(wu)eE@+OJWK(E<+ac|_ujXmjcO4%U@x%2% z2+KZk(ZDGB57wo?SUO;MeBeF~QFSL_cmmDr?=KS0JE0(edRE`jN4M`l?8m`iY5e?e z=`|nXlOh1fmT%ttW`285DN_byw0+j;p^IqqYV_~LSk=&IcaQ(#UdGz zkuAuW-v_03gv`q@cs6e+X9XuZo5UMS69{~fN7UO9Ig;SmNfV7c7b_mBIVooc!Vo5J ztucI{ewP59izNj=!UcW1MgTG7%^ecvd{)V`v;an~&ymt=EGLq~@dj`}3|HHP>mrF= zMWO%fUMIDq=nR=tnSLRd8sM|?GgaK72AU#GQV?qk#fGJ+@N4eDry`uQXhaJkddsFFeL4EAynnfq6p9o)^Rc@wqCB?$N8Is8_5jU73Q&QRW-i83hUrUI)o9(PS1m;{;=e}lc7 z>M9GMh#aKy1O{doP|?Hyo9U$cb7dNda0OV%6>WT}cXDhF-Y zahjI9L!9O=V^!Q}r3W+wricpzNRyDp0@88 z!gXo1e3$pxIvHSH=7C?J@@E!wXRh@X;NJ@P^ORNs$12rBd8#65ajDzuDW5H1qyib(7aejt3f zrwPX2!(PI(L3*%=9ab!&~ z|E3<4N?ZS4Oh{u(v=1PKgbiDp$?(ebE4GqPO+P}Xpo@~f0e%GKyWgPqsTVipgAt~h z`L(bQr~`0VoYQ9+Tli@AEQ;YMq-Y%UnFlGo;7q>3*l0YKU2c#jVxi?urXg>ZWb+#O zs@amR+7gDS>?HaT#a$iThTAtr`a_7)ib_Kf$FlL%Z39H{S_H)WSloF12E(E5lG+We zpOQp1ok=hd^a?qhBGBEmoejqN0zMmbiva7U9Wbt;XRE;yIOC!h+s=W*QXcr>{1F78 zLcfkCe3X`*?r7_OVM$v}yu(dz8JeJFuK_X~020YJgHvDzPgAuJI~O-ciX8zW05dHR zFi~nHl&Ua7a2HMpF!OZEK0XLbM}tK!fB4z>zlR6(k$6}R(c0Efych`M0@dL=O8|7g zI6trR7}bh!=&d>$*0;D?DlDsG0TBlT+4Ei*Y8W;0@eZs9U@{fx+=G%Nsv-FmigQ zQ(o(EHWZt9>WB*sG0EtVqe zcx%}iyBv_zE5x>!Geay=$LOS664Kotfug&m9H?BB(H8UJzX09z@sFR(7MAv%d~zmz zdN$XQkyW-T6*@twl3@6a$r7dtT^$Cr;t)VL7-klI6u0YggHPjDfV?8G0*K-MejYyq z)&Y(&d-Nv~dMbAe^@%$f&v4B3xmZ?_JpCy+h764$9~D5G3m-og2q|eN0o(L69>m5- z+^JzKGyr~fKo$tbQ``#q88KH!>L4a3J^UowCRjRSMD4RwTwb3~T>_f=5(IN)(dmPa zE6sInXT#FXR8MwSz1G(Z*}ZQ0(;k-=$Fb^EIZ*Foatn`0gX8J*5S2;GUSzw>$9I+= z;vjks_$M!a6bgVrmJ9zlZrw_6?zaG~O`rvMz7^wZ8zHL@5<=g?0&%@HxpwK`RK;^c zoR{^EUzm_q&iyq9c>)B;x-rB%WxeQp@`rX4RRYHOb^-ozllcpEqiK_Nu@2>s&&@RY z+IJ?^Mf843PYDYT9Jl`Qm4)uUrrxjfr3~g_;7TD)u`$OwdX{I~ZjKALt(}KJXEisp zS}$my86=tLqMq(Jrh;K0t^zPg-tjV&Q6XTGW=H|Z8rb_DZ3`(0Ig@M*z^~oY-t(75si3Fi4*k}eU%7u-cBZn#1wEKd2_X5sQWHj@{I}~}!b4tDUQFVj5rervp!FfcZVJa?79Ltxc9S4qf(kYn z3}uStW+k2tN|Z0Kx@@8&$tA>hNppUv2zNwlR_Sw8b-J=13(uDm>O zdJ~Xtv~d47Tk&~+2O#U{oABEFG%K-}2!EIwDsDn1+8|8lFnPF>+T-v7&fgrNUzvyL zD3CY{nEl+Cqt@+#^dp!tc?*)v56BZ>%h2U3uGZoizjqNgQm5*wceDO=F< z>RgPG29g!>J!D<3Iw&mn$dka&OyN&dk z*cg_*o^p|z>k0UP5g}8o;}j*T%x1J1xrH#WDuEwWZwjkO(n+~-wF=qB|7DeA&2kPh z7Xs_sMBkMpB+O;+v`gk#E6B8lJ`6ueB#aj>m5Ga^(Zpq!_s?& zq5`pG{capf6CMcvr=(&~GjsSZ;>l$ydLBpc+8cLZ+DHhAnJeGI70GVZYO+Rwv zux15UvmP>(So=rDKZ)`whe{&(5}J#OHyp9Y>y%%uTHC3Wz7^wD)YTN*=q>%IGBMRi z(!|SO?8z{=FuJ}q317gxR|hs9PAgUye7UyLSN1M%XxAs z!-72XVP~}iq~jO!!nY~;ehKm0Q59_Bq-2{^9vNu{=37y`26lfS&6uun|0}fc?cb;i zhQ4?=1kn+~#UPIm{KL8oN#OQGH(YkoNZlE)r7*3Z6NcR^4St85_!g$cEw^HWxT9~o zXk=Bm8Eh;K&b6|$HjnB={K-{I^%O3w>14lB+~fdJmqw4DEJ+7vCW^buG$GJLL@i@? z_zsA<7IhlGu@0!a;n2MdR$IHRRG@cE2}jh$);*}vZyyi_XaXn%ZVpGW0;MK`=#523 zLx8xPQ$CS$+Ulu3Rtf#?W1g${3Y?UBXT2Mk56f=DqSn`(9{{~^<9%Y0fduvGvvRO7 z9LLEdehAzm%&g3D3s^Qt{FHJnK*sX`-yR$Zd{(cifDJ7(faqMU3KXJ(ld1b^p{ZVG zwbxt9qa*+jCCnBKSJnJEu#BY#Q3YoVSFFv6$#8I;u#bFPMi%RiBq+LH?A<#?f+m^R zlZsnltz;G-po}vvRgk#M$sF*Uy~RVG=B@Ubz-}VHAfcv^^2m3lIPj`XqMi&x7Z2|- ztUB#I}_}~nP<$tHrMmKC>Uebr(_hX;@(tKuiH?o;~5W2=Wc>*n(Ymc-9XEf z9tM>*h80r;U8SH7{gb zSClE5U>ZCSR4TWb;2d=k9fQSP6$U^djW;bPnV?o2n?>O*!UBU*% z_cvvMCEy7c&dL3}5xtp$1s9K94ave_4;(J4NzSZ0Qjuh^+$pC<7C}KPM<9DMOYw}WiL%5&}H;lJDM4dfe6R^uhdKV6MvN`)=dJCr!m~&TPhYv z5)qcqJ?nC+RSM@V#|BUv={-^1senW$!tEDmSyeC=2_SI+s*-17-WWeXiatz7ErGp@ z0(!PGH$vW=@Q`*_Mo{0jK~86ae;UZS2tC)=j<|2sn9R!qcTKr35FI2IYSn+B@Um15 z3KdwZo3@|4r$Buo3HaG3IR=MZVlL%b5C_AmE!nU(vl#`S?kzsIxmfB~}w(>g!o|9a`5G{V(Cid^eSbvlbV;=ZTJv8j0l?#TK+rHpw_H7Sy>*%X2x5 zkzbv<8VJY2i8yx)Jh>_w9?FX71;m?YZc_Qr`S}+BC-uB`&I@iQ!9<&wQQxOPCRK(e z={{56FR-p)7=_u`z;X4b>HyGAnM7d+b@57=mC%OIi`^AYxoP!wTZ=T~IPgJIydsEB zkvi!2OP4W%(MB27s(cDIaIl<*k3=0(i}$7*?jm=rn>PY^dUhhgf~vu#Wa%6*ll9Pv zKiG^{|xs)nURLz!sf3=*jJfIwD+th+VkZq@`m!6L!muV z!xtN~QG=OS1)|2q-`SAR>r-QSUsDOHm{pu8y+?DE>=J%E?0LHBwCL6*L<=dfvW0yG zW4SkC33MWF*S!lOXCv!7r(G@nBG2LM9!|_m@QRs~S~}mjCR=kR(sq{(tZW5JROD z2!K|Y6b41bIUf3Y@f_{ZGPih+j#qI=O$Yq&tCx7#y+(58s3dQlPJ5N{LmX!ht~;gG zE_ng|d^QeX^es$xLRK)s>K0LP@wp$>yH6QQXJ*=$P)FkhOFNs_FITH>#`?Ge25&zpVV3tQopoPKO7u;P;UP)b%iEZmc6A26yyrvD|?XG z$SiSBCs_$Z3>x;W9Yd7Y1=L&2OAGJUeK2N-lLm!VZIU)7J802~^&1+_qKc-7{?w;U zpHH+^74>duvq3K0stcfp+TLcc@Ek->prnx#PP>;;@iNA^szip^p!E}Y*-16F42|1< zt^r5r&>nwKryJsPC*yDK2^=n|x2r!U1aCX}lGeMn2m;Z`P-d+JVeXHeVJCXTS)g;2 zC=^LnuW!RfcaQ?)?F;NF_GcDzn~_LV5Nh4+7(d8x)B@8aqf|y^Fk`}qvllVU`3 zpLG(vNBNT+!n^g(VJ#JLa8eCf;ziPZ;$q*`EF?ybu)>_sR`_ALt7f0Pjx6UzYR+Cz zRsrna964UjqZE>RzC|~NcSvb{_G~_Niq`o`^Id1WB<|arXMci$N)~e|H&KP@mOd_s zQVhVU(KViY4Mwt&+-Hs~bFfnWpd(r@aUfO#Qcw}W81cP9?Oj-@fptx&zy-&(m$cbd zNeD=yC}Y3nUu42!yGG96tuXW45cnm664FIIk(kMDQ4ki`c+&y%xRsk`I53akrH7=Y z<>&j6xw~^%u_*#ieS8Q5bZ7xk0Ee?}a@G=-(5&yFrr7smR;~P+K#|-OYi1t3YN6Y!MP7+fm zgl_6+YYp7A)UoP_v3Ea?t+3`XNvXaG z^AYP>bs-QY+z{y0%MM4`b_JbTUb;_M_%n`QU=T$=Ig%APm-6FiZygFbOI6$-lLzqO zR1@hX`IX@v?6HQ45YF?FG`uT*!Xvs1bZ2N+4PV>)?N}Q0_cKOja=6jSt6|rgv|`b% zF9d7^DMr2|=P#!csMBr}f_vH+wuNP>ml{V*=B}pL5(LI^>m<1ln1L%MJGLo?5G*6I z9EgN< zMgWRxmCaNdV0VkY#0%tXRZ1AoY7o%{sALW1V{{!D)0@~aqr)sNVY0q^b!Cydc;nun z2OY$TP+~U_Sc3~;8`mf{HbC-WI28!iY)~{{H;Ko|MiS)BRFYOnMizI+m$hHTTSn#K z-KKzI1QL^kUoT&7t^?Gry~-pNx(U|R1A5tZrvAj+W3IT#T0P4wl1wwrniu1YU?huN z_O5<%g@Nmf{OEbhg!+?Np=mbg?_Hjy5(PAt>y?>vVppNW#}r|Uo0iNfAkfB-ozZuUX96mX;;bGbS>!_P9Y^a=kTrn}(n3*w;W1b_AZQzU@e&Hi5uCaK@P{Ff{S`UJd(dum;dwOE7z09~;>H_%)c`lGj-CPjvu;?I zf=|?G@G|b;EbV)EI*rf7!I{O;Ee|)y97Mku0;S@*YonT1alYynAukboGr~v}qZKGu zDIr%3o{frUs1Wbn=U9|4gtikg0o&g(&8FLJTt-`9NFE+Py@t}1Tt0{H7bAVAq%MYP zN(VIB4QQ6Iy4eTA2@NZGJW&Im)*5fGYqYa;*Tnc)fIDpL+Pdct4H)xChZ2&tt@awA zjb-s}mc&r_&@EGaYwh7j?rOSaqmn?E$pK*-a$fbQ z!`|3GvIeMCZ*gG*ilw0CKtn04xtcws8N8wV4pE35Q@&M3x)}tXc8AK|<)n}U3N7S@ z6U3}ys>BcjMyea)kiVVqPaKAgs_R`@jt}Inc?2!dw_NoBn3+o5A=DP*6C5iE0{}?= zl`4?x91_Xy*jaA#iiu|7Lj)tlX0(^W5*L;Rmlj#FF?l=oeZk>@|B(-ccp$M%r7=l> z9V>*U>7I1zZbVldr`2y6>;n1}Pr8b@o*MKPZI75s6aNG0avX=8y;)glJSolG@)+yt zrZe~j;Q$}pQDesh#g!oKOzms%obd5Y-Rz0fnVZ8EocfPqyK-w0&MJL*m|pF0~9G3P#@OVJ(p+f zhH11C1fBQ-ywny+cPVWhm<_)Iz6R_!7uc&E!Ih%+s2lHv&;2K%8cf=BcI0*z( z?hRY^!StW+IhZUkaT5*)VT#o#(jK~gne@sCVxvxB6VaPHrP2rx=j_?S8Yp79O(v3b z^9=J0M1~j44;j|}78E0cTftB=%q6fbxpYBhf>l$95hCXu{zN;SOX>A$O6u^EC5^zo zwJHqgeFY8PMfnN(EfU_XRp=TF8-~r9yoj`ZakUWqS;%bwwOr0*wD2(py#;y(5syIK z|KlZ)_4(lHQ63CY4uo0(0z6Is26}|y6(S%>5@oG-W63p0hDVrotn_;*;)*_zJ54Af zlTpL?a3XFJk=V3dQAMU}vqIK!mMAZ7+8H?_47HsuqXAC_u@&isNGK>jcF0VhGobv& z1P#)|Fm!HSHfzeNMgjNy`~W2nrm|pZTPmw0Sgq{vaG_@7k=>DhTq-77U1|A_JO+Os zfi~-U4*sn)u@k)9ZiDDOGPpA;keUO{sT)G9as|H0lp%ShN(GD=RVAF}aqqV>Ij8Z7 zIm#i>*8sTJgB|)?2RtQ#g)wlqtO(sxvq+1L_tVMWM-D*#g@R!nIi#vrz7NFQ7`EkaWCphDoabu7yK@e?u;&`m~p4TXRl zE+V5c8PzIfAI&M)Aw++9?*y_RNpp2*{vlu=+in=!_B$At6bdBn8I{G# zP|v@T;>_ek6;E2)6(az6WX(wNyp{I&j}JSPb;Yns|KdjQ;Pxj+q&f$ySp@%3ZY|GX zjJ>LDh2VjiwgQ4(F2x#}mM z&dmgXY4r$lw$&Cj7^XyKa}yK%C<7%;6^u9ME>}+F{DI0DF5X4}pBk?jXC6vE>JPy4 z-K#uhv>2m)D&ne%?JfordKq!w7%S!@b=%a zgA{s^Fh~C7R;N|>j4?7OUb+7wiq`+DZN%&le+x$1rLX5xE=grS^6{uh5r}HE z5QPLkIVr#durS4Xl@i;3E~|*6(sr5B)q>N(u*l;;TUf>4xfu{1u1SDp2PA1FmNl`1 z055LqVsC}V{!7KC!=~4v+y>LWp>GX=cOY+3ymtcAL#d4Oi2_O$*HHwRFn3>H~9ZHVhgKYY|407?qLzv&Q?T zwLN|w1^y?+;bb9v@(DZ#;TLGFXkKPCwO_rvlwtU;na+ESWXvXr#uWAUd~2ivyA>2sU8fBX zMF?7*_ZNr&lDSX9_{#LqzK@6`wrh943LgOmN9ML(T)KIZjx2Pb6_^F~6r`k!?cu~c zxMmcWZ5p+i2Xv_=^*v9QMF(Mh_{QfiF`OPX(1%N0Ls|OSDFyFN>%Su1R=3?2d=ibj zPkv5s@6M{(;3PFj(D95%Arfsbp21bFc};Yle9z{nyGzd9m@h1Bxmm6ug?=o#J{-*r zHanWBcHYy7($3w0JH7MFpr77SHaj68jAX^t9S?kBMqW=ETu=1h`#sydkFCr>S3kEl z-VeEM@Rua`s{`v>`b6i>YJ3CTHuWIh9S6_u29U`-BXq}f=*+nEh6mPWFfC6PJw@*q z;j7*oup#qH3ur`E#_GvEul|W^lm{#gKHKGlI4ghk35y|QW!q4x?CG?#nn!F5Ty&es zSqH&cydr3ElPUiddd_ZdJ>5#miK*q^gnpK=8C=Rg?jwa4c&KvV?GY5ZDY}U{HN!mP zfHK@lf@r-B$(rIt)*g3^y}bnV&Esx-UMQnuaLoWWK*+!S*F~R+OL)NzbwDQPp^R7$N+G+jNf%Jfvp-KT#Y)L*o134BWg_)6s6SSb&VLF zZ@B?qcEy+ip7;TOz9p`#vB8JE{&+I)j$IK%N8*R7f@KtXn)5~*S%(O{2x2NR`J6js zCf!ATWKY_;Rv9!&xKcT+hP;JU*6st6Hs)3A)c@LlnDWm=y5lm6ACrd#%VK`X*rNwS zM|%*BQjdK|1qS9XiUI2pR;>Nw!@l~el+kxlbW{%m{a*!!~0 z=*F599z!48`T%J{~f`Vb2O*KMl2FC`RBB+vapS-YQuLlfE zMtn<(|L*L2uz= z<~bxoXXTiFRushSTE+;9yn9#54BE`+R=Fwr>9cOG*-jKuu41?J)oI!br9HZB9V}Fyg(}vQfJ+Z*)GWz2t)&L0W=vlY*xG(UH|qvN-(Ml! z20C%p?@>)q?X9H&RoIZ-n)a3JX?HkmTK6JSSyn2JhHL!DQFuCqDnzpfqXNcJV(ej~ zLaxzOAa$Lm7b%MV zv!xn8HF{-KI(rIaH6-W#AwBiNB2y9N&I&VYyH~qmFO1+7F(B#$MLQ%X*#}*|WK$)| zb{TP!z~7i^4(y5a*u=E5%k#&czL_KN*!JPi%co+VRS}v2w@e`1E5qr%MOaIi@1Oc{ z8etb2*CX1Bqnkf{O|HM!=Yh4iU)%6uVj%Y(r%L@DS4^@@lkawNLql6^kTSrIlXGx$4Y)-XKhHm3VckWte3irsOR*f3ogjkq{5D@_(xA zG;o~Xtgb+E7SkrqZ;)R#70y#mF?i)Xr?(S^@s>`Pq z_7^b{qUR6n_5k>?V$;2EQ}HPH#9x7rt;vN%>*EVfVb0ceq=XyasDtQO!TOt@lX8DJ zYO6jDBx9*dEm-R(V-5aSlK28>95#3TWf*6vsgbpnmJH)a;Oz8`4A9Jgyro_LjgA5o zRgno@Gq3q}&@ToAU}3kh?8Zx&_%o+BTI@O2hfgCg?8HJ&D)w*!z5HMmOmA0=mp*<7 zcP)12&3=5E#fS&I#p|uzZEGhk2S^=G7#h-4Ns3(eeF8?3CQ4^=P_e80X;%tQ420pSX4yh{rptTrRt^(e zFehCww~%6fGccL*PJ$jCS(~I;N%XRi2DxdCkrTQ3+yIUS!V|CTAcj^!0@)V6KxqB6 zA<~5t)^;Mg+eZ9c94q21R6Y{43|;=<){`H@DtcnD!(TgSGFsXe|5W~{C4=|CFE-@o zH*MqQ!8RWVH#m>i*mTTecTGV<40J_?gt|>m%(C{wekF~oz}F=Pa7wK4BsH&CEfZ^G znc>72!Hc1!C6Ei-&Mb@85BwFksKq#Vd7x?UdE5$vFvqv2Ul0-$7x0d6@R8njQtcP| z2#ndSYY&s5Vw!SifOo!7GPds^4WCHiLW&m&+DHvj=5B3x>$HGI5Bsq5{~oA#zmL!= zEYq_)=w@O?zB2}_NH;Sr$>m(`Xrnrz>aHY3Ux##kXtGc~dYe#`F)t(EHfGQ#t5~sm z2@y-{D1{3gBH1Y3=D|le7-f?kn4SO=ZtjBg?8&}N0W}O9

*Lp9z-c8{EH>-8`$ z>bDTme&L$FFL-=#tMq|xXO%DQQS&lW$miIxQK+IMz*7Xx5$()zvseZQ+BI>yxn3|4 zlz3EdVVx#kB0J?=UZ^5MNHVCn#m8I{!BSl|AJSSj?Q)RMqGGPN0EiLu!08VPzyTT9^@oN3gR3kqJaqyRr_Zzw!MJq{25YYEQH7MGI$(ANJn?P9@7Aq zEfiLPY-sRY==wTMti4bE$+ZZV^HviSJCK)_^8SmV; z1RCuV;5Bu9zF7kRc7bG{?G~JC!#qc< z0xJiPbc&hW^DGcPO)STaSmrEIpDO#}cg`nb)q1VazfKp!u5}}w=QGH zpRHR-kb~8@+ErAgSUehI4Zdrql^M9mBW!Gh0nX6#w9;_dVZy#HtHk=%Q)w8rb1gdE zc1Ew3lU9fu{p8T>ucO0_uVT#qmM9=rrK%TD!EBVqkS?g9yHf$kyAg{$m%8@qYwPWQ z1D^EL-;S@ZuiS}y$@#KC1O?ya#m*!yU!+Ldto9{87CicW3QSMo)&FE$_zOQ zF|$OAaC8B7i1Yp%a-6z4NB$A1`eedSSAXZdXC;QBzDGtiS9%~JQrE`KljL!Uf4>pc z)u+Um%C!rJ>(Tzp)9!1=Kn9)BqV^cjZ3JncdT<2(%X+8G#_I(b6@R1R2HeO(A~FCc zgzp{H8rWi}&~+nYF`ix7_c!&F#*=$H)*C!5rB#)i#I!i1S5-c3xX~juJ@OOR@w8xP6DUD;CV@#N<4O-i}_4=figAi!lVSW(%oH)I&`5S$;yzBxPZDENGlLe1%L~L z0bFF3l-GzB^AMn%07k9{s3f=58;l{cv5FGY!p^8C)Y~D*(R zC(W0;I@ku`UJOy5XG52)1ULsG36sg=%{-ZVwkAgD`}PF)BUKSqqdYK{WZ21gJwJlm zaQYl5~Ie~;e`dVUy=xXWp#QJ6pZwhE{XIALFyHHG1obfgSn+TfDVImdo^_0qOHph z=JmdQu>B5S=#UW<-pT1X_#Q;0TQL2ki^;>(Qu6~F$|&K8iu#T!C&LvJb?esYFTokk zurYMi9-2DA`g4-3_>!hdAZ^{*Ox6Hh4%j3d<d7k2} zo#Yk5$%byHO{froq_GVY`;Jp3$l zm$207U54;8j8f5B0;f=~0AfEEzorY#7XQPdrd_;f4aKxrBnwks^A9C!2 z!Hu$CwoHzP)M{tme8WR+_c2;WCy*%#oY^D#D9xWexs=@zWEQgPd=>RQ-Z)Ex@|ALs zE&FdtN|P1rJSxO|HnlT-Qqzo4aGiaEDe6SYo!Di}2VG3wj?N92aHC?!qHqgY0J>c2 z!fyK}bAv+kRuw7IzjG|&(bpR0HP3aolA2l{7%>1o!fn;IaBXr$Q>xwZfZy;iDOm&I z%)d`jEzXVk)dLX{>j7+aAEc%o=meZ+`Tf%e<);f1m4^M@PAD@=M$YJ=y>(~+wHbq*g1L99=y%9Mp+c)@c znPnC4pe)6pt~0g1LG^UYvkR0+swID1CSvQ7#9PrIw2O0sw>j=8D?Q~Va*BF>I!B&O;-EaP2O71 zWCF9uTF>vRSn4YC;RS>UTvrlOk!>R7ojoI%AOc}JPR0xA2XjyJ#3oL6gJ3K z^Ku;qqsAz&8iIm6Xg*f44%-kRxUFb;1rHz9C}PWJewgMx3VLPG&Sa~g#^vu*z%mw|^_QvVY_&VWe#-r6{X=JYS~;~xw%S_1TM8~+`e z={4@wZ^)ZJVkV>kSpI_&B>Uz!&JH3VWOCVlZv<*zbB*pU2)V?m#t_vw_r9#H+>MFf zHv${<;=&fR1S=`BXPRK+8;aZm!PmeD!HnSkkD-H6CS4vp?c;r}o9$Q61%m{eh%6?g zZ|(#nFYMOff4F3QiD1opieD6>o!3wMjDo*zP!>pR+ifqBN(O2{=;AsSw635r! z7F!3U1}V`jlUH+dF4bZ1rF74#J`jUpFryU9go!nK!sWOg8&-6YAl8EN8KTfgdn#^3 z+u$!JgU1uCWy2n(a2C`dMeoQ@mgMZsJlGAT@WRSYbpY>Q%bf%Xtw>rN^rOb&)9i}H zx1}k>>-D-8XrfAAM`PLvJ!=PsSjyw%zLO!gQjI=ELv4!7w6l>LL_>!m*acHjw{h`7C7-;*}p+Zt3e8wKgTEu@g13 z+k5+cp}K^6>)Qf^Y#XNlhizDm?D{7Ob%i{gW5^fe0rXc2DUg|ZhQ$W;q){F_w#DIa zy7DD9y#nIzq9R0UvN4QdSR&^R7k?XEt#VvPlf7xq@n?fMJogm?kI8Tu0}~2R5K6Io zUq%ykRS&4K0MF1NBc7=b+~*n;iXrUhl z_w0eDSjz*g$-!|*81VymPGXhbPR$n=m}vn(;SgOIFBqT9Tq8yU)V-m8SgQm1oT39u zZkDg<8tS#4PrD=-aH~;y-$Z(RVgnPc%=!<*WIA#kyHGQ|b!p+WI#V1>f_E!{l+d^p zuzDVLXN47d(S9noCP%Z;TUIN)3oW}7jtw4NNMUj_sLASrd?f<*YoJf>C6BJ|$t%;S zM;;R9^VJo_>{`IjPoQqD)VKx9lAm`j!j{N)1zPh~NH2s444uzlS!FNbEm^IpiP{(W z-y;jff*CVmNhjDThdP8Sg9?HI<92;YTSs&2FM=6tnt zg0A2-r}3DUbchQnWRB1Zem`sYlM;+NwBz!C%B7-=Z?RKdMmICpuq_M6vX%Ak)*nrz z#`%!_fh!n2A5Yz^w8I;jY6I`S73xuvr2a>Cq>hWG{m)p0cN)8KU>j|WQHDeUGi zA^Y(&13aHkBtSYk<8BUrf>{EhZXH5)e--VXT&dhfmm8Js9uswW31o5JB~PL z7$?fsyB#%-pg+Q;nMItu)fkX~{t~pE0e=#b$n>zQ7E_msB;a@7_U|r0EZv@a7GJdJ zN~?ZPsv{$jk(@e`ev}+wf+MX^*HVQLq@}v@ja{H3yp6)JaeE~Quz&+?Vb?N%ZF0Nj z3E5JdIVfOG^N zxNm{|ajM=_9Nb;tHhts-T+Fu6Dz_g&NT#j509PWlrw$WH=rrnY1` z0P-5#5wY(zVc6q=Ix+yDvMfPT(02rZf&TLKXgHhnPkk0`+w*yR;@ewiT zXpHI8(7yvW@mdE2m2fv7K*4O#KyMqKUfr}sj`jC^KNM&++W_zT4^{OzJ@AQE!}%w& z%>W9umUduW3FOwUF!N%wfG8jRv}vLE#O6nzolXoaG%FoN-YiA$I{*#{2^R+da%tNE zNak^Gy9Ob~#msXw>~9X-;K#j!#*1i(Ksn=c&KOejf<`4qIRq!sl*aW{P|FZwQWP0p zq&(CgWs?espQqUGZ=D|NM2@V)9g$h6Z50&l0NJ-ks*fUf&P$1Vz|J7WTFkNF0_D}j zztd7JETR$I9v?;Y?-^dEJz8t#6gW3 zg-TKE{2>T_TQ-bDB{Cc?v!E+5c1JxAM{ei*Cadr4Bqic~!@^P1Qe2>3X#69K{C%b7!Vnifi0>q!DD^e2c=s95Q`SOfCUrCvoSZW2* zb4%opynW}lZB&+_AN_j@PGoa0*$*-&ZIkMCdN?8zJ4-GH3b?i^+PyFhT8t%PHgd(_ ziV4y0bxK9d7|kUW2mH2JActe^=)yoPR~f`y3dte+CmFShS8alSSlkdH>8~F?j7mT( zNri=iT(=BzUe7=@cCkSE-l@>DPw*s~Hh&LAuDp=i1-(&Scc>2-ANu^C>gec8b;x4G z1wiP_XAI}fXg-xg z9|j87dUgoTy$HkTY-G3JVgwV)7ZBE^eYJ4J*3b+3jKmk!x@&(6Igyz52F=5NlE0^4QzaQmP8rXG3NvfJD%B00YwY#NceG`@)UAO z1eS0&xipMK7U+H(_uwSKkM-XXnC%5Wb_!}+L=bVUHsr9BsL%=(QF){i=O1)Sh%sZw znb!e@u1|~+tSWk^F4#D^MKQT?+huN?{FL%)J%&96o?_xrkJZ?x)#+_UJO-Xy7IeYA(QgeU=pnjC3sE0E67)%TZ_aDHDueT=Px$-> zcAM|;h{_liht*XgR+i}kb~^KJ^=7*jxmy$}@QN7KfcZl0pi2$;zlevt3rppJ^!|Y+ zv6Y`N)M9wOXiTVh({OGAS5qNsv1qS{qK4XrUHS3#z!DY4)pMg^moXYwzlB_FdI%GY zyam)Lk4|S|M;_eGrhP0O+4~v^eoCok96^)z*Tn!x?8Ao-TrJ(^nAq-U*8(*6O7{ok zgmTq#Y1PpWqlgOe+}}AoGIzNx1$p*8r;QvN0Eo}XV}!`*vU<$SLc$D~lTQaYE6}bg z?cqMuM`(fucElTKztE~VGqE=v!ECp*V|P;LJ>MM8i9<*SG^QLyJf-L z9f-@OXCYoTJX6yO?=WN*4z-02>h)o%rd2ZRfOCHse-P}w~uLeOJn>xS(gDZ%jr81 zMDGZJI6o7U8Du3N^%}>$K<)%H;;)~uqy1vGQ|mhkfZPN6kxX+ygT(>|atguIyNT!J z0+aX-GF7DlNoF+5HB&P^rG6@KL_iGoM$V_XCz^sV)1*a3acW4W7FZr^;|x3>bC>g zMQHV|FUcGWpWg~GguWWD3>NVwYWDs`aMPYlVMmIav+4a}z+kq;U~0IUG<*RzkB=Lz zzMOHI_RM|mU8Ld%<_gw&FVK@xRVzHeA!igB<64=gz!=ozoNplJ)yQJ+W#aZ!JFWAy zst&6Dg5)3M{^l?C!7+s5d@y~ldxj`8h(7r0&NT8bT~X57tO5s?QRfpqt7bOn>4BTH zYe;gtRT9Q&IZl>?TOpt(9gGY8Q7|V$%XJ~@kU)6`-*lnaQ_hnD%KOfiww`c05J@Dm z!>ZRG0;k~V6p=vSWZR#k<{_DZU;b!4Gs`J1^rsN_s8NjFzEe||DQ1vng@<-cZ_+qM z{Jbo@{8U%b-{%E9OvUBG`j<$6(>K!5gSn_+whB9H)R!-)z~|4LnIZx>2>X^dI|brp zCEUd*;q7Xx@%IB-QRjNrxH&N*K&T*WTbK*>56%2K(Ka0T7tJWY8ms6kMP({gRN89Q z)1)3y;kNGoRZnBd5_Rzs*HRDqBfpN)zvKTVq9GhWt+)lB6O*h+$MC1XUbm%u7y3F1 zy~od(4?F0XF;IB*S{4>nJI;bKi@xawF3p}I7dJGD+l2b$#bnSR0#FWTFK`nE5Zf$z zD|2nVvc(CX>Hm`JSn@0~A`I(~%bS|>d$0^H7e}4$a;+3@;Ta<8p(F{arB@I2t^59| zrN^@H9I^#AVKmjym`1nOwb%14i|n{Vf4#)c)kk*wWk z6BdC>_Es%T!I%N6A-eHS&u$IELY3jc>gI}<+~sL~H>MNG$a^A_Wv&Y|EJ;ON=N}c; zpfj|ZUeBp5(CZ@E^;-?$&_bUml6Md$)JpD3rw4=oNpk(WT<0Ie;h`$SGzxXf^!(2f z{@f!mN}d991hX4|LmeS=)$YiPLMTOek79ityFY^P6GR}v3Z(|0B=$Rbm~owkfm7^O4KzHas?V|+9eS8BcG50NmqxbR~gC>k@w4+d!3g2YEVK%g$lB()>9B8XOMt9b8@f;=jbXe zY99{Is53dWlBXQ}BZ_?*;T;v6^#r2M7yJv%zVW1_>nZPl?XqAn3y0Ph9a=nk(^vqy zU*@4}v|d>MwQ)zwzM)?*$H<1(ZFD5j5v&(0Plg(#J|ieYh-%KN3WOt4TQy&I)=q;k z*LpYK%36BXtWF>D=n4_uc99m2MS~N-aq)(keeUMVjPu=ski?YztNb6_p#%QO5D)eO zl#;+gM!A3sV*ZTM_aOq9f^l7b;|T&Ob2X~Muq9r1dEcx}k>WzWPRqDx*Dqa4wH<;d z<~bXx+odQQx~)9Vib3hU366`Fb~9SumUB#RvW_nmt05yqkTI1&RFF1r-MZCmy0@iw zcVt$cFTjVnl*5c&o!SK?$+)62WnrBg{T(4*pd6`Ac;VSchKE`nNhg+hBD$551p z-iV1av`8uV15cUz$vJk?fVzWvV%{(V!(XNyn`R0* zGH)A-OG#vvy)?d=@!x(@1Ta1g;U_ZT2-~nkx@#QAs-!zSG>c=d8xR4Y*DUem zI0%L)cT-MlEoc+Qg!Nc`0{xs~$VtyHyQXWu%=#jQrbBwp@y8bQEM^fW*MP|oBjtQo zvm@(tdxKNav~T8iCn@ z&OQrm6g$Cgw~Xpsu1%OKvV9s%Y`K7*70;I}4VL-RCu;@FLkr0$HFAXUfbhd|KK(-H zRjENWbPE4Yz|T3nIX(b05aD!0;TeZUva+vE-looiIZICK1Zy7FALJrHZk7expezgE z$&rrlS>CnMpEX%WCVC@En^%EInRh*|@RI%ZUq$4>qxDC3VNf}SW6u19 z4>;Y>e0>6uOqp!U9Cq0rU~x*aFPYg|W+P+8_CRMOoR5^yiwh8RJ<5|@YZicAl%+EK z!?U6#sN$#TA`wHE^CAU!F8UL0bVzCn?dOa8`wuZXo_<0z`e=c!=uSmL7(mRcaBCsi zK|R!5yX|d-G)mW@^Ri+lhuLNFl$zxf=O-dy$BG6MlLr??KszoYT9D)gV_^hDDT z%m|^hZGyK(Rz3|VjrmvoVAu5Dq^W>`p$#pe%>b<#H91p|y3B4@E*k@#;a>~9ss-j~ z8s>x1yM>>mMr+mbC7aF6)XjsL*SZtvmIwFnEoE43fE6lEdG)r)PV$jz$y0KHF`6=?2^;)!j7Qx&oMv~gJCbX5B1a#soZgxRwQI@=jH{VFmxnvD%*1O77TPei4; z(;>)m!HW14oz&C3b3YggVcu09Rj*UU;Y!JwDPKf8BB8bI<@f|KQ{I-MnN1|VLlDWY zC57Mxks%aFRH2oMY`@0TPUnm}wP-PBbWa&yFrz>>xk6_jAhV<5P5I2W|MQKDC}zt= zcr;G7poSi(L~i>B(xTIS!NLd*!bLN&*;uvoXh4lm5IH_}k}m=RW&B-cWETqG<-R*_qO9r|QP!>xdcJf5 zn<{>j;AnM8glPJ@qk137-21#@rSy&`@3Nk0xenj9JjK)J@4X_g$wOF!EV2Sy5=G&O zD}yxgz#TtK5ek9w)XB5>D|$q%8jy)9Cy5B(BB1Qu$EYiP_=d=t8r9F4YY9fPR0am+ z!L!j#?qm#@yF+YJ3P;%KGU9Xg&~2=Gs}_h(r&fSJ3>_sFUziU_DGS18O}{>LA_+$w zZZF44G3r<1z>@}niYx8&E|Gsq zq&dDUt;fzch1Qhdh78bfOc9zL;WODFu}V_pe7hsa$@3He!|BX|2^(d&`x12CrBHu?XyD&cnKo_X8OpKtQ$rr}|II8T;vnd= z==G~j2$k1UoLG6mogLNOvz25mggy>PFw0qHDtVPgM3t}DYOnds@shG5tBapTEJ5gY0?7$-qPmYhsXfD69VVSq3a~~7a3n~FIfCgnjsA^ zmw7Z-X6dI_e}?a$%~!P>E`$ZYcGcg830bI|p@%>V#(&iY0eo}8c3!&TAzBtEwxSQ# zNX3OgjRx<=GA+jZg@Gp)u8zaj#H?m5?Tdr%`_J4tZzPbale!wPK+1Fe{Ht0Jee_9yy}vPuy3jjPU-=r#AT!hlu0nX6>13&Z$pn!W zjYJ-)vjAA(dV)~&?6?jpj?>35H+Js86BeqweO~uIhoA$M(A+#i-)UkV98MV(FKtLM zXeftw7m`~X3%>jwbN98x*hvq`%y=Z-OY94zOUf61P z`k?t6V1{;9f4BcPWw!D2I9DEBNP|}aC$Zj0h~687hzA9RK*PE6 zLvjHKh|>#%hmA34(N|!RL3{U^up=X1Mb`@bWk02!pl(b_nH(44)!#yVc@v3AfHq80 z**OZf5T+uLYDVS3|B5}|J7@UM&0|okAxHxRXp*IwKJx;Srb9Xp%F5ImLbEY0!*M(QVJ}~<5rKLdP)D45T$Iv?mJ3rSZ2yWr_f_&D(+Mmu&?pz1KN{;;TBpm(>f8j`El;CksN_BDjPSi??&Ne6kQ} z`)JEg=CVe5!z>7;X~2Yl`SBf31-$)#$01e@4)s@f;9u8tU%2y(Q&Wu1oyGv4t3NgrH3C$uqeC$CnhVt z8A}DJVakC#yPB9>XgnuD*r%VAC+j_<`9Kt?wLkrPbPo>M5Os2JB{TP7+OBbqjZZts zWv_uf0>;uqpa{-X6yFr~=)k2OVNiAo*s>x{qy3pG{`201#Spg71`{o`~K-&Nl1Z;w>*;JIwwQjR6d0zaJQss%U zW~UX}!V3=WEL6{%L!oj1!$ad0a>}Qa+qCa~hQlVg(Morg!Lk;(3j&wRoQy7{y;Zvs zPIY|LxEfmxV4l+3pnR3SDUKix?7@r&PmnNE<*XsZlF5)%ENSQeq-E>zu4KeZQ#={5 z>z`DWZ}7Or2`OZHWv)_DT_CJ(p4#^F;MGYE1rP<7c3E_71wP&`3-v;~8xO=F*i)AS zLNb=h8;E#&JkSMNNS`t!d;m}vn45c^jMMIP)m-`id7wm(%G0M%w8ReyDaj93TpsUt zJad_i$RvjQIrD1Og%C|VSsPt|{s954yZizpsIO+2aD;b;?;cVHla6GIAt_5yHlzfB z!%7IpfK0*NK4<%eN@|AS&O+Fk|0Ydfj?`l@s$0-5}xe(Akhd)x&Ksz$qI zDI5H^&Z{>e=Q*|R@Eg(fuQ*k&PPsU$F*qal)5Nq@TC74o8+TY6~H&3M)+~la{Bgps{$@ zbDZ+{C^i8W`GE(?lMa`4)oBQtb*9Dep0^^-zdHn-UP2R8ez^-tEc|Q1=@XrUQD6jf zyR%eUwB}aBWy{@F-AkuNIU7+Gfh5Qea64Rs(oxfg8I-Y z_r#FK>!NWTTv=@{mCP~vbhrvmt4So3)SiSh0}J*c4R-Zy7SNfll|XKx4Hu13%#4|; zKnEkOX#+L>+Z6r8&_a-T_XQwEMGD-U<{0))`L>0dBv}Rqj5P!7xD>p{$GLiU5?9|~ zTu)Njj&;15M*lEc$$=wKv8=W4s8d-+3)&!vmt-T%|xIo1Z%v|9vlNwpw1sYbwL|B{!5UU*WE{5hoO876>S6Nl7?7a&2Sv= zumGAAdzC{S&+4|unmFnO=yl)Z^%8|RjFNr_6isqspV((SCjelxxrho>mXY3Y=O3vwBH`7u>kt${@c2TbKH;_sS|d6buo z8X6L3XiyK`M!mmIX&>FjgZx0&7A1?{{&WGag32$KpottkoD3lvG()-09st&(o_1UT zat)o^sD6ajT-s-)SZEKt1qg35 z0b$*#g-1a^E6K`PXP?i)!1u!bAge^`XP@TC{z7!ivb#she-*C zf~cB2TM`C%3_Q=sN!cg_FB6s*r29+T#BP6?G7=i|DY*CAJd`|g5#P70AfrLR2LD^E zIml|2^L9y~4pt6gCJm~kEKY)zv9pdVO}zF4m9*(EH;`T!=6OP06AKna&hi`yiJ8i_ zTqGQ!yZsAZb(&@<@W{V;$>l<19%*WgjDaUG2pZ( zU9N~~$b>S5PIkHIOQ9UP6=%8gQ=WRC-f6Tk=N|uMe|df~`Vh@5`A|eGa;^dE$1epa z=I98JF=K?q$cy(12hY5o+T5cv0@v=o&L#)+3Wtu$-1S@7u#w4(0qyZ1o6i{*^NNh}#Djyy}`+hx4TN8#1#DhOM zB`$}z+tZgc)5ejPm#c3I5nUAlftpH7CW)1<9CSN3tC0IcSgK6s`05RuyxLZPGMWw| z>sGk>siA#%;RygV%*6bb-f@s{m?CX;!nA=oSG*Zn^p@EqUz}+m&sN8IeJ0amJ1_$-L*?r7MUxz` z9*xulE|;UcIO*X5Y{g~+vI#$g@f8sj|C~W*Ps7z-bARp>4OkB0@tcxC;sR$8yZHC5 zT&V~)3ylEto(ayd`8=mIAanbFXcU%%H7RYuaS(Nv>vuWKfATyBwo^WpVYj~*Z z;vpYAcqZNZe__c`s0hH>pQp(=A@V>SIRa>*)K^zh$(SNYyB?R%I~0dj zm#CXhIE=C5c z0?84q zJLVBL5IW=%=8<6yexnEd<2BZjdsgsH(%b=%I%NP*g~i1Lypm<>7E>z`t5$epgVwgG zJ2PZ&wfs#s=CI{IVLuw7_AjM5hv4HFt^>0q``#}cuY^?VTE`wLrh>PhW}pkbr+SbY z7#9qD-p9&Vs^ktBkvPCtB#b^wx)wIznJjQxz!5j(H8Ll(~$gJ(Y*EY`>tXZthEf-&)j30gpj7otpg8_ zpR^?6wV0LLfnUay4ErA+i5w4})$-v`|B?itlpHDw2q4{CA?|?p!7WJS)eT@!98eo9 zP1ch9?7fG1V(&`=6b~bfx#R5`Ux93HVNfPH#Vriz0DWz5%%%Y*shH|Y>QKK6m!mk%=606!?}JAE(*l`zt|uph0G)^ zvBk!`W3H_N>6o1jqw{$+1E2ZjU<2BvUR4#QtQc2P2H5nLK4{3G$ zPB4IgYMNWPKq#8Ee|I?Bl=Ld2m+5avNIw{t4K}<>H)i7Fz{^0{H?UYFHZlDl#sohq zZR5TW6T%%gc>k1w03iZ9E|7@ulwnz};oN${J;d7gR9npV^$J((Yp)p_0N1$YS~-Vt~5;X$d9e=u|I=w8kg0edJF)_ zWwD8qR`K1O8A4QL$W1w(pj(#F>;)qnExi!%V1WrK2Fzc)-&PdGyg~-HWfddJ!Wg@* zZ8nK0fGRRqRQMe1giVAU{9%nNuK7Mk+-5AB4ieTc&?xK85`R~mfYt~#>=@3;FctQgF_)k`B=L(Y)bYgIPbNInvbzZRR4=yk>G2XDf2fR z>{0}fAC6Rna~V&v(?T*E5Q2wz186EI9XD2q(aru%a3E6*5pYDH5MQTaX>+3mF ze5S$F=YL+Um->WH>%b9Ezd%PA7UJEL(BTt>Qns92e^&;#Mw@IpZWYq;hXNNSq9&YO zGwG!E9IE5}wzxJ-Mu*mlN@oCyyxv^>5sw3x>T<#8d*Cm4_jvvk&^;=V7@-8~32ujO zuu`k?{SFs_K;AbYaTkEsznhF#9!ZIYTPqU8vgp#6+V-INSatXW_BKZRHy;R@Anq0cCBsHyj)RE<8+EQAw52%glie;tNhL{@L zLa|%J2ZB5D;`YKt4ZqJgEAxJ}_qM0lz|8C?p&}#EIhD{axR~hjL(t2vs;C%e_SH8Ifyu30@#O zZVM8G?ECLrH>N4ukX>%8S>FjO2k66<7Pgj+s%xk!0KoB-^!k{C8^u;9|85$ATi_Z3 zUHkwpXC$`l!F3*}N}>9>la$ZhYIBHhXy^%sgFp@~IW43IgYO@0PP1oF+R6pgo{=%1uEoF#$>#8qNf5Lit(~N+-bf z!|ph+&gfcW$7L5BIu@y&hr+%ZUW)>V{S3SJHsvxPc<44G2MKQ;rxfQw;H#ZL&WmN7 zk~APRr-lJdR|#UN)_3n=b@xCv`xGv{nYMuZz|`VmN+uo^&7L|%*+c+GK)Ao8a~kmo zE(dYIErK~rO`NsT!nOoNQ{|Q&f7w4NY3yFwlMg7`^LMplVQ(i0y8dw{K7)#AX8mg# zE(m5S$}%(k*ye5sSs??s^e^0A9NJ)B3ilPwSJqt|k)Jnc>mR+fi@%tn%faue9_q=$ z+m-VjMIxp$*gaZ%>xgJ-!GXhPpiokxxfyy>I$pJe!Afnw)=I2D5W;H^r`Y1gbl*4Fa2Fv2x4o8s~ z(0AIxs-}Dz!3QaDc7Fg%i~GC)SogCH>O^1UdEM zOm*&#wlN$|l!B@$=vR;_R2=q+XfF7-Fp5~2;wMiYc2Dj3R_Fo|E~^*@lc3{^km&jI zoPNF)B#Vb<*M7SNz0-s6XM!Tuz=~&@(4~>D!X(vD{Q-37H_vCrFZ6C9vG)REJ5@G< z{13M;Y%yU?-_b+6U(TKqX~!6OQV1Cj@pRd@Dp|hjrUWq*3ga#HeMU@9PjsQzQ1LrN z5F^qUXW{Oi^}M$v(N<>-X5$Ls#x+4$e}nv#Qnwmd=6R^Zmp(@5VBk>Vc$Wee9`=`dVHRvU1Au z(>4xzL3PJ#GG(ZIftFB(l;wi+#TaOa6pZQ=NA+5^iNN@!(-Yl?7ckMTemB2-RVs38 zAuwgKYxML61BeHVj)K0>W76z;xD9#*aCEn*^iHuNh<;K3GZ2;*wPK2Or`g@T6;@^< ztwE90w!HJle3ETIIyRnz>Z*hV>R)+V`xkf?#2%%r(`9L{cm)l9ZnRbB4lGC|s2Q{g zH?5&FmB$qj-R0~aaD#O}>wR^QKT*ojoPdyHv--g$70GzXQw*SVeBeGtZ+N73>d+Sc zG&o2kJi45fE3soAPVW-k+>a;8Z@~(4L}_Jfa}%o{(=hqWWYGH8t!M@VGHr*b2n4J-l;TUsQU+=re6{nsN3xUJ?fWxW;2r?|``bb+@9 z$0Zp8Ot`$GP$nIObor}M)1++rpaG*3s3l%PB$U>=Q@y|(ww9 zI^zpFv4gV(1U{o=Vlt5nO!Z0pc-`Z;X_REMr18eW!sL@FvLH@ixS0J^jM0%Hsb9z; zg;aqaa;Zh=A>%Gc$&()ehef74VE)FETm6gxm&mCV^^5k#kEa;*m;i6--###a7bq|( z`~dcGi&?q?`R6=Awg>3Y&0exH6W$FRV4W>jGY@I1!6u7^5By0Da-Jep>BlE+@GGX< z6NZQ_`qF88Et4DlT1|8SdwEs@JibCNiS?XH9ri<#Q1}E#OH(LXn=wMr47}2{D?*bH zqA59XitKw{MOU-^nF0DRJe`p)_@UJ>h-{OI;%DsvnY)UJNDX0mz-dJA9=}QT)G{(yJb&1H^~8@Qji#mzaG5 zru(D~{$`3Jt*S#AmXVbjhShI(!EDlLdlqN1z`pPftW-J!B~(ap+M7EEE~6nfC4;|^7!&Y)5K;`F) z_xcZfq{h^lIL|GI2-Rc+k+dH3F+F5jS zQVtYQ6gr5T2N1XsT0&izx(0<7VBCAQKuE&%giz8cO6V+X$LAIC!`HJD7+4=CubQD2 z_U3~p4<={r<7I}3#Ge{@oM!^jWKy{=<4XHYqC}Pv{Ss7W-jARe))W z_?J$D3Se3IKUv!i6NE-)e5rSnuZLI&9+@EuAEJ;gDP~{-Lx*eV8eQBFHhCGNM3vCK z?4e4TlpmzC$ zvqGx?cgyp&Cl9JeQ=3Qb*4A|+Yzk(DL!Vt0udWZ?v+tyhR2~$xlDmO3Edd~=qVLoI z8ZQB9^A-6ajF8p*N#yS_D;$Q<(#zVr(}Qu+l$y?uloLo=bs~8yP3XI_nDW z+OH)@07GAI&klP~yUmw&D+wnGR-+l*I{kGrI*AyTwK)ArDB`JKmwPt>2iZiJVokse zbmvbDt1mG*p;>{Qm#`U?z`3x=v-(fHgZI^ZK`y%jD>U>>h{M~>;FYlZcNVl$tLk^* zd&0MI;P4GuXa0aBuADs!1CnO@e<5-W@qxh-1i|2~$B^>qBAtv*>9AoFQCGM`kK9Kd zr_J}@1$}B`*QSN7p@fVrF~|Emon)y5C2OFFBXvQw5SFQ>Kt8j}rbT|DPQ!Idhh33` z6fR@|yNlV0l6}Joy18NJVrTzNWr=q0w9vR`(c&&f?M5utEq<@VhK`YrF1N|3g zmLc+vgF!7^)$Am_uUp&96NDoxk^;B5%rDs(&Cf|~)b~1}S9*cQLc z%Je&Af!sUV`A=%(nsFa8N*mDihJrUi$aqN0=Ig@dC32Tc;>Ce8`LTsVK; z+~2N1pc>=gaE|-dY}jzhZ(KhFwr=U9yo~qCZ=v=gi@FMYeZTxN6=RURn9v5r?)116 z=2Al>BOJo*wrmdKcrzFm`z>`kjEMeRVB?<#dQQ>`0RQ^7Nm~k0G2WnrPmg#4wXge9 z0l^5pk=7BD(h%(w9{*&5iyzj(X0W*03D{WR2Vj#w*dYLO(SgN>TYtd} z`74APo7tH5h!Xl%D(BSg>GZq;!^X)TF6}IQ^o|@y{K`kHDm8%9^UHy~W`TxJhb~*D1Idi-?>z(o-9p}9mZ#x26om~NtcO0ny{`k|r z5zm1h&VZ}-=vEhW{@LRpWAIQ!-O`s4B5Ah)C%U9&Hf2GcSSC3MGbW0GG6gXJ+n2Bo zM32g0s?!{HpWW! zRoXmoTcw^Op0j}eP(Oohgjs%j2xx_`=X{uyARNIfB`38wx5>sN7cqhcip6k*0z&StS7ErP!Gq-+(Bh)VsM(?^9Ea*FdvJui;W#-V_>m{jN{aApiAmf9~!F7Bo zUC+rA>2Eq-_hu%cj--Xd6&Gn`7;i36c&p8f@^|=eg51>#U6#E|^FR4}_z#_n32Mko zi^D!H!T6wZY-NN4b=}Jb<9d+ZNLp*`LdlrkK`N91ic=cZl{_D>qX_0Q=ZhxG9?b-e`q3^PxEkt~ZwwgmK+GdZXU zJ9XWPTQIpGIdJfN_LlOA%_nqG;&i3MdPiyPIG(JYo@7O87rd+wth}GN6kPyzGs93> z|D?F(T&x+WlBF$@JSX1*OuMTh_Aga0D)BABb54m+ODeFF901NK`jm-TvXPc56W)*` zsrNw1^qsqy2seSM-ARCyT1u-Fa!|F9GL8t%N100gboo_^Wa?hd zvwa*~O&tRFSnkv#Dd@=`KHiXqTfS^|jta3mUJ8G|E|WNmYhpE0RD6sZux`8(R^EX8vadM~Vwzdr{PZXYt0L$hVdwHB zJKqyQgZGu@`ob|TyumsHIg-}SbY8k$x5`G9%>Bao3UAMPZ*f3QbKA9q>}+TYj#)ZB z0_evm-`GR?m@b-g8|J5Z;$yjWQxpaW`W^Hlb>n~B?CR&f=*&?*gP$mlG2@EJH#d9! zc#_+X^)T%hU?xd#HCUN?1^#NV0;EFtfvWH9t3^MD(&*DG&#gZYRf35ye&I;t9}9nI z^7tTpR7b?>SBOd1*^&6!qAq*@6d}`rqE8K&Ol|n-H(*jd0obucX#XnKT4xU4umY9~ zy_SpC{{w^|Yg(u;kq_|mEw+?RxrpEJ%vcT_I_@P1OlGw}w-S`Qx}r?1VpCTa~NoGh_u2yUg9V^BZkHWnVmY5RkVo zwGSoFlpkw~6>UH z&RP$Jfx&Yb__shkBZ$}v^SWKONMh+%O<;^2*6Sw3qJB`UUz8-c2rDh=r`O0Hvq-k0 zFZX=zj-EpY;#VV=TzhMOEeCu|q?$^+d)Kb6?Bgr7QKZXEQ#->6{c>c_UpOIdVSp`w z&lhKwi5n86J`Gb?m)-IvxJ*(4$)8%K4PFj9ui2)=_7eikqB>yeuFFXPQkUnq5ite{ zA{vso$2$9bQtX~roiIkv10|KxZZI{=3np6rW*hV^%%z*S(jOW5+hOjLbn6!a?Eh ze+=;C`X~RKye-dPcNvt=KM|n;mlb3O8(~cjPIa7K0zD(%gCMM69)*JLKCxKlqm=xa zAgw8~S(%a-rTrgvY0KT%@imRSbyQ4ldN?n6UH9E0!6vJj1Q5X)(l`Z>>q|Sg!OVR_ zU!WB*nf0I1Y|m;R#FvMU10z`msemAJKfsT=JcdjYtC)yonKN}QBy<$;JsfpowqEuW zC;UKB{^XdKuFm}@=<9?N1;Z`U4T;-CH7j)$2=3?!1I&9L&_u%_<^C`(iZ&N$MvCas zlwp+yEdIE@kOyc3W2=)tr=-0Lt~G7fUp#7Qsoc(8ym}mF;cDIdjK3fwFf?a^46%&& zFh#P@>Crq(Oj+Z5|Ma79zBt_?>k~T{sJ)#ggEH?DDxks*48Bp^<7#d3$Oob50l0-u zhjkVf+wT+{_Jbvv;a6^6m%r$E(b=M z@Rce(&f*sf4dcw$r*8S!aKV`*XOMRL`wu90@5cIPH_#q0QN=o?`J_d}Qt|yhS}RNz z4tgceaB>LEmPn#v{rVxYoe&5`2}eSYC|5io7v>%#|4>~Qe%jyj#f9o7TjG?8g?fxGiztnWXjdgTwXlbrB9)KsG;bLHOpk!V;Stzz`O2P4O;O zBkvVV!xOq*Mb|qzSVfOf!ui_8CiggBfK~@d(FL5<-5JY_pf4GCG%9ofov^ zRYy4t!e49T;W0!HR|6NN{-aS=l+^Uta8$d@C=#$3|3&NvO#6!=ab&PgZpFg2r?l&* zkUJ5knPGqHB~JsZS}L0Wf*;|XC5$4Nc6neambNkl`td_6$`-YeA1DQer*JDDp-4R& z7pXWf?i=tMhK>`NbQ$ua)Urjbz$PU)Xo|NYNn=rCRhw}E%7McY8q;ift=vL3)UmC% zw&AMX0XmZh%01h^LtA|4w_r@O33H3lLo?CrF!K0SWcrz}^o%|m|7MDA)q07F5OX!I zIhR7riy>b6{Qnoa6g&MgR2UkV zJ8$J$b#G}2%X#q}VAAaGbQ8b_H>%521+%-1YLsv8Zh2w!1jhHsAYhCkRcX4!36tqF zI}ddTKG%kTd*5v3M&MJYxbf@O`r)kzF7U!dY?3T@ppp@Rf2I{q!m2jNsRQXKoAH-ZxtS zF7KJ)l#<`GWdKlSl!Hzi%d%7}7Mi+=e>wi0oNy-lf2vNIIa9rf=7PW@TB3x^ zi06bn%~?)R1n92wK2dHo;l8q;_*%=?nZW)eeke?w!;A%O?e{6BfMhF|g%|N0+5upO z0K~bGgIv7_9Vp@{p#7mUJ_{>E76iW{Iy5Pp-Bl11VNUKK@sw~6rjT20JKB|3x(?)y?0Jb(O_Su*NYMs&!qC&!CR_3#s)RyIfEdI4G_YW!+ z^#VN5(ToUZ?_9)gm#q3Va8NAm#Zc3nS?&seioq5S|7!6YZ>W}Av%rq;mB^J?@7SdL;-3o&xmkdr9tx*c zPfr)`de+4z#%rrBSqjN}8oAu0VWzDCjMA41Y5~$)Ug+J)yWU*;hnQi5f%7t_8U~@| zy{F_I%Eg7P&Eom%a}p%5H6~U?UBpv`PeA5NM{N!5Hw&K#NuUc4cA~o-3nJnYoK{tM zY*TewSa5GhY;;|}%JkDDDXof&I>#6X9qeGYl7L-J;gWQAheSX;E~%xmT?xSiYkI1z zXXPAZLr9DJX}7Ck;c0EvMrvnOxcW;U!c>JD0QaU{GuGX9cY_j8*Fa1an3ctFuaggA zq&b*j(Z|OXO31;o6~r2*BIMTIY;G>(<3P~Cyw7Wyy@=}jZyNX;)_r7@jp7CFR5u^2 ziOz}B%=(vUM)ac?cJnF?wu%E6TvwYFBos9Q8%QvH&9c&5ZXCnL@nVG7`+e1*k`I*x z9ya3zgJm1;uC2dt%d?lI+K@8Rljkqm&HZ#t1RMDl4hqd)Vc_xW(&W*8E1fE1Q!r== zZssT&FGEuO8>aaj@rEJ&Ik+|;?I{;UQ~;81WallUS}p#bOj<4@m)utpMse|Te+toq zE7`KmcpT=7ePD@a(0xw)LRMJ&5-;BqgLISmpUEDj`Kel|77Q|-yJSkGV!N6F76Yq4 z36OdcHDiEB{qWVBT!Vo2(_WsMO3JlF@5eXh=NwDYYk{Z}<;|orml~Yxe{c(DhYZl? zMySYxX!n)_vtTVwPPv^3f9;0J*)SJ*0DOShQ1p{NhZRov6{YH(LzX4Xifor5u(~a) zZL&;SBE9rW!3$yg2}+KCWJKh;KLUAGAFg#ET|tFkrY@ooa? z$df1yWeZaZ!?uij3qXd8OYxXqQ5|_G=-k%IiRou@=X>MVPvC(QnZo(7gq!PV)!keK zimJ@4I(qGd^N$8u&2djC!o05x2%I@c6{f*D4eF9k1nt=+$S*64O$PZfZRGo()30hI z5%h$o%hGSSbNJkNY;7B0g$eoC{!o2+6K!dm=F%kzCq)}^JLHEq*Ue@SRI|tFo;)n> zro>95Kdd@$t$wTlHyI*}qiOT<9|uob`$Lq|V~3c@8X)aSA0?S9fH5;1BFMx#+-*EO zn_R-CB3z3tLkH<(>;)6=KpVoGR8(;sKeT}S!&MrG>(Xb1aU)RbegoK`MA~RSIOX>M zGc8~YN#{=fSf2E(x^SC|CsC?(Rd3|uO2YpP!_SZ6kcr>`jY~P#grJ?HaL00sjo3y6 zviB82?uJr~f&|)Oy+9Nj>jtXW476S~dEU?i0!FEIi5YPJ#4(4gZ~D8CCscnKd=T); zT#Vr==U*7^YdI()PXzDIMqz3ic|_oyIjQXf-k#cEz@%?b;6*Gf#2~jPZHfkG;2ZiZ z>}9>G)PCj%rgZP|m;W_k@3zt|PpO85K&1`x9c_wvMHai9|LHISbY=lQ!bkc)qR@egtNtF38$9UlU=CEYg1G- zf249VN}dw1@%!-?i3^XdzCT7gLJUtFVQ7r%=a@_tK~NjtTM>c9e9&YIZv25p&I!St zwGInAT!-KsvE?L_C_NNX8a^jyk0u-(S)y0Y?Q6j}!u*nLa$dXt^~FD}p6idlJLAsj5`mUbbsp zk9QdE0z^&&hfq@_Gm+;N?zeO<7nC6k{jybTVpfF3KMS`!3zAF?DV9bSfT`OS*lLAH z`>sB2yx7NJQA-^%O33+JZKK@*bJ_KeCE3CQ4OhIT+I`5TQuY+ukngCi#09X1lXLNX zszk&Hu9oo@j-n60QD|lMjcYD|C=P7ykk?zjwmIUxJa)8kd0Qg^atg5oLbSc$WmHu) z87i5%xdrh?Nq4~k53x7HJ^E4uglUIE`4R;v4zzgH8t;g;biO1H!~dc;#-vh77$y`U zUrBsa%j1yZlm9}~J4zJL`!}|*@!fb@(^c=HjOxE48>UHaVk77Z^PqXkl8kWtW7iDq z;~;4N~k-s#;ua#B%^%S9Bb zoy{1Aby5+!@1iNtEjpXGpW#{Qts_=HDnQ)@g=v-P#R>SBx}ML+tYMLFYqiI zqe+hk!|NVH8Ju?CwBz(gYkG{i>^D98jNO(kpf%YIv}aF)0MdLabxby1H*=VpA5m-; zGeHl4wWf+8IcG8gv~caP4I2^1zh1h-%}cT1mbWXL_hNPcr>BxpoC3C6u(dR zI`cCE^hLcGx=2ZwTZjjCC~J}z_7|%=>&U3i=Yq~Qyu*STFcU+d>b&6F9*XXpQ++WEPZbcb*1 z!Y^5#+{rK(m@^m;)%3avj^=|kvzM2V=1@C$Ev`=wjKneG|EHN2ye+imr_1TvmXU~g zU-j~qiaVD1cT6{tGLQnUdN%?DwDG`=1Bs@?8Jpg+UJ`FAksXWD55WEjw^J$&@6*>7 z-1H3tT)9@8UqA?F`k>59_9$!|6{AW139pVhvuY*?utfN!TMUj#R0|}%l126KGr-U% z{@ONuHt+sne77YZWsz7(v+#@`&EZ9F!(3r6lH}~sR7+-q4*u>UY`P5z-xBVj5KOk@ zbYPYG9v8hj#SQ@yt3JO&f;^q>RC6l?V6}}eHGB5pbPv6PCBM-X)F(f<=02QmY7<5S zOpCJ{T`W=6fXsWoXi{no4o2t&EkNY;Yk|;HP#5zeD5+@_(hNmK!ZGCOuID;FH#leW zCr3Ww|6g3{dT1wz=k#?7+4hIyfS9mkT-%eJ?ZN^jW9a+`m^ZSq+1>=eq`)L0`ZoYK z--SM${n4}@HYSE-m9{KkO+Rjq=k1I44==P8YEOR9aHGo$&L?tP3TwjcaH+Dw zOvk8Bz6;qKL%c_=@eGbbcKiHBmb81Dz+o9v5HZ-V5cUnH(C)bdheuscja%YpyqFKl zs&!+r{F`AQk#HtW|7zCMNQUYLjG#;XP(3Quc-K%^nRVvB>j=$_Sx6luo87Ok-hXKi z8_!Pzcqvb-%cF~kI4CC@?tM|z7^|dl{%IRd?mJo)NXvG)95W=NwGzZ95+YRaprDo<@biF-gUzk3q^GI2K)>=IaSgE|*Q z=L90b{x?4CX;@a%ugn+n(pG)hW>lIJAqmESRUhcrVZ)KTa8(wKrfVnHlRe^N9^s*; zP8wGv)kB#u`&MkTS{bigTIdvGl}#6!%!JwO-VN)~4ZIK?X7&(gJO-YVeWAdt0T6=h z%ygkc!hp81ogq0+CVKch8c-2Jq1*wYfO}^<1UBETN{;ho? z!T&SF&Y8$O^ESAks07VyUTpN1XW@b&s4>;qJ>aOLPb=B=jH`CAll8GdTU`I4O#Ab^ zv_zZ&C~h3r*nR0Hx;;za(`bT4{w_-hT$QE9uTI zaTdhW-$sE+OAED(*mjey-dR zH+8DS!JH9KZ6gu{?m?0@2X!kK@75duvW_4j+EuF{(29fL#;jAicUDgeO?Mf6t2frd zD$#{Q{w?JSG#*fxRZ0)w!0c)Q8vskHLIeLPx&$*im2aC1pIosL6uBHBk z>#(T|$kY3yW=eAuGK=&o>-(fm$aIfjL8BuyN{4Vw-Ff2;8Jys&{cH6V;exz&ov4 zutyXek^d4WDGz;+DREel6~>Pj+Exo*V1LRsujsD4-Tc)A3Z4+naEdMhH^<9jOi)ST z_$Y)#x$&gd0fxDP`|>^^(yj>S0%%X1q9;LvDt&am#UKx#l-%L;Lq{qOD+)>=TB>u* zNX98aq!T*fC^w3=n=$z#`PO5@8ks%roXp7AifY+wYj251fPrc|?xqiWXX})pk zZlV*)C`jxOHMgu1)!$e|*n*6K-^Cd*zSYXHl7t||Ioa5QR^6TroWs|pT6lULD7kc( zJ3`eeL0U?=b%y$504;5<1Oq`4Qvye=;p0oWErd}*>GdEMYeZzQJ4Nxq9CX$qs!d@4 z+wLX?5vN5#$7!#zvwc<^R9=vr%RbP9tyEZ)diBo}?PkI6jiFY#t&c)tiCoiRmVbEA zXdh{I#aGfnfs#B8y=1!f=W3DPdwBK+8B8K6w#PMd&~rzZNAhtb#dGHT~@2tS_ zK`MhH_9zqV{b&_)vbo0xI3xi06qySO+c*k9A3lrzD5!(hk?L%ZHPGsg_!UPn04ef{+4<8(lH`6kthjt!(kVnyXDi{d6J+x#4 zywQ-fuSu~a`5hyyIDtiFjtd_1n<}<�$!oG8BzG=voweIET<9d&h?-(VM{i(DT;h z@jAKcy8H3l{h3}UwhgfQr5&DT)}(a2SUkRh5AqIy1g|ans6n&| zN|%^h^x@Ix#OIM1{+99`?En@ebUvcyU>$9x&BzG}H{^XeIMZ;y;NKBLFdit>7tZsf zv`6+fjmo3`BPSLcG;t7ck$*dF0qc|6W$)nu)h_f9mRkEH)mP1q~IqHcLqW2W_;cE~DaiHryztXKFU8IIv!@wHDRvD$gr8w7{QOipA}6Y2AZ05fz0u4`j)lV3K1}`4 zXn=dmA~2s&9^d!`)K=D0ii561(!MASJNz`H=kLGs;wyp7m5TX(;24z&_`_{lsH@-^ zuZJFHRf=KC3b|ZlS8r*4rI*IJd{e6sx*Z(*kJuAZM5cGsvC ze|e7^0oUaoL^8Yem*__)GTxrY6_Ije^9^ziE-J+2itSFF(JS4iaDDAGa!>6RzJ&97 z>D(>tIeZEMVYv{d;;PR3NP3=scDz0r2h|oBdGVbXDk~P-$CdR5yAtS@1~3WQ;M#fj z{g9W^Te5pfQO)m&q=`126t8ylP@gsWJeveY05n?-H{Jz^x@g< zz2I*n28dJeTh7oRS1j5A!%uUz2o=EkW6m`fxil%L=I`fws}U_nVYF!;xmpFN(wcah z>tgWdG9bdtR^ck0X*;DBc+xw_sgM03AVtVA@9Uz9nj;TBJw<%x`1n>w=xsz0$A6!W zUv#z+tL6DA7=DrmZ8);D!t5V2vnnr?6=jN}b6v*#V^uN}+3&}jLh>1KUxd0IuVS6H zMlI%Nx?S_^yVjqGODH}gxn*9KI&32XD_sPZ#L0Y{^8%5BCJ)ikLCmpkJ_4K@*~qjE z;^VN^C4GQ+krpB!bJ*mpWRieSdaE5|hS zq`MHM!s1H{#&iM5WnA>8Do?nVt;0S5f~xi)e7kC9*f?_>P)VW}9M^4}t`z+PI1}CB z%lzC>wWH16tyyCnEA0Qi?otvX3~EDLW&BX_;O~U%Jrzx{(f(A6#U$X`e9CKzrx@xQ zXmV6Hxe9UtC<&->5pO&nVo|>D=~AeB@;IW59d4}@WWq*B$s)sfdSdaobMUcLKz`yT zX8N<=s+Y(n$ltpNm>tE@{JX<*E6+$dSY>R+=_l2V_wJ=yoeJTLdaLh8k^#RyhF zD7oK~?Rrnrj8hz(GB)uDE0A|TQYje&O4#jN>4UX(i&(JwOupf=I{eI@oegE#HV8!seSj9FcUps9gK|B(ddB{k6!sk|x>EQi z0*&b=1!ZCY+Rw3DF-)&9QentX5bq6co~cNa!1%7_v_;j2=W?@u> z4Co+h#v1Vvl(9d+{EBz#Q(XiP$7jq=*PFXZnxh@~pxUWn!MWq>5q(WIF7y2W0fUnX zOE6sa6;e*$fh6Pz-+)Chp~;umCo)*B`Z#TCK%%7z#si`2L73XUE}wbsX}i)EIdWIy z-HilPdCmjK9EO_>-8@UN4G)W}OyFJOp3!wrvcNxS!#7OvstNX^m3NK=3D-dn84Sv2 zOtri0_WTJrgm-Dw(biOCAsCiU^Jg0&<;pMeALud3H6^M^@RcAvd+I1oh1-G!iraCW z;T^~XgQBx?@C|G?Z9LpsOageYq1Hra9|OlVC0E8F2?9$2go^c1k)+4M3CJm0+rS6+ ztu_&P;XFaJiR&Te<_vfl0)feZ0x%p)#M>I}AZ$#L{c{7{@~f&-t=aa-1B62oQh092 zQg#m&^*hpXdiT?B>KJ_rHSH3$n+p&xf)vUc9G6}=T5sy;rpRs*_#P6klTC9Zqx{Xp&NB}Sb+sh(`?KX(@rIUT# zD>4PB9IijuzM0PnqY{$ojJO*elp0xjyN8T5OiMteDA=#gqRiz$IzZ8&>J>=AZy_`q zn6wqwcV2TFGV<@XCuwg3`Op^u=PbTt>f{tf!oK|@JxdCWhKT)6>&@^9$`DoWWSO=H zxX|RRT%jEXZT71KA&B%{??9DJ;Nwj{`!zZC;!KAyTggnXNI5HEu_E^XY5*PzQ{CxZ z8T;3ibbb_bb5yCH=;x#oM%}DiSMaJCrCvwTuODK*X?e`T!y)l~a?leqJMwO9>8`lh zSs1qixFs5=L6{W;vSs~+XB)#eY2>8IcU>TdnCfA4ls1DJ3DpJFdvJFEg=W|==jw8Q zei-cw@4zFgcc{670GnVGcY6Jn)Wn_JVe-Z#3$sH(L5vl~nQA8AZ({D~_zx}|D^=6E z2XIz~aZl;n#%Ce(PG#FYGzrN=f#BiGOl=Mp?j&U(IS!YOcsAgpZnz*s+BIVnYH0HC ztT98i*L2ws4X!9YgKLdw%Qnl8m1hgcvx6o(?g9)zk6C*Fiw_3|Fsf#T45k&9W}&_$ zo`UAv5#VQj!-LQB737~sj71y?Phm!Y7?rg)F!Ej>BwRTi%+|v9BDZE;9N>tsh-Tm) zgG1Eh_TpGSA@xUX`G53H`HfTu;T0;9+1nm+5eG|fDs!p{MhOKft z=1)YLDIy59q6u&c11NU<-`$|vySllV9GxDg??Sy^M_s08DOfnT>pPinmHBrzf|Lw(h;xLkd6|_wUR33?YqSwwc?(rao)lVy9+ud&?WF zX<_RoWcJb;VfbfJurXV@oHk9YDtQ&0160U4_>!C%#G+B&pc%jrh49NMTo{w!*VwWo zTw4C+455+^p%!V(*JUESOR^*&U$TjL=oQu92h=?Gt09-b%c2Ux4;cyES=%(Uu+07j zTKQMj*z!dgWcMMtl1KosOUxO*=ccuqwXF)#tm%UU)^PpB_wVmfsqH51FI-gtG}T!* zyPyD!H$RF%PA{kw^7M)W2+=1k?eeLaWZvj7uk-``xcBG>YiJ4V!sGyJ>Y0j-1dc%8)@1 zaj;-g-gmpt>!?F`QJ?b()#C?k2ZN=dtVZIo2h_5@n^b4VmO}nV5HHU|!B9U2Q{BSf3hz zT{NS;lNGlSXpP3SG}Q}tw+c$1k|cm0gU1)gUI&w-Z$i62Il`(PD@QRdtuE>_n}}AB zy94BI9p6#wE0{Y(;0|G0cY4DDae96$l_-K3M;!Vh#K#D4*RaA}1Ne9CIc66_ev*t1 z4ZVMyR`5i<#Ek9OdMtiZ8?<6UU@uERvrs-fKJZZ#$1)Tk0+gmls)ye9Vp^w#H?oxD zw17td)+KMjMi$lp-_VL%t~roMhN|H(nve6dUL;s{`)x+eO7=RrVWb)a(Egpy-uZmP zC;4dmznd(&T6!&ZmZEEHR2i8?@5gQ;?r<5M-{o#`rG|~$D0~r4tGf*5Q%&wYx={q) zw?Cc_AixZ~dOy)Tf8KHY5VHow5f&g#q?sfn;Q_@I&Rn8jZQLe+gW8_ z>9r2SaFjWpQBiJA(LvW25~ju=0!uwXZfdgtRJpQV*iau1huGY$G|J!CCIu1*@BR`X z5slWxl_8c7A|N}@wq~V=l_k9+$Z;EoWjK`v-dF+?v}^gCwCK-{%FH_G|^m@OZ>-cX+71>+W%g1GejR9PA~OQf{cA1$4bh*1O*D^GMRT1(y{ zd$aN8mm2Zfr+~Cxp&1mC2dXLp3wPxDq$0IwrWKS8OtYZV1EMMamxjzIi7EL^wz!AG zgP5F#*u7y3psRymYeM;CIA z%QUgD%4nQjNv!vrLOj_Cb7Ih=w(0*Fn-ZZ8WI>lPSox;fA)lGPS7ct5abGwDj6PZJ zxHD4y9-zvVEga4MG~Rr23mnI$%gU#u(2kG<&V?dGVytz_-LYQi5UV(lP!?0jV}fT@ zZT6R7-izV^$L+yPH+tQw`HlzcZGX*n#y2IlyVlS%MP7Pq^Jl3W!c;iSl2jgM+=Vc^ zQNK&K84u&b4DJLf)v7(Nm*%XG6 z?ZI#i+q@}09cg(7e1_*{M#Sn4O{bIFoqEZ4<*+9EsrLO7Cq*7;0o(@LSn~Ss(m4tW z!p`;tZkZTRkpM3dkBVIboVAakom0`CAxN<|P+*_p~K$vI@j95eeZ40Zs-A!x%lHgBu^h*7ggseO$Ce$uR#6LDfaW ztxB{s4AWo^V{D^Q*e3~Q?OCAd_Z5ZpVPp{-CV+CR9ze5gIZ~t`j4xBmkrg_(A*45- zp9#xBH_sJ&%CbS0HFNg)(|N@mRG971*7;HcPHQn>x=I+pPx zP5Z4Wj}h=~;1;gc85I%bp5(0KJ&DV{EX;Ekc(%wG1cM&lPduWb(O3!A_dq4P;1h!p ztMAkNcPkrjFudCtvWcHxnK25IB8Mwd*ZXHmDu-h20VhF>3XN7b8N`@7k!x(XB-7ypRyO0 zX)FpAYqPEGtdNA}ycBo9RhSU|4d>OoQClqeX9?U>2};Y(hNHB=@2EL zjR}3f1Fo|PlwIp5yqPAFx->hnO1Z>PO}zUX9$uHd+I&1&7eBNU-O!RzYtA!Lt#kcS z+pc3(R#^ei1s`dQ5<`e8mTp%GbCMQXuREwVKI_qo-R`Vka;Mj!r3icF#SK%Nv(-8omfqQrV}m z-mhdZ>D`G}y!*rm#b(bTbKpvM7G`8TJ(qhYr{|AjAmD6nFV=`@6F1`np}N2)S87-@ zLT1d!=|Fpa^aaMSEm7_!prZyz!zVT#n<$#0lzDd1^SnJ^=C+i0oL%q904^N0I{7fm z(I0LQe5)5?V!z{3x_YUJ)TwBAA2CPe^^S`5pF8dYG6V${_F>QRgaySCXPQ_5zGNkZ zhO-#Jolxmea;NU11C2ij)Fb%dr~hY#D#Lw+tvD?0P>asRG)9Jf7%R~+%I4b_?9T~E z7;ye|6sy(PXp56+*bsjY3k@MNW8FNm+iU24)*{S@I69&`m^mf ztxi!yFzT=!-?!HsqRq&OP*Kb?Op+n$Cy8g7OBAmt*YgiJ918p)^TL&^1dIT>hEcyY z*7CWSc2Jsu%+FxKz5hgdC-&$LJu2%Zj@oHm_6WZN7P;O!>V36`#T|yj5^|`S|MP1a zcFBvn?{scQ;~N4{l)>y3ns8=z^%L91K79({v8c`nXf|?ORIei=*)svmW>4z5WO`bP zTTyo7G7sOZYRv=!&JEub|VcIcVK)x!(%P( zO{(2ZL_WeIhd4Gn_l9`g0X!$h%ROfxhmBQ9B>Bd@EGxvVy~;io`;OV}Ct`T$`VLPN zpxo6GhWy*ng{CSMdWnD#1fl8F)$APycU;ZdHeY&{=|v45HONXqRSQmtr~IUCISVo6 zhtnY?u=%IS+W^Qw3Nj!De7c6ur&1LBq$g%al5A$-`6YC6d>#`^Lr(D{v2wNmfF#_2 zv8OkF$}z_?gpbuxJWRxDmA ztVz7`IT)-EmM-L-hIF0ywNK{;0Oc+&_J$`rIX;vO~jmN~DRIhEje6zJVema;`^PoZ+)e zde-$V8_^m4=I-soc@0HU8$}Tib#@dZQR?Vm4ud?K^G)20-bXNALUBsdCALkg@@M2I zX8W58T!?~SGFrj4J{bdtmJR{&KMuf1X(l+u<;T*Emf#}?G=B9;9`*g?!&me2Y{|iE zTLqwUyl8`UnFEY|2gEaMeHT;+9iD3ZKfI{ov|p09weCH7{plf+bHjBCVmG}RO|1rt z5!dah%;K*wFuAb)mNkO?;hHy;&rQY0i731UIeXHF9~mnO#*((_)Kllx6h4TEHoSl8 zVg~Sq;_*sFA0+Vl4BHm&3ojKk@6r`RbBb*j4obd6s{EK!3Y(r#>0jVQQlPGY9X5)b9-QgynfdNTJ!nXFkNK4 z63d>$90%|sZnx?1u0(W{6ACGd&z1K40VyJ0ME>x3C{FrP5D-sgnhaE{IlF^`Y_U4b zqWgB6T^=gf)zdYYLMb$;7=+JT5*CY@qv)!ikZ;%LL)&yMcVcE2pyG=@ZoKA&2LhV7 z2@3E3|C}3F7_TSP9lDN%+hNg+mOM*LF+5)<6MoOW8CTe}+^f)+!rA@NSfYjACB1F~ zO)Bivq6BX$8;6$ZHu6nYF;1%vv+LO%QM_+vt02%8p3-ul{mEr=2Mhi$g&emg`#LeC z@Z4%=TpgeO>;g}YWLH&5$pVo+5pOzQrsy?g^mMLw$XgzM7Bm7i5VnZf;CSP~f*g(^ z7Xqkim|@YhE%WGV2%X%bRvg3z(7sv|U2z~gI=eT!?jEqL^}JbQ zDx23<1*LwJ+PoHK^kf@4mB&BdNh0|QLAcAsJSgo@bO7t|0%HA8o7-udj+~uwooN~h ze;X9?N?hGtaRiQ4>-laI6t=Jr%p=&r-TpQ{bT^czYz$_+(&lm!@9F;Py-_cO4t76c4oRm7(VhvnrnhFMQ8po$VQM$f9}CT%0%N1K z9c#>PgNb;T%HE19`iMZy{*5^!ZLSwx@C4W%*-04e9PZ$ch+v-^ZoM-Bc?{kENw4dQ zw>|X8S4TqLRdW!I5wk<3ICY{cG za+@l|4+&h)Zl_pjHJQ~!GMcFBzuLi-2P3Gf)6QMBkCG)D09STLEfUDJeop*2tuGa@ z7B_&hZoN$g&cu-L&BqAg5|_&$nyrZLstoWPD-)DS99OcGMwNjA#SEglw}?kN0g|}n zp8IcIA|BIe(H^(D$zu|rR2as;wi^lK^g`y84#7F1a5gXsFaMMy@r@?ib1jLVmLsnz zRia>=zp6YO5Kf}KCryH+@GRf2gm4w4L0W-$+KI#d(Iqj9OvNda+I02lf;Nygdk7 zyxA8%?Ha1Wik@AaE2qai@7uyH{)jj35UJ;whrafG9K9*d%m5_XP1b1JREu61i3wg) zP0A`48ZARM%R9?c1aP)_TiPg|BONIZ_BN!_A{fQiR}JHf3_ohF2AK8weX|5T+qJ0W zPj)Ib0-(OvyF?X+GcQ|D9&?K6+eOR0Km^09((YM{s58x_(d)sJrU1zeJ(BTU7DC1( zXppZ#RVQ^D6|mn^W24(;`cb8F;on*GX@5WX2<1ek2f6Qd*4|RF`evX=azoYMOtdS{ z73SXzrIf?5120e0yMbZj%Q`juH&FMnRn|u;DR%SWTeCfhy90*N5^j!7n??+Oz;u;aJ-j*I>d;-1BU8FWTR{0urxZM|8D*y6g}`E-B9iDoN2m zb&DY`s-?-!tuj_v2n4I#;YIyjPAsLqoC!<2`CC4T!M_j}*M~s!h^%IJ3Ef!=2^wlN z{Z+$!5#*eR&y}4PxK{MNwQ(Y|;o5U?B^vld&pnF!!H-1;zDWyqvLSQ{Y-Im6$VyO% zcUL(;7x>FG)1{5?97qBMm3S1j>hV~J&q&KV`LRIXqnY))8VsPL?A>U-d>`l(VJx9U z;RBZ^86^;=;cv<|cUG%F6jq9S*JC6$Vo>|WIBgjWvnQn9E?TdC2bz&p8t!!d5KP)8 zitq>8whV59V!p!#7j}?_8^IW5CZbV(xE-fG5XdxSQ`cBHnuVPm22H1|M3x-iJLT^0N1JS2jIY~uysgUh# zZt<)63fi1DUzBIB*;r{N(1#i_bJ9p1WF}GUY?PCd+y-A46*J65I1%6w0$>E3E9nuB z!qjAyFWVwYhuD8=Cr%R5BCIj@hHi!wnGJ{4U1u90M4*FkBb7n$D+1{|$(Z z&elYjeHDy(gPHq?hLco88bGLeuRI!wLc}M1&@3RLPt>u!q*0-FBDL_$61fgj9HV4u zNFCGvLQ8yRx)q8wYIbTd^UBD}Y{!d0pCl(E5tv=Cwv;&qicrv2{lT_zQ90arrp+Blb_4y>j=-Y-;rq+YydY{XhLH>4JsDuKGsI;);`W= zG2B-A2B;&?gio@KP=z$hdks3G1PYry;lP)pgz_XU$ryKuOWr3M-;TWkfJWVaoI(YU z9DI$Ry9prb{j%fR6cd=kQL|sND+5Kd_ICyFJ;9Lv4AQ2zz=o9-!w#r1>Ivd81U&U$ zX&1ozAIz5S9)(?g7m5{RU4$Kxh0^@_r=Zj;8NXSGy9N-34mnM zn_`sYq~5;WdaF=9VhoKG1j9DrCxocQyYp%Tmm#JwGEw{mnFA?_-f{c;kVJRW0P07Z zMhdJZ_gPO;JH3K46;4&8+q-(P;hs*>eoV z5r6t89Yofnv@}bN(GK2ng}42Z-lqJkp@*fLJexUl=+Y z0jF}^!~-zHo*)EIsD~zW4fE5`>5!m=e89vNr$H$CBj_&G3<~c31eu!jmdCypIbtJ= zY_Z&$;;UqW#a8-q3o?hh<0>Py{|$ts_?k~RKu$-U1X3H43e#Xxs>-7z4B^J%V9WN^ zl?Z87iRME=C@7zZpoGRDBq|BYjJ?*+A&g*a1<;Fv&iN* zob+aQ5f0|D-tpB1PnA_+M)}!i$oY7ebQ2mxMic~T+jF3I9J}H22OBo>qCt{%=$PH7 zv~VHqQZ$H7hHs`Ffbhi&5xMC0C%VBoW&`&WtbS;6R%LRNY?UJ>TL^b-KgprC1$YD; zPbGE~?Q@9r&2f$V`t)^J&ac4_i)X)BEr%s@5i>roOz!1N9vuHt5c7x01JQ0n**uyr z=qon0&RxjC;d z4Bep`slCltzd;&)SRYdJX?X>H?TN|gnJdj8E~F}50N74>yEP}5aBy_zoI|;Cj7F%m zNfqSLlNiuRh7DwX2*ghON72uL$nY9u?P?4jb_4}Xa_+1^d3dJovbZYOAODaWQp<- zLoo94xTh105t_=7vzsb-h#1TG#XBCmf%(b|P(@0iTvr5lv;I2|Bjtx*p2MaXrRJX2qgGp1NbTp*oY14Vdd(o%e^!2sla z1J0xTQViR7!shBboFC$xbt*g5qrec!^~aL`V2U&gA2*8tqewEjtKTQ?EK}hcn zQ`h0GYvPCICC@`S1R2LLJ@bd*9Q2hR3K{(Z^K7hCTx76~-x`-&Y}R!EcCr=k(nH0* z*-P3*4&-X5Qb!Cj;^V||@r<^Ixsu;p@02?ilo1KhLC_pf4vp_C$5-JV3B!?x5~6A~ z=uQyHpYc|vST})mQ~>3g1NK5_cT>*rod;CF-y{dcEoDo)bLm&+zJsWlMgX4=1B8|8 z*CTy>^;6DN_Db6{))7oCUtLv(*YlhjP~huZuBAn(jH5bZt~dfH?J%-L6e zmj{+J(!I17Z8T{}dzZMaZ008g1JNi1wVCn$zKJPGNl)sR&@#3$1Wb2u75M1qMZuk< zAx#he+EkmjK{lY?#Ng(~{GS???4tM&PEZjjI7KooAL~ov2~W75UAMb>UW0hYue!SX ztZ@{cboHfF*bI9*8LrrE6Vr5Tzv(oNCmK$8zG~no)BRHAwqtCumC6AN3U(0i$IcMT z@ON+$G$j98RNnO%OC=O7tZn%o-Be?;0#_OlZHSc6(HVSp$11ij+4YOP+!hPLobPatB5^rhrHl%QYtI``d{gd@%pDY162FnX|o-e{_%Iy_Amig zHMqAU9em#Jpb%4GRYs$LBSieXML8y0c#lrJU!MY(O$4I3ndn)Mh#}|)m4<=D5jR-% zqAu@X^)o0Su*coe5ZpZ8Cmkgnk3Ev4dcuaAZ^uNt&z3d zNMb^yi6qLC4M%ZI4MM4nKSt8~q{jyMR;+zK8Dp~*Tg zoKCe#s9X0qNLzb$YvL8khkk5x1kE`Rb2%;W=GZ{adZ%WwDw0eY*qy|8SZk3EYx8pf z4&yGaSCAyd1sAz$fyLR+tJbtP6|+UHOd~ABDE45GP7`< zI%Q@$CZz!Ida9c0v|!SEJp|nRmD$Q+1u_zB0%1mKY_nxl&MCfbpI`$i0mKeU#WE>i zy}F@y4!8u^><(khkfYNq_A`H&`5)y6&5%|#I&fVV+7-?)Af0u6xRegh3$ta|y1)em zTP+Y&eBFqY?k8uevM;>U0bkQK3BMiVWoGOdoS?reR_+I$KFd(0%7pK{Sv;y(2pMu5 zbG@Otoe8}ge#h42T}$Nc>6Oc=Vn9WF_6X~q2R$_iKTkm+G?P6mgh(yTU9ZJ)?=Vi# zR*wV%9m|At7HQuQL;PoWr^GkfLY1`;7`ULS?C4_9_z@#z4$3s77R01#TGK^cxbG1& zlbBIX9(LV6`F*IOY9yW;iaN2y7W=-ntW^U-kufthxmm%(8@kb$O$8l&mz8E-gs>kB z6%{;CyCR9_3bQ)sOXhb+4OeW9EC^;zEPQS>gDeIZ3|IYlW)}yl1PoPN-7U#7;1kf0^ z8Z(_T$&(`4%Gky#LKk5Or0|t3p~^ku7CKT{F`BPd6iY?p7kx;#<{B;BH?Za=sMzVu05E(DL1j?J?r5$s1d03XPS-DL~Xi)2(1-tXB`aU>Twa%|CO$_G{FVOuXwT9gMBzWelWtU;I4X*-y z^T2l#+ASzXZ&R*IQVIc_9M>6C=gF1`_Jsxn5eiSB<+{2YrWqROid(r`CqS4OE>KK@v7k_@mQOF?|-THv0 zh5Dw4UVo_%Dk7hJl6fvLgVe=R5jR``979JMhcsGy={9Apw-td=KlmY9dlXNW@B2Nm z9DN^%FLnEj*>1w6!!_+vbP?7R&J8`Pvl;7+t_TUEIY4?#ed zpv0VkB?<9wen1+Lol8_y@glLVOXF~g!aLhU+#i-4MEL9I5kV1@qy<^ga87JWG~t_3 z0SZL(V|dOa2_rvw8(T#0Ba9ew-YA>IoM0@g)!B#lt%;JbH*U(buOG<{mUp_+1pF}_ z36xiv2wgHPZn-DdNp*{fcgEpUd2Jwy=_MJU0K3XP4^4FmUhuUqjxosu>q}^Th6*>8 z2C*g$R#44k769K5;ePEUQ=cANOQH&S8@+wE9{u z>*Na~Nv&Rnc*(GP&a$09oZ90S6x(nVvy0qNkAbOH;ibeBdM7stfPE4Jq5Rkl^=8x&LZrt##dy8oSXEfA{0$oGI1@QAwUd7K6K1q5E6gZ4^Rx zHT*LVA(v`5(l8%CHC0m#ayw;^Qtyno&oRK-UE;>`t-|k-8k1)4HtfwM?M7onTK3uB zW()|6)cr?Sdc>WbUBCoJ8XND&ZWukkQNdRLMn0<<7PppT8*6$#euHG1i`r749iqLV zv)Ibvtm+Ot*zh1Ex?PGBja*HoQbDhg%R?*e79FHiKZA#M3#HzN53Vz2_FWa80$(AO zhzB+ogGgUI7m_WbCymmf^Qw1>u_fMb-^@N}?KgCA0F126i7m)K`yJ z0k}~q;l2wo-h5@()E>zAeBfb(c@YJS~l=j zk7wB;={mIc%!ygj5K<$qADnQSL*<;n$+8H@CO@TRfppQdZL7kDTYo!3)J)< zstWA`gGRQCmj}UvCZk~fNn1bLj1ggBd|?{p?UlJw73{A_#7#<0?RH!7mr0 zdPG{E@uSO%?91aoSOrPq3I@no1y9=t9xpG_(9zW;JKw%g96BN8N%x0paDONwn=Np7 z5M7&7#L4MAlu!*9rkD%}>T8Vxx6M&Wl@WMEyuiR64cF&1P&!8t7S!2*gRj!jl?}JQ zp`WExcJ{y5s~N=A8504XK4$Fd{S47pYL+M0Gh){mYT2eH$9Fy-TF=Of6L1Iz8xv_e zx*9^d$mm#LSNg?J>3p#OmB6^lc!8b`7wF3{T(F zYO`FRu9{D1wkn&^*3z^BcQux?>g$qdi?j2{M3&s&x;$qHO@MqujTHsun%(Hi3U1mjnos1ad2dO5ym zbe?_UIhtMp1-I52tGm*Z8~R5~90u7qZy=IcqD*Hy+|y?YH^MIEucd(a)I?qlMF~tF z9C%mw51O4ts3ffAuP|kv2o-NB%3oyiMdy7n57m+{5w!2HCv019! zxJ@3N?=CjV$uY>v5}6&Dr(X}YYEx!fJX-KPv0793n9ldcIeC}}usF9^7UpZ)2Th{S z8+1H){XD}4y3||V) zXppoGls;Dn38_(B+^d9u8K2km8-!tXra9SR)Nh*ue+B?_7gDBS7)gXK#h8ic@$7bw zZHkP9|3qu`$zQm?G2e+oZ z8$Qr2Mc&_{(&MM&fOj6RtZgQ`#1F|``Z_j(NZj`}3&>59_HOjd3d3YAUc*n+RTEq) z7alsvwHyncta&~~8D|7|&w_2O4eMO-ulH=Mou?_!N2d#RBeJVFGPI@K1M^dDg_ShH z8gxJ1JX>YMit%Mih@En)W9Y6%?!W0I43rK?1fb9v^$+p!N^+sh;(P11hBep;JcM8> z>eUZB8CU24x{BzY4V6hUYgj_8$?r)S$68cE#S#sMeXIFz7x77z-mbBKioO#!@$XA_ z5}(;eG{>tiCr%>12R<5d4g;VRExp;bt)angzGZwpC_QjL;(Mm0O--zE`1t;d24}2I zTAsQP2O!EHiUhbgS$Le+_+B^;I2WTYs%iz{8Fs%e!%k3`B&*R**gsbXi#-zaPXR<7 zHgsm1!(CRN3}dopPda6CE)m6y@Om|DOtAR;c)Y*QNy@VS_sd@*0x)CIs#||4=4J{h z+~|ZN@bnQ}y1zxj3IXYNidtu#1}}26g)x2~YN5hTJ6KxNVEDt>vCCm5DPQ}E*e9wI z46PSgw03}cM`*~zE5tc#}s1;ceMxD3Zr*k!g)!8<&ik~F439jK4Smt zP@YP75*;p^s>n!A0F5i?nNFD1mBGZNf(Pt0BxlaTs=xMK-rRg_|L~9JB!Xr|by1Vi zb05Fqy>8F71-#dNx&IJL`6liCD-OUs9>4yN6;mdT#ZLO_N&I0%FZNQTqJ7v8bY_+Y z&OYif7;^H<4`$Q|Y)8P*?7t#gj*2t3hNdcuzgF4R&^Ai57Y3!Db4riZ@|=-0WT+W_ zFTt{9Uvo7G81uae<2p&}1q(lzUCi|hOltW!H_V6_cP~MbfNvuK6md&QLtUI92(?WT z9nt#SjjcPa@tSazEQdMe4Mc%Q4*vb$2^;Il8Fs6gOvSnU*}s)-L0g4bq6D4Cy7c!7 zNf|8I=BnF0BklvUvOOZKKws9*G%%YQ)G`DHLy*^yXfLwWQ>}N`5q=t#kpjz%Q8&|i zoi*597Ug$O8Gz6ZX5cl46nu5A5-vPRC~sc6f7mexucUkdO02 zw|1=bZp}jMS&l*a89+*zzjKB9Js$v&?mYJq0l{PiPWI6Gx=Wl=hqjq81?u51aLB=r z_BcbH8lO&vmOTCuRaNVw4p5#jQ8DvZ19Z;|6wa;5ugUXmi>cGY)r~v>R{XiC91|FN zEyHqd9(g$pkcRZNYe1il9b)lCAv!tA;&tJdx58b2Y({l@8Pz%CM8gZo&D)lO%c)3B z@9Bz&2|c9XnoRXV!7Iu)9F^^bcM-O5yX9j?B4yLEr!g~&9>Ctk+6ns(P$&KW5_N^j z>gDj@j6}Bo?(ev`v(K>`;19tS)xh-BA<8yL4im!?$m7+98*q*r3`0qR?~&0Qo;+3Q z8bc^5t{Ad~Al&uBY}g!gC=hClOI|k`Y^9@J9?lkP?op!s{LhSFBnSH06vzfH68?Dj zy)Oy3+C~f%p5`E0**?2#Ksi6%5OrF~9(>mAfq5^2%zjrqUQN1M*5yg42j4D>rJDcy z2}Yj|U818nLQ%Yl3@euZi1C%Vgs-uoP9!E3mCG$4dZjK3M=QX%h7?5>nb#qcc1_aI>n&6@dFHr%|o& zX#&z<^iP+Rk2_N3zEcVIAg1zsCq^y)71n(qIca!+3**JHay`0v%+>Vlk`s1-jnb$6?Pf+F`#i>}LGhc7c8MoJyb-{U)9jBc8 zi*lsUmmg=pH3~Sht+a@R#S>Erb#Q-I+%NKo`6Rac}brZ(%$LVK;3vzX}>01LrKgY`kD1+Z_Pz8J^;?TSS! z`y_LJn3NS&K2gf*3o|)`F_?S4l`1dons#G0A|+CDAIi(_-u?WrScD|v9|Q;U?%&Er zdHrv{voS8)ox*r>*O~ywa9oE^dRxVM2JjSSR_Mg18#dwfO1|}>J(kAIR$A?eef8KU z3TfgB1RKJm;*9bU%}@NOAbG2IABn@SYLX-v>8Z3+zrUJV2jJ;p{eBQ}qdV&b*8#T) zvi)o|W}*7OLG~`EgZeF>mGS92(&8> z-t-GguY!$%^YM+|U1`&MjKJ2jcX2Kux_L@t0_!(*Pj?}fJNH#5;F+rwz4T*29!LX! zOl&UgNAcXc8LWs$SFc#!YjB$8A+|*X)S=V97_q%)Z#=RX#yT8)6(^dSZVs}hD1J+N z*VcY6O_xE$Zxa!}gFBBZ!F$NXB7u2_c!iX4+;)-@peMviNS&Hn1sA9CI?9UqNL;l+ z78|0b6p1r?$Qe&~$W;0Pt`Y-JjI)N4vetF(?& zUUHnH032;DTIFhw9g}%o0Q$pypM%({q{M z!yepE_`aNkd*F6k?>_*G44`gyl0Zi*IK4+6$v@5Lxk^d5xXhs+i}0?8jWqEQWwMoV>(y!o4|pYo=J%yPVX6^F59`(ErSvpC0E4hM z8v|V;v(UQ(j63N{+)X{37i5r#`*~^E-L(I_e2)67A0h1GDRx0W&#h-Q4CU}gLuQ!t zi>&A+;R0juUg1`J5wc_fWd!6cM~;~xePWV!>;`usj-*-huX@P(bR@F00Sn&s+h~P` zj5Hb_i)3C&l!%QdCqmcq0U>7=HNN1s8SWswWCv!J#%GYv83}4A$-QrJq}4!v95v(0 z9sw=$8IEGy6w3IL9{_{EkBojL@cCnh9*FEHCQ{*UVRReg2*Gj@IRo8`-i<~STxgE~ zzjiC-v0IcckH_T(f7QA^0trWUbDT_)@@u4Y4lJe<8uyk)&wggT>9@mMr{bh`7oozb zqHBeg2sYkL!V{;?7ECiE7Or53mYIE9O-|oSBT+!Kas;Jjp0R52?4K!;LDz#_J~~}0 zZ0wUsZbSm@3`lC8uV|nNf5m6lECr*xvn_>uUAF&ss`#WkVh!(S1EgjXTHi(fJ!@n~ z)Jzd_KzWi1KHEY}G&3mSXsC;>A2*PL%eXYb%X6OlQ!xZBFz5sjNLPZ^0{%^NmgySz0ZFusumX=& z7u4X9s2ZS^ViWW)$S4Z^%-pvks{lr)3j4?*jQWKS>mK@ML0+dAZWQ|p5y=DxZgov= z1Dbg%6#Gr!vdeD7jGceOSQa*r4p0_~=^7oYngfh6H_4s$8tN6SI+0H4&X`r^HH_}0 z1~xmd(m(45shzga44Wm$2@B;fos)SeO40a7Nr^EbROUCT6gkR=`i!mW*nna&0^ySe zd8Dem=v5;ie8Bj_zLDEbrzns;AH~Q@``Xqw5&YVe5#FJ;yt^e?ftJ(WR=iar|@L06%8vNf8_#!>j7-zIQ z`4buKOPHJ9Yr2&wX+N0a|A3oYXp8C_FN!2jLSvgtEcauC_yU0dxnbXwaGJR_0z zBy_Vr+J2iY8)g(Mc-j{RJ@e4JCO8kZTLT!S;}{G}6jsx)+hE4Ar&Paaw~3x6xS~@Z zaQw0fT>mZpDDQe+7p#b+>0Lw!Xo5O^;T(j~s; zORVrCy!%FdZ$#?*^Xi{(xPD7sKl_J<9yRd6nb0<=p$>LguM~z)XbLyzNIgKhZjh~N z4K6Y00&T;yd*z6*C%keOltCJwn|~v_D=rz|ry`Hk$xZNx5|S2$@PX3W#E8kL2Zp6g zuah30_I7I6et&2Y{fe>$4ye;yOG;W#$R5zALk$0}g{iB$2XN>YM?#*>NETk+BVOtI zhwOwmr&zDSp8*F9{{2C+?Kk&(Kc5S(*{lI;A;u(O;m+KwGzR2JUB~kpy#1mHA-Gux zu2zVZiSQQM6#t-qQ>C3-hmsB3nHO?Akl)=nc8S}U;VZMGea0Z?Bdvd8u#LPbM{%po z`F~v5*4;iA&-^*qEXdpBf1bUl5itNg;EqU1d<96fy)w)Q^Q6a1Pzcc_Z9G3)NB@F( z4ywBy`d}-co^V;gAO0YCU2Ig){uehn#lb1ros!#90T2WOyFtAsWUKlcPmF}xzlOVk z$W|!Kfsgu;x*$h|6)8fbtMt?@hqm}2sFwQvOTJjkoo)e6z`eZS?qaHp7CU}9LmMz#7#$AAXjYSOhi)} zR|9V;sp-Po5L=e)ux!yPX9I~%)Qtetm)ZgJ=HZpEfQ@HpW{uUY3rbf3AU>q9U_c>J zCcu66xs1n6%{rkZ)q3+utvB~Q7KeXr{V3CeThxBH6qA!3?wlTlBxWHwUy+0*@Xls< z844kKV07lr7&1+*BMOWB$}wMnT)0BTQE)FrN^?O$AfV#rROqAy3z}GvE%usu5N3>Z z&T^1^w;$DR2I`<;6Ri*lYwmK7*B2*nP|V=W1&=W0zXOENf>oqA{RHrO6#pKC+Ze?*xT4#5edt? zc# z&q01>7CW)%2q@Od=bBG)4{s}~bk#~g5oxsr{^ta4orG9}Smwz7gls~#3cJkk6fL$- z&Z}6KI$^p~pGP%U6iIV;K_gNjm4eo->zo<5)xa>;3`7(?SU#MD=HWPvVI&5 zf@vNT>~|RhWj!lcYBg7vv-rx2d>u z#-?O5Y2S>HA$8m0)mpGeuYy*3+D8Q$kWONNkg z04SODF&})K$aOx~?k8EQjrC6x=m$g@{bHs;WscsQ6l1AK5tP^+c!EmavBsZ9TNKa1 zqOE^3H#dxh;M5cX8&>NLQ?cS&bt}O>@1~gwF*OibG%mYiNQ8h#zMqJ(1kzRB5gW#D z+6{ImiSr#!9-p|2?0zJk7^8OUH0(Su7eLGw}I6T9H$_CvKgx*6JPo@e<@)2GbZ|Wo(Oah!fe|OgQF3YO*IqDWeFh z-dF2K!ww}LH1iHnSsj`(sB6Uwt0h3L=*mPni` z7Yfs%x61%tcc>bH`FKS;c%Q627fUAXarVRBM@|p!#}Y2QHeBCdQ(;1Q?sapjJ#E?i z9^KiiY`)La^4l#uezfL7yevGmaae8vPG*`PWfaz>8H3^57WaWd8rdeRc+dQy64@9zBZSL1l*$jee87p(*Y;=YQYxZwM*_$Pr#4>CmNNKaU4 zVpcUJnS_$R7l@1_lda{-o2ZgAF%IFU9DXAvfR#HW#UTThk3j2Q1?EsREz_e?*nu_P zw&|S@1h&%7pQdrob;S$^-|R-hY#bB4n&wvEYeMkS+bM2b5Qp`MxU3{_J?)Lw0_I;% zA3vC<^&y3;&k}=eCa9CT4*j_GOH(!n+34koECX3JZO6ua=1@LbcK*w(vKKI$*C|_9}GA3 z&X!HxBL*J6DD8dhbHEiY$#6XC7lbto!a&^xb+y}HZhxbP1C!!AuRqgxQuDRNwx`@1 zuhytTqbGVsv_#w^=pUzC1f!@2EzRWc%`S;ew9YyxqDTF?K#AN3?R?A;^LW0Q-N@fJHA=b%Tx69iG!`5 zo!|7FpEk=W5LUYT32Kv$?3K4!j~vK^LGK0|qEvJ;R%2!g!Wa!+vf*Nk zBwG~J#HIy)fcj};1Y=^rR0u(&93b1@1rN0oj9o)#6*EChN<(tHXYM{f5-g$+DfH$? zp;1QnB4daWH9qYK4AIF7YHHr0C>_!$++n}$URWq^Kkwc`0)PR`r<@}n7RZasGkpZH zAToIUY4?{EW zFpNwv5y7zTy7jD*L???hMwjpije`A!#m91hB(aP-?oCCV1iE6XJQf*)D$;AFR)(#; z_i{R#WPvRV`ai<)C``-~&FrM7(PWsYH3A{BM6?IkEw0Mwba zuA$A|e1iLm)Z&4njO0vfk8<2v2!Om%!9y);rKirow100{hK{VL2% zkJIEW7IW4jVx6#3*N?tpR!B~m_ZsIlL&%9wgD(e^D(Xb$4O&_bt-!`884gr;AOzpZt)eR zXe4DCmUUkeWtuaVTx%Dnq(G0#BjN&3PLOn3yM)tO?}%V*Q!`&53It44jp>BxjIXlbkDp5n}@v0x`rqmB`_SbHeXB*f=1+}l#5s8c;}o(!OorAb!G{QG@g>!>w8 z4537#6@y5e!WMvB((YIGRVaf+YK`X|68&@PG!O(5{MN~{2rq@NMb0tqBu0vRxkeV( zMWun-n=y4g*|6zCOiTfj6lY05FR-=Ac2xYhu7vsY_+sk0KUzbf{m8a@1sgc;xIsTBx9>IIE+a_5a#gA7T2Qr@*XE7?2vT*zC4%ly=}gw@=p% zvfJ?DYopms0$I-PW37Pn`3qwQcG{h1B-y4-reDF438au#{oH-{1&m*HIRi;xcMk^G zvNCh|VerV2DMU-S_4E}zKEiKjAci$$@^4K&n&^%yQqgcQ<0+C2UCXgKr!{D$p)~Zp z1gZ}SvTKg#3zK!nt&3Cx>-{OUGdl5!bc|eUXBF`R1V6M>Gp2}Mh#^YXoK06SahIHrF@2W(kBo7V6{8!ioV;KflLm6{>J;Y~A463MX)lQi9qc zYd7N?&lzEQxx}*B{U%@oUKCq7O2%?sAm=f?{V^+X5I{5|smn^+PFEuSXdnkJ-)gP5 z#j8tWAG9&wnHvm~g<9mX%7=D~OqgpyH)i3vgg(-*OA>Gw!>%KrC&GOSzq z2^E|^;{-)72z7>-xqK26L}MB)*5jG;SYr4~GdJO~OhM62F4yc68L02sHkN60ZcSo< zh0Kz>VjqIq?l6Tc$8|??q`@uoBDvLw`{DVWcC-?o{K@9r$n(NOWTJv^1Z;C)=x23g zBL2bM5Et_;kZwI$n3NedEGg!F9lUh)0>*Wwk=3B7B7-@}l>U~bkMYO_EaAEzb}c>N z9Q?A2H6QQfru|2e0~luD&Rl9|^dE~_yvlV>##*Tr(~WA7{xddC}5Tv zndZ|^@pnEVOC-wJ_On`QMki8=?_;if&@y?tC+_%=4=n;x7m4^PRinPm|&|63ewRccdh3q-y;mZkn+hIk8UkBV$E z9w*_R_s(H?K9c{IM%>KGDAl??o^Z@wnyEMY43)>}RCtl`V_3DIYPd}o PrwOY*-g1h^plsaB|2yQh literal 0 HcmV?d00001 diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index 0eaab55429..bf8e9fb353 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -1,6 +1,6 @@ -import type { FileSystem } from '@aries-framework/core' +import type { FileSystem, DownloadToFileOptions } from '@aries-framework/core' -import { getDirFromFilePath } from '@aries-framework/core' +import { TypedArrayEncoder, AriesFrameworkError, getDirFromFilePath, Buffer } from '@aries-framework/core' import * as RNFS from 'react-native-fs' export class ReactNativeFileSystem implements FileSystem { @@ -36,7 +36,7 @@ export class ReactNativeFileSystem implements FileSystem { return RNFS.readFile(path, 'utf8') } - public async downloadToFile(url: string, path: string) { + public async downloadToFile(url: string, path: string, options?: DownloadToFileOptions) { // Make sure parent directories exist await RNFS.mkdir(getDirFromFilePath(path)) @@ -46,5 +46,21 @@ export class ReactNativeFileSystem implements FileSystem { }) await promise + + if (options?.verifyHash) { + // RNFS returns hash as HEX + const fileHash = await RNFS.hash(path, options.verifyHash.algorithm) + const fileHashBuffer = Buffer.from(fileHash, 'hex') + + // If hash doesn't match, remove file and throw error + if (fileHashBuffer.compare(options.verifyHash.hash) !== 0) { + await RNFS.unlink(path) + throw new AriesFrameworkError( + `Hash of downloaded file does not match expected hash. Expected: ${TypedArrayEncoder.toBase58( + options.verifyHash.hash + )}, Actual: ${TypedArrayEncoder.toBase58(fileHashBuffer)}` + ) + } + } } } From b570e0f923fc46adef3ce20ee76a683a867b85f4 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Mon, 13 Feb 2023 19:49:21 +0100 Subject: [PATCH 040/139] fix(indy-vdr): export relevant packages from root (#1291) Signed-off-by: Karim Stekelenburg --- packages/indy-vdr/src/IndyVdrModule.ts | 11 +++++++++++ packages/indy-vdr/src/anoncreds/index.ts | 1 + packages/indy-vdr/src/index.ts | 10 +++------- packages/indy-vdr/tests/setup.ts | 2 ++ 4 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 packages/indy-vdr/src/anoncreds/index.ts diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts index 6150435c51..cd6b7244bf 100644 --- a/packages/indy-vdr/src/IndyVdrModule.ts +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -15,6 +15,17 @@ export class IndyVdrModule implements Module { } public register(dependencyManager: DependencyManager) { + try { + // eslint-disable-next-line import/no-extraneous-dependencies + require('@hyperledger/indy-vdr-nodejs') + } catch (error) { + try { + require('@hyperledger/indy-vdr-react-native') + } catch (error) { + throw new Error('Error registering bindings for Indy VDR') + } + } + // Config dependencyManager.registerInstance(IndyVdrModuleConfig, this.config) diff --git a/packages/indy-vdr/src/anoncreds/index.ts b/packages/indy-vdr/src/anoncreds/index.ts new file mode 100644 index 0000000000..c1e469b307 --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/index.ts @@ -0,0 +1 @@ +export * from './IndyVdrAnonCredsRegistry' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index 8278d55827..be45d47b96 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -1,8 +1,4 @@ export { IndyVdrSovDidResolver } from './dids' - -try { - // eslint-disable-next-line import/no-extraneous-dependencies - require('@hyperledger/indy-vdr-nodejs') -} catch (error) { - throw new Error('Error registering nodejs bindings for Indy VDR') -} +export * from './IndyVdrModule' +export * from './IndyVdrModuleConfig' +export * from './anoncreds' diff --git a/packages/indy-vdr/tests/setup.ts b/packages/indy-vdr/tests/setup.ts index d69181fd10..a570fb8396 100644 --- a/packages/indy-vdr/tests/setup.ts +++ b/packages/indy-vdr/tests/setup.ts @@ -1,4 +1,6 @@ // Needed to register indy-vdr node bindings import '../src/index' +require('@hyperledger/indy-vdr-nodejs') + jest.setTimeout(60000) From c63350c855d7c8ae7c3e50a9a9659d69ad0e9fd7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 13 Feb 2023 20:58:46 +0100 Subject: [PATCH 041/139] test: add anoncreds restriction test (#1294) Signed-off-by: Timo Glastra --- .../src/formats/AnonCredsProofFormat.ts | 6 +- .../formats/LegacyIndyProofFormatService.ts | 6 +- .../src/models/AnonCredsProofRequest.ts | 3 +- .../src/models/AnonCredsRequestedAttribute.ts | 11 +++- .../src/models/AnonCredsRequestedPredicate.ts | 4 +- .../src/models/AnonCredsRestriction.ts | 19 +++++- .../__tests__/AnonCredsRestriction.test.ts | 65 +++++++++++++++++++ 7 files changed, 102 insertions(+), 12 deletions(-) diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormat.ts b/packages/anoncreds/src/formats/AnonCredsProofFormat.ts index 2bfeb689dc..0c326943f8 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormat.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormat.ts @@ -42,9 +42,9 @@ export interface AnonCredsProposeProofFormat { export interface AnonCredsRequestProofFormat { name: string version: string - nonRevoked?: AnonCredsNonRevokedInterval - requestedAttributes?: Record - requestedPredicates?: Record + non_revoked?: AnonCredsNonRevokedInterval + requested_attributes?: Record + requested_predicates?: Record } /** diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts index 7cf5b18786..b75df46b52 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -149,9 +149,9 @@ export class LegacyIndyProofFormatService implements ProofFormatService + requestedAttributes?: Record requestedPredicates?: Record } diff --git a/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts b/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts index 806f5f422b..7e2df55c8f 100644 --- a/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts +++ b/packages/anoncreds/src/models/AnonCredsRequestedAttribute.ts @@ -1,11 +1,20 @@ +import type { AnonCredsRestrictionOptions } from './AnonCredsRestriction' + import { Expose, Type } from 'class-transformer' import { ArrayNotEmpty, IsArray, IsInstance, IsOptional, IsString, ValidateIf, ValidateNested } from 'class-validator' import { AnonCredsRestriction, AnonCredsRestrictionTransformer } from './AnonCredsRestriction' import { AnonCredsRevocationInterval } from './AnonCredsRevocationInterval' +export interface AnonCredsRequestedAttributeOptions { + name?: string + names?: string[] + nonRevoked?: AnonCredsRevocationInterval + restrictions?: AnonCredsRestrictionOptions[] +} + export class AnonCredsRequestedAttribute { - public constructor(options: AnonCredsRequestedAttribute) { + public constructor(options: AnonCredsRequestedAttributeOptions) { if (options) { this.name = options.name this.names = options.names diff --git a/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts b/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts index 5f9f99ebc0..9df0bcd698 100644 --- a/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts +++ b/packages/anoncreds/src/models/AnonCredsRequestedPredicate.ts @@ -1,3 +1,5 @@ +import type { AnonCredsRestrictionOptions } from './AnonCredsRestriction' + import { Expose, Type } from 'class-transformer' import { IsArray, IsIn, IsInstance, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' @@ -12,7 +14,7 @@ export interface AnonCredsRequestedPredicateOptions { predicateType: AnonCredsPredicateType predicateValue: number nonRevoked?: AnonCredsRevocationInterval - restrictions?: AnonCredsRestriction[] + restrictions?: AnonCredsRestrictionOptions[] } export class AnonCredsRequestedPredicate { diff --git a/packages/anoncreds/src/models/AnonCredsRestriction.ts b/packages/anoncreds/src/models/AnonCredsRestriction.ts index def1fc70a2..c3f8ce843c 100644 --- a/packages/anoncreds/src/models/AnonCredsRestriction.ts +++ b/packages/anoncreds/src/models/AnonCredsRestriction.ts @@ -1,8 +1,21 @@ import { Exclude, Expose, Transform, TransformationType } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' +export interface AnonCredsRestrictionOptions { + schemaId?: string + schemaIssuerDid?: string + schemaIssuerId?: string + schemaName?: string + schemaVersion?: string + issuerDid?: string + issuerId?: string + credentialDefinitionId?: string + attributeMarkers?: Record + attributeValues?: Record +} + export class AnonCredsRestriction { - public constructor(options: AnonCredsRestriction) { + public constructor(options: AnonCredsRestrictionOptions) { if (options) { this.schemaId = options.schemaId this.schemaIssuerDid = options.schemaIssuerDid @@ -12,8 +25,8 @@ export class AnonCredsRestriction { this.issuerDid = options.issuerDid this.issuerId = options.issuerId this.credentialDefinitionId = options.credentialDefinitionId - this.attributeMarkers = options.attributeMarkers - this.attributeValues = options.attributeValues + this.attributeMarkers = options.attributeMarkers ?? {} + this.attributeValues = options.attributeValues ?? {} } } diff --git a/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts b/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts index a3d02ab549..33884d09a8 100644 --- a/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts +++ b/packages/anoncreds/src/models/__tests__/AnonCredsRestriction.test.ts @@ -77,4 +77,69 @@ describe('AnonCredsRestriction', () => { ], }) }) + + test('transforms properties from and to json with correct casing', () => { + const restrictions = new Wrapper({ + restrictions: [ + new AnonCredsRestriction({ + credentialDefinitionId: 'credentialDefinitionId', + issuerDid: 'issuerDid', + issuerId: 'issuerId', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + schemaId: 'schemaId', + schemaIssuerDid: 'schemaIssuerDid', + schemaIssuerId: 'schemaIssuerId', + }), + ], + }) + + expect(JsonTransformer.toJSON(restrictions)).toMatchObject({ + restrictions: [ + { + cred_def_id: 'credentialDefinitionId', + issuer_did: 'issuerDid', + issuer_id: 'issuerId', + schema_name: 'schemaName', + schema_version: 'schemaVersion', + schema_id: 'schemaId', + schema_issuer_did: 'schemaIssuerDid', + schema_issuer_id: 'schemaIssuerId', + }, + ], + }) + + expect( + JsonTransformer.fromJSON( + { + restrictions: [ + { + cred_def_id: 'credentialDefinitionId', + issuer_did: 'issuerDid', + issuer_id: 'issuerId', + schema_name: 'schemaName', + schema_version: 'schemaVersion', + schema_id: 'schemaId', + schema_issuer_did: 'schemaIssuerDid', + schema_issuer_id: 'schemaIssuerId', + }, + ], + }, + Wrapper + ) + ).toMatchObject({ + restrictions: [ + { + credentialDefinitionId: 'credentialDefinitionId', + issuerDid: 'issuerDid', + issuerId: 'issuerId', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + schemaId: 'schemaId', + schemaIssuerDid: 'schemaIssuerDid', + schemaIssuerId: 'schemaIssuerId', + }, + ], + }) + }) }) From ecce0a71578f45f55743198a1f3699bd257dc74b Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 13 Feb 2023 19:01:39 -0300 Subject: [PATCH 042/139] fix(askar): generate nonce suitable for anoncreds (#1295) Signed-off-by: Ariel Gentile --- packages/askar/package.json | 2 ++ packages/askar/src/wallet/AskarWallet.ts | 7 +++++-- packages/askar/src/wallet/__tests__/AskarWallet.test.ts | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/askar/package.json b/packages/askar/package.json index 1f7935175a..af83ba53ea 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -26,12 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@hyperledger/aries-askar-shared": "^0.1.0-dev.1", + "bn.js": "^5.2.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { + "@types/bn.js": "^5.1.0", "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.1", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 432e50cdda..c84370cadf 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -34,7 +34,6 @@ import { FileSystem, WalletNotFoundError, } from '@aries-framework/core' -// eslint-disable-next-line import/order import { StoreKeyMethod, KeyAlgs, @@ -43,6 +42,8 @@ import { Key as AskarKey, keyAlgFromString, } from '@hyperledger/aries-askar-shared' +// eslint-disable-next-line import/order +import BigNumber from 'bn.js' const isError = (error: unknown): error is Error => error instanceof Error @@ -669,7 +670,9 @@ export class AskarWallet implements Wallet { public async generateNonce(): Promise { try { - return TypedArrayEncoder.toUtf8String(CryptoBox.randomNonce()) + // generate an 80-bit nonce suitable for AnonCreds proofs + const nonce = CryptoBox.randomNonce().slice(0, 10) + return new BigNumber(nonce).toString() } catch (error) { if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 15bbf174cd..94732a15b0 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -57,7 +57,9 @@ describe('AskarWallet basic operations', () => { }) test('Generate Nonce', async () => { - await expect(askarWallet.generateNonce()).resolves.toEqual(expect.any(String)) + const nonce = await askarWallet.generateNonce() + + expect(nonce).toMatch(/[0-9]+/) }) test('Create ed25519 keypair', async () => { From a48770530fefea5efeb5eb7a010e600ac69434d7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 14 Feb 2023 10:57:22 +0100 Subject: [PATCH 043/139] docs: update readme (#1298) docs: update reaadme Signed-off-by: Timo Glastra --- README.md | 83 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 314db5a4d6..45725071db 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,6 @@ alt="Pipeline Status" src="https://github.com/hyperledger/aries-framework-javascript/workflows/Continuous%20Integration/badge.svg?branch=main" /> - Language grade: JavaScript Codecov Coverage + + + @aries-framework/indy-sdk + + + @aries-framework/indy-sdk version + + + + + @aries-framework/indy-vdr + + + @aries-framework/indy-vdr version + + + + + @aries-framework/askar + + + @aries-framework/askar version + + + + + @aries-framework/anoncreds + + + @aries-framework/anoncreds version + + + + + @aries-framework/anoncreds-rs + + + @aries-framework/anoncreds-rs version + + + + + @aries-framework/openid4vc-client + + + @aries-framework/openid4vc-client version + + @aries-framework/action-menu From efab8ddfc34e47a3f0ffe35b55fa5018a7e96544 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 14 Feb 2023 19:49:59 -0300 Subject: [PATCH 044/139] feat(indy-vdr): resolver and registrar for did:indy (#1253) Signed-off-by: Ariel Gentile --- packages/indy-vdr/package.json | 2 + .../src/dids/IndyVdrIndyDidRegistrar.ts | 334 +++++++++++++++++ .../src/dids/IndyVdrIndyDidResolver.ts | 138 +++++++ .../src/dids/IndyVdrSovDidResolver.ts | 16 +- .../__tests__/IndyVdrIndyDidResolver.test.ts | 148 ++++++++ .../__tests__/__fixtures__/didExample123.json | 100 +++++ .../__fixtures__/didExample123base.json | 12 + .../didExample123extracontent.json | 92 +++++ .../didIndyLjgpST2rjsoxYegQDRm7EL.json | 110 ++++++ ...dyLjgpST2rjsoxYegQDRm7ELdiddocContent.json | 98 +++++ .../didIndyR1xKJw17sUoXhejEpugMYJ.json | 13 + .../didIndyWJz9mHyW9BZksioQnRsrAo.json | 43 +++ .../src/dids/__tests__/didIndyUtil.test.ts | 23 ++ .../src/dids/__tests__/didSovUtil.test.ts | 5 + packages/indy-vdr/src/dids/didIndyUtil.ts | 165 +++++++++ packages/indy-vdr/src/dids/didSovUtil.ts | 38 +- packages/indy-vdr/src/dids/index.ts | 2 + packages/indy-vdr/src/index.ts | 2 +- packages/indy-vdr/src/utils/did.ts | 2 +- .../tests/indy-vdr-did-registrar.e2e.test.ts | 342 ++++++++++++++++++ .../tests/indy-vdr-did-resolver.e2e.test.ts | 9 +- .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 6 +- 22 files changed, 1688 insertions(+), 12 deletions(-) create mode 100644 packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts create mode 100644 packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts create mode 100644 packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123base.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123extracontent.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7EL.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json create mode 100644 packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json create mode 100644 packages/indy-vdr/src/dids/__tests__/didIndyUtil.test.ts create mode 100644 packages/indy-vdr/src/dids/__tests__/didSovUtil.test.ts create mode 100644 packages/indy-vdr/src/dids/didIndyUtil.ts create mode 100644 packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 418d681c36..fa1c9d3e39 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -30,7 +30,9 @@ }, "devDependencies": { "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.4", + "@stablelib/ed25519": "^1.0.2", "rimraf": "^4.0.7", + "rxjs": "^7.2.0", "typescript": "~4.9.4" } } diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts new file mode 100644 index 0000000000..7505c09280 --- /dev/null +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -0,0 +1,334 @@ +import type { IndyEndpointAttrib } from './didSovUtil' +import type { IndyVdrPool } from '../pool' +import type { + AgentContext, + DidRegistrar, + DidCreateOptions, + DidCreateResult, + DidDeactivateResult, + DidUpdateResult, + DidDocumentService, +} from '@aries-framework/core' + +import { + IndyAgentService, + DidCommV1Service, + DidCommV2Service, + Hasher, + TypedArrayEncoder, + Key, + KeyType, + DidDocumentRole, + DidRecord, + DidRepository, +} from '@aries-framework/core' +import { AttribRequest, NymRequest } from '@hyperledger/indy-vdr-shared' + +import { IndyVdrError } from '../error' +import { IndyVdrPoolService } from '../pool/IndyVdrPoolService' + +import { + createKeyAgreementKey, + didDocDiff, + indyDidDocumentFromDid, + parseIndyDid, + isSelfCertifiedIndyDid, +} from './didIndyUtil' +import { endpointsAttribFromServices } from './didSovUtil' + +export class IndyVdrIndyDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['indy'] + + public async create(agentContext: AgentContext, options: IndyVdrDidCreateOptions): Promise { + const seed = options.secret?.seed + if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid seed provided', + }, + } + } + + const { alias, role, submitterDid, submitterVerkey, services, useEndpointAttrib } = options.options + let verkey = options.options.verkey + let did = options.did + let id + + if (seed && did) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Only one of 'seed' and 'did' must be provided`, + }, + } + } + + try { + const { namespace, id: submitterId } = parseIndyDid(submitterDid) + + if (did) { + id = parseIndyDid(did).id + if (!verkey) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'If a did is defined, a matching verkey must be provided', + }, + } + } + if (!isSelfCertifiedIndyDid(did, verkey)) { + throw new Error(`Initial verkey ${verkey} does not match did ˇ${did}`) + } + } else { + // Create a new key and calculate did according to the rules for indy did method + const key = await agentContext.wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + const buffer = Hasher.hash(key.publicKey, 'sha2-256') + + id = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + verkey = key.publicKeyBase58 + did = `did:indy:${namespace}:${id}` + } + + // Create base did document + const didDocumentBuilder = indyDidDocumentFromDid(did, verkey) + let diddocContent + + // Add services if object was passed + if (services) { + services.forEach((item) => didDocumentBuilder.addService(item)) + + const commTypes = [IndyAgentService.type, DidCommV1Service.type, DidCommV2Service.type] + const serviceTypes = new Set(services.map((item) => item.type)) + + const keyAgreementId = `${did}#key-agreement-1` + + // If there is at least a communication service, add the key agreement key + if (commTypes.some((type) => serviceTypes.has(type))) { + didDocumentBuilder + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(verkey), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) + } + + // If there is a DIDComm V2 service, add context + if (serviceTypes.has(DidCommV2Service.type)) { + didDocumentBuilder.addContext('https://didcomm.org/messaging/contexts/v2') + } + + if (!useEndpointAttrib) { + // create diddocContent parameter based on the diff between the base and the resulting DID Document + diddocContent = didDocDiff( + didDocumentBuilder.build().toJSON(), + indyDidDocumentFromDid(did, verkey).build().toJSON() + ) + } + } + + // Build did document + const didDocument = didDocumentBuilder.build() + + const pool = agentContext.dependencyManager.resolve(IndyVdrPoolService).getPoolForNamespace(namespace) + + // If there are services and we are using legacy indy endpoint attrib, make sure they are suitable before registering the DID + if (services && useEndpointAttrib) { + const endpoints = endpointsAttribFromServices(services) + await this.registerPublicDid(agentContext, pool, submitterId, submitterVerkey, id, verkey, alias, role) + await this.setEndpointsForDid(agentContext, pool, verkey, id, endpoints) + } else { + await this.registerPublicDid( + agentContext, + pool, + submitterId, + submitterVerkey, + id, + verkey, + alias, + role, + diddocContent + ) + } + + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + did, + role: DidDocumentRole.Created, + tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), + qualifiedIndyDid: did, + }, + }) + + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + await didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: { + didIndyNamespace: namespace, + }, + didState: { + state: 'finished', + did, + didDocument, + secret: { + // FIXME: the uni-registrar creates the seed in the registrar method + // if it doesn't exist so the seed can always be returned. Currently + // we can only return it if the seed was passed in by the user. Once + // we have a secure method for generating seeds we should use the same + // approach + seed: options.secret?.seed, + }, + }, + } + } catch (error) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async update(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:indy not implemented yet`, + }, + } + } + + public async deactivate(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:indy not implemented yet`, + }, + } + } + + private async registerPublicDid( + agentContext: AgentContext, + pool: IndyVdrPool, + submitterDid: string, + submitterVerkey: string, + targetDid: string, + verkey: string, + alias?: string, + role?: string, + diddocContent?: Record + ) { + try { + agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool}'`) + + // FIXME: Add diddocContent when supported by indy-vdr + if (diddocContent) { + throw new IndyVdrError('diddocContent is not yet supported') + } + + const request = new NymRequest({ submitterDid, dest: targetDid, verkey, alias }) + + const signingKey = Key.fromPublicKeyBase58(submitterVerkey, KeyType.Ed25519) + + const response = await pool.submitWriteRequest(agentContext, request, signingKey) + + agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.indyNamespace}'`, { + response, + }) + + return targetDid + } catch (error) { + agentContext.config.logger.error( + `Error registering public did '${targetDid}' on ledger '${pool.indyNamespace}'`, + { + error, + submitterDid, + targetDid, + verkey, + alias, + role, + pool: pool.indyNamespace, + } + ) + + throw error + } + } + + private async setEndpointsForDid( + agentContext: AgentContext, + pool: IndyVdrPool, + submitterVerkey: string, + did: string, + endpoints: IndyEndpointAttrib + ): Promise { + try { + agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, endpoints) + + const request = new AttribRequest({ + submitterDid: did, + targetDid: did, + raw: JSON.stringify({ endpoint: endpoints }), + }) + + const signingKey = Key.fromPublicKeyBase58(submitterVerkey, KeyType.Ed25519) + + const response = await pool.submitWriteRequest(agentContext, request, signingKey) + agentContext.config.logger.debug( + `Successfully set endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, + { + response, + endpoints, + } + ) + } catch (error) { + agentContext.config.logger.error(`Error setting endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, { + error, + did, + endpoints, + }) + + throw new IndyVdrError(error) + } + } +} + +export interface IndyVdrDidCreateOptions extends DidCreateOptions { + method: 'indy' + did?: string + didDocument?: never // Not yet supported + options: { + alias?: string + role?: string + services?: DidDocumentService[] + useEndpointAttrib?: boolean + submitterDid: string + submitterVerkey: string + verkey?: string + } + secret?: { + seed?: string + } +} + +// TODO: Add Update and Deactivate +export type IndyVdrIndyDidUpdateOptions = never +export type IndyVdrIndyDidDeactivateOptions = never diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts new file mode 100644 index 0000000000..6f6d40cbcf --- /dev/null +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts @@ -0,0 +1,138 @@ +import type { CommEndpointType, GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' +import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' + +import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' + +import { IndyVdrError, IndyVdrNotFoundError } from '../error' +import { IndyVdrPoolService } from '../pool' + +import { combineDidDocumentWithJson, createKeyAgreementKey, indyDidDocumentFromDid, parseIndyDid } from './didIndyUtil' +import { getFullVerkey, addServicesFromEndpointsAttrib } from './didSovUtil' + +export class IndyVdrIndyDidResolver implements DidResolver { + public readonly supportedMethods = ['indy'] + + public async resolve(agentContext: AgentContext, did: string): Promise { + const didDocumentMetadata = {} + try { + const nym = await this.getPublicDid(agentContext, did) + + // Get DID Document from Get NYM response + const didDocument = await this.buildDidDocument(agentContext, nym, did) + + return { + didDocument, + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } + + private async buildDidDocument(agentContext: AgentContext, getNymResponseData: GetNymResponseData, did: string) { + // Create base Did Document + + // For modern did:indy DIDs, we assume that GET_NYM is always a full verkey in base58. + // For backwards compatibility, we accept a shortened verkey and convert it using previous convention + const verkey = getFullVerkey(did, getNymResponseData.verkey) + + const builder = indyDidDocumentFromDid(did, verkey) + + // If GET_NYM does not return any diddocContent, fallback to legacy GET_ATTRIB endpoint + if (!getNymResponseData.diddocContent) { + const keyAgreementId = `${did}#key-agreement-1` + + const endpoints = await this.getEndpointsForDid(agentContext, did) + + if (endpoints) { + // If there is at least a didcomm endpoint, generate and a key agreement key + const commTypes: CommEndpointType[] = ['endpoint', 'did-communication', 'DIDComm'] + if (commTypes.some((type) => endpoints.types?.includes(type))) { + builder + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(getNymResponseData.verkey), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) + } + + // Process endpoint attrib following the same rules as for did:sov + addServicesFromEndpointsAttrib(builder, did, endpoints, keyAgreementId) + } + return builder.build() + } else { + // Combine it with didDoc (TODO: Check if diddocContent is returned as a JSON object or a string) + return combineDidDocumentWithJson(builder.build(), getNymResponseData.diddocContent) + } + } + + private async getPublicDid(agentContext: AgentContext, did: string) { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const { namespace, id } = parseIndyDid(did) + + const pool = indyVdrPoolService.getPoolForNamespace(namespace) + + const request = new GetNymRequest({ dest: id }) + + const didResponse = await pool.submitReadRequest(request) + + if (!didResponse.result.data) { + throw new IndyVdrNotFoundError(`DID ${id} not found in indy namespace ${namespace}`) + } + return JSON.parse(didResponse.result.data) as GetNymResponseData + } + + private async getEndpointsForDid(agentContext: AgentContext, did: string) { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const { namespace, id } = parseIndyDid(did) + + const pool = indyVdrPoolService.getPoolForNamespace(namespace) + + try { + agentContext.config.logger.debug(`Get endpoints for did '${id}' from ledger '${pool.indyNamespace}'`) + + const request = new GetAttribRequest({ targetDid: id, raw: 'endpoint' }) + + agentContext.config.logger.debug( + `Submitting get endpoint ATTRIB request for did '${id}' to ledger '${pool.indyNamespace}'` + ) + const response = await pool.submitReadRequest(request) + + if (!response.result.data) { + return null + } + + const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib + agentContext.config.logger.debug( + `Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.indyNamespace}'`, + { + response, + endpoints, + } + ) + + return endpoints + } catch (error) { + agentContext.config.logger.error( + `Error retrieving endpoints for did '${did}' from ledger '${pool.indyNamespace}'`, + { + error, + } + ) + + throw new IndyVdrError(error) + } + } +} diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts index b563a3122e..842707aaa1 100644 --- a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -16,11 +16,15 @@ export class IndyVdrSovDidResolver implements DidResolver { try { const nym = await this.getPublicDid(agentContext, parsed.id) + const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) + const endpoints = await this.getEndpointsForDid(agentContext, parsed.id) - const keyAgreementId = `${parsed.did}#key-agreement-1` - const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) - addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) + if (endpoints) { + const keyAgreementId = `${parsed.did}#key-agreement-1` + + addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) + } return { didDocument: builder.build(), @@ -69,7 +73,9 @@ export class IndyVdrSovDidResolver implements DidResolver { ) const response = await pool.submitReadRequest(request) - if (!response.result.data) return {} + if (!response.result.data) { + return null + } const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib agentContext.config.logger.debug( @@ -80,7 +86,7 @@ export class IndyVdrSovDidResolver implements DidResolver { } ) - return endpoints ?? {} + return endpoints ?? null } catch (error) { agentContext.config.logger.error( `Error retrieving endpoints for did '${did}' from ledger '${pool.indyNamespace}'`, diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts new file mode 100644 index 0000000000..e37266f5c7 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts @@ -0,0 +1,148 @@ +import { JsonTransformer } from '@aries-framework/core' + +import { getAgentConfig, getAgentContext, mockProperty } from '../../../../core/tests/helpers' +import { IndyVdrPool, IndyVdrPoolService } from '../../pool' +import { IndyVdrIndyDidResolver } from '../IndyVdrIndyDidResolver' + +import didIndyLjgpST2rjsoxYegQDRm7EL from './__fixtures__/didIndyLjgpST2rjsoxYegQDRm7EL.json' +import didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent from './__fixtures__/didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent.json' +import didIndyR1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json' +import didIndyWJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json' + +jest.mock('../../pool/IndyVdrPool') +const IndyVdrPoolMock = IndyVdrPool as jest.Mock +const poolMock = new IndyVdrPoolMock() +mockProperty(poolMock, 'indyNamespace', 'ns1') + +const agentConfig = getAgentConfig('IndyVdrIndyDidResolver') + +const agentContext = getAgentContext({ + agentConfig, + registerInstances: [[IndyVdrPoolService, { getPoolForNamespace: jest.fn().mockReturnValue(poolMock) }]], +}) + +const resolver = new IndyVdrIndyDidResolver() + +describe('IndyVdrIndyDidResolver', () => { + describe('NYMs with diddocContent', () => { + it('should correctly resolve a did:indy document with arbitrary diddocContent', async () => { + const did = 'did:indy:ns2:LjgpST2rjsoxYegQDRm7EL' + + const nymResponse = { + result: { + data: JSON.stringify({ + did: 'LjgpST2rjsoxYegQDRm7EL', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + role: 'ENDORSER', + diddocContent: didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent, + }), + }, + } + + const poolMockSubmitReadRequest = jest.spyOn(poolMock, 'submitReadRequest') + poolMockSubmitReadRequest.mockResolvedValueOnce(nymResponse) + + const result = await resolver.resolve(agentContext, did) + + expect(poolMockSubmitReadRequest).toHaveBeenCalledTimes(1) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didIndyLjgpST2rjsoxYegQDRm7EL, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + }) + + describe('NYMs without diddocContent', () => { + it('should correctly resolve a did:indy document without endpoint attrib', async () => { + const did = 'did:indy:ns1:R1xKJw17sUoXhejEpugMYJ' + + const nymResponse = { + result: { + data: JSON.stringify({ + did: 'R1xKJw17sUoXhejEpugMYJ', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + role: 'ENDORSER', + }), + }, + } + + const attribResponse = { + result: { + data: null, + }, + } + + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + + const result = await resolver.resolve(agentContext, did) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didIndyR1xKJw17sUoXhejEpugMYJFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should correctly resolve a did:indy document with endpoint attrib', async () => { + const did = 'did:indy:ns1:WJz9mHyW9BZksioQnRsrAo' + + const nymResponse = { + result: { + data: JSON.stringify({ + did: 'WJz9mHyW9BZksioQnRsrAo', + verkey: 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8', + role: 'ENDORSER', + }), + }, + } + + const attribResponse = { + result: { + data: JSON.stringify({ + endpoint: { + endpoint: 'https://agent.com', + types: ['endpoint', 'did-communication', 'DIDComm'], + routingKeys: ['routingKey1', 'routingKey2'], + }, + }), + }, + } + + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + + const result = await resolver.resolve(agentContext, did) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didIndyWJz9mHyW9BZksioQnRsrAoFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { + const did = 'did:indy:ns1:R1xKJw17sUoXhejEpugMYJ' + + jest.spyOn(poolMock, 'submitReadRequest').mockRejectedValue(new Error('Error submitting read request')) + + const result = await resolver.resolve(agentContext, did) + + expect(result).toMatchObject({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did 'did:indy:ns1:R1xKJw17sUoXhejEpugMYJ': Error: Error submitting read request`, + }, + }) + }) + }) +}) diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123.json new file mode 100644 index 0000000000..c26715ddc1 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123.json @@ -0,0 +1,100 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "id": "did:example:123", + "alsoKnownAs": ["did:example:456"], + "controller": ["did:example:456"], + "verificationMethod": [ + { + "id": "did:example:123#verkey", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC X..." + }, + { + "id": "did:example:123#key-2", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyBase58": "-----BEGIN PUBLIC 9..." + }, + { + "id": "did:example:123#key-3", + "type": "Secp256k1VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyHex": "-----BEGIN PUBLIC A..." + } + ], + "service": [ + { + "id": "did:example:123#service-1", + "type": "Mediator", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h" + }, + { + "id": "did:example:123#service-2", + "type": "IndyAgent", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "recipientKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "routingKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "priority": 5 + }, + { + "id": "did:example:123#service-3", + "type": "did-communication", + "serviceEndpoint": "https://agent.com/did-comm", + "recipientKeys": ["DADEajsDSaksLng9h"], + "routingKeys": ["DADEajsDSaksLng9h"], + "priority": 10 + } + ], + "authentication": [ + "did:example:123#verkey", + { + "id": "did:example:123#authentication-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "assertionMethod": [ + "did:example:123#verkey", + { + "id": "did:example:123#assertionMethod-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityDelegation": [ + "did:example:123#verkey", + { + "id": "did:example:123#capabilityDelegation-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityInvocation": [ + "did:example:123#verkey", + { + "id": "did:example:123#capabilityInvocation-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "keyAgreement": [ + "did:example:123#verkey", + { + "id": "did:example:123#keyAgreement-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + }, + { + "id": "did:example:123#keyAgreement-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123base.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123base.json new file mode 100644 index 0000000000..ce8b62392f --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123base.json @@ -0,0 +1,12 @@ +{ + "id": "did:example:123", + "verificationMethod": [ + { + "id": "did:example:123#verkey", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC X..." + } + ], + "authentication": ["did:example:123#verkey"] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123extracontent.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123extracontent.json new file mode 100644 index 0000000000..81397021cd --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didExample123extracontent.json @@ -0,0 +1,92 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "alsoKnownAs": ["did:example:456"], + "controller": ["did:example:456"], + "verificationMethod": [ + { + "id": "did:example:123#key-2", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyBase58": "-----BEGIN PUBLIC 9..." + }, + { + "id": "did:example:123#key-3", + "type": "Secp256k1VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyHex": "-----BEGIN PUBLIC A..." + } + ], + "service": [ + { + "id": "did:example:123#service-1", + "type": "Mediator", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h" + }, + { + "id": "did:example:123#service-2", + "type": "IndyAgent", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "recipientKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "routingKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "priority": 5 + }, + { + "id": "did:example:123#service-3", + "type": "did-communication", + "serviceEndpoint": "https://agent.com/did-comm", + "recipientKeys": ["DADEajsDSaksLng9h"], + "routingKeys": ["DADEajsDSaksLng9h"], + "priority": 10 + } + ], + "authentication": [ + { + "id": "did:example:123#authentication-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "assertionMethod": [ + "did:example:123#verkey", + { + "id": "did:example:123#assertionMethod-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityDelegation": [ + "did:example:123#verkey", + { + "id": "did:example:123#capabilityDelegation-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityInvocation": [ + "did:example:123#verkey", + { + "id": "did:example:123#capabilityInvocation-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "keyAgreement": [ + "did:example:123#verkey", + { + "id": "did:example:123#keyAgreement-1", + "type": "RsaVerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + }, + { + "id": "did:example:123#keyAgreement-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:LjgpST2rjsoxYegQDRm7EL", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7EL.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7EL.json new file mode 100644 index 0000000000..49e43fa742 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7EL.json @@ -0,0 +1,110 @@ +{ + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1" + ], + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL", + "alsoKnownAs": ["did:indy:ns2:R1xKJw17sUoXhejEpugMYJ"], + "controller": ["did:indy:ns2:R1xKJw17sUoXhejEpugMYJ"], + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL", + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#verkey", + "publicKeyBase58": "E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu" + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-2", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC X..." + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-3", + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyBase58": "-----BEGIN PUBLIC 9..." + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-4", + "type": "Secp256k1VerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyHex": "-----BEGIN PUBLIC A..." + } + ], + "service": [ + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#service-1", + "type": "Mediator", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h" + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#service-2", + "type": "IndyAgent", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "recipientKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "routingKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "priority": 5 + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#service-3", + "type": "did-communication", + "serviceEndpoint": "https://agent.com/did-comm", + "recipientKeys": ["DADEajsDSaksLng9h"], + "routingKeys": ["DADEajsDSaksLng9h"], + "priority": 10 + } + ], + "authentication": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#verkey", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#authentication-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "assertionMethod": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#assertionMethod-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityDelegation": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#capabilityDelegation-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityInvocation": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#capabilityInvocation-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "keyAgreement": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#keyAgreement-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#keyAgreement-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent.json new file mode 100644 index 0000000000..94bfaed219 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyLjgpST2rjsoxYegQDRm7ELdiddocContent.json @@ -0,0 +1,98 @@ +{ + "@context": ["https://w3id.org/security/suites/ed25519-2018/v1", "https://w3id.org/security/suites/x25519-2019/v1"], + "alsoKnownAs": ["did:indy:ns2:R1xKJw17sUoXhejEpugMYJ"], + "controller": ["did:indy:ns2:R1xKJw17sUoXhejEpugMYJ"], + "verificationMethod": [ + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-2", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC X..." + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-3", + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyBase58": "-----BEGIN PUBLIC 9..." + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-4", + "type": "Secp256k1VerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyHex": "-----BEGIN PUBLIC A..." + } + ], + "service": [ + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#service-1", + "type": "Mediator", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h" + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#service-2", + "type": "IndyAgent", + "serviceEndpoint": "did:sov:Q4zqM7aXqm7gDQkUVLng9h", + "recipientKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "routingKeys": ["Q4zqM7aXqm7gDQkUVLng9h"], + "priority": 5 + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#service-3", + "type": "did-communication", + "serviceEndpoint": "https://agent.com/did-comm", + "recipientKeys": ["DADEajsDSaksLng9h"], + "routingKeys": ["DADEajsDSaksLng9h"], + "priority": 10 + } + ], + "authentication": [ + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#authentication-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "assertionMethod": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#assertionMethod-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityDelegation": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#capabilityDelegation-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "capabilityInvocation": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#capabilityInvocation-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ], + "keyAgreement": [ + "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#key-1", + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#keyAgreement-1", + "type": "RsaVerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + }, + { + "id": "did:indy:ns2:LjgpST2rjsoxYegQDRm7EL#keyAgreement-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns2:WJz9mHyW9BZksioQnRsrAo", + "publicKeyPem": "-----BEGIN PUBLIC A..." + } + ] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json new file mode 100644 index 0000000000..56014e70be --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json @@ -0,0 +1,13 @@ +{ + "@context": ["https://w3id.org/did/v1"], + "id": "did:indy:ns1:R1xKJw17sUoXhejEpugMYJ", + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns1:R1xKJw17sUoXhejEpugMYJ", + "id": "did:indy:ns1:R1xKJw17sUoXhejEpugMYJ#verkey", + "publicKeyBase58": "E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu" + } + ], + "authentication": ["did:indy:ns1:R1xKJw17sUoXhejEpugMYJ#verkey"] +} diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json new file mode 100644 index 0000000000..c131549e18 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json @@ -0,0 +1,43 @@ +{ + "@context": ["https://w3id.org/did/v1", "https://didcomm.org/messaging/contexts/v2"], + "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo", + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo", + "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#verkey", + "publicKeyBase58": "GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8" + }, + { + "type": "X25519KeyAgreementKey2019", + "controller": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo", + "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1", + "publicKeyBase58": "S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud" + } + ], + "authentication": ["did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#verkey"], + "keyAgreement": ["did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "service": [ + { + "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#endpoint", + "type": "endpoint", + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#did-communication", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "routingKeys": ["routingKey1", "routingKey2"], + "accept": ["didcomm/aip2;env=rfc19"], + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#didcomm-1", + "type": "DIDComm", + "serviceEndpoint": "https://agent.com", + "accept": ["didcomm/v2"], + "routingKeys": ["routingKey1", "routingKey2"] + } + ] +} diff --git a/packages/indy-vdr/src/dids/__tests__/didIndyUtil.test.ts b/packages/indy-vdr/src/dids/__tests__/didIndyUtil.test.ts new file mode 100644 index 0000000000..81c8218274 --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/didIndyUtil.test.ts @@ -0,0 +1,23 @@ +import { DidDocument, JsonTransformer } from '@aries-framework/core' + +import { combineDidDocumentWithJson, didDocDiff } from '../didIndyUtil' + +import didExample123Fixture from './__fixtures__/didExample123.json' +import didExample123Base from './__fixtures__/didExample123base.json' +import didExample123Extra from './__fixtures__/didExample123extracontent.json' + +describe('didIndyUtil', () => { + describe('combineDidDocumentWithJson', () => { + it('should correctly combine a base DIDDoc with extra contents from a JSON object', async () => { + const didDocument = JsonTransformer.fromJSON(didExample123Base, DidDocument) + + expect(combineDidDocumentWithJson(didDocument, didExample123Extra).toJSON()).toEqual(didExample123Fixture) + }) + }) + + describe('deepObjectDiff', () => { + it('should correctly show the diff between a base DidDocument and a full DidDocument', async () => { + expect(didDocDiff(didExample123Fixture, didExample123Base)).toMatchObject(didExample123Extra) + }) + }) +}) diff --git a/packages/indy-vdr/src/dids/__tests__/didSovUtil.test.ts b/packages/indy-vdr/src/dids/__tests__/didSovUtil.test.ts new file mode 100644 index 0000000000..f09f8060bc --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/didSovUtil.test.ts @@ -0,0 +1,5 @@ +describe('didSovUtil', () => { + describe('endpointsAttribFromServices', () => { + it.todo('should correctly transform DidDocumentService instances to endpoint Attrib') + }) +}) diff --git a/packages/indy-vdr/src/dids/didIndyUtil.ts b/packages/indy-vdr/src/dids/didIndyUtil.ts new file mode 100644 index 0000000000..6644703169 --- /dev/null +++ b/packages/indy-vdr/src/dids/didIndyUtil.ts @@ -0,0 +1,165 @@ +import { + AriesFrameworkError, + convertPublicKeyToX25519, + DidDocument, + DidDocumentBuilder, + Hasher, + JsonTransformer, + Key, + KeyType, + TypedArrayEncoder, +} from '@aries-framework/core' + +import { DID_INDY_REGEX } from '../utils/did' + +// Create a base DIDDoc template according to https://hyperledger.github.io/indy-did-method/#base-diddoc-template +export function indyDidDocumentFromDid(did: string, verKeyBase58: string) { + const verificationMethodId = `${did}#verkey` + + const publicKeyBase58 = verKeyBase58 + + const builder = new DidDocumentBuilder(did) + .addVerificationMethod({ + controller: did, + id: verificationMethodId, + publicKeyBase58, + type: 'Ed25519VerificationKey2018', + }) + .addAuthentication(verificationMethodId) + + return builder +} + +export function createKeyAgreementKey(verkey: string) { + return TypedArrayEncoder.toBase58(convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(verkey))) +} + +export function parseIndyDid(did: string) { + const match = did.match(DID_INDY_REGEX) + if (match) { + const [, namespace, id] = match + return { namespace, id } + } else { + throw new AriesFrameworkError(`${did} is not a valid did:indy did`) + } +} + +const deepMerge = (a: Record, b: Record) => { + const output: Record = {} + + ;[...new Set([...Object.keys(a), ...Object.keys(b)])].forEach((key) => { + // Only an object includes a given key: just output it + if (a[key] && !b[key]) { + output[key] = a[key] + } else if (!a[key] && b[key]) { + output[key] = b[key] + } else { + // Both objects do include the key + // Some or both are arrays + if (Array.isArray(a[key])) { + if (Array.isArray(b[key])) { + const element = new Set() + ;(a[key] as Array).forEach((item: unknown) => element.add(item)) + ;(b[key] as Array).forEach((item: unknown) => element.add(item)) + output[key] = Array.from(element) + } else { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const arr = a[key] as Array + output[key] = Array.from(new Set(...arr, b[key])) + } + } else if (Array.isArray(b[key])) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const arr = b[key] as Array + output[key] = Array.from(new Set(...arr, a[key])) + // Both elements are objects: recursive merge + } else if (typeof a[key] == 'object' && typeof b[key] == 'object') { + output[key] = deepMerge(a, b) + } + } + }) + return output +} + +/** + * Combine a JSON content with the contents of a DidDocument + * @param didDoc object containing original DIDDocument + * @param json object containing extra DIDDoc contents + * + * @returns a DidDocument object resulting from the combination of both + */ +export function combineDidDocumentWithJson(didDoc: DidDocument, json: Record) { + const didDocJson = didDoc.toJSON() + const combinedJson = deepMerge(didDocJson, json) + return JsonTransformer.fromJSON(combinedJson, DidDocument) +} + +/** + * Processes the difference between a base DidDocument and a complete DidDocument + * + * Note: it does deep comparison based only on "id" field to determine whether is + * the same object or is a different one + * + * @param extra complete DidDocument + * @param base base DidDocument + * @returns diff object + */ +export function didDocDiff(extra: Record, base: Record) { + const output: Record = {} + for (const key in extra) { + if (!(key in base)) { + output[key] = extra[key] + } else { + // They are arrays: compare elements + if (Array.isArray(extra[key]) && Array.isArray(base[key])) { + // Different types: return the extra + output[key] = [] + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const baseAsArray = base[key] as Array + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const extraAsArray = extra[key] as Array + for (const element of extraAsArray) { + if (!baseAsArray.find((item) => item.id === element.id)) { + ;(output[key] as Array).push(element) + } + } + } // They are both objects: do recursive diff + else if (typeof extra[key] == 'object' && typeof base[key] == 'object') { + output[key] = didDocDiff(extra[key] as Record, base[key] as Record) + } else { + output[key] = extra[key] + } + } + } + return output +} + +/** + * Check whether the did is a self certifying did. If the verkey is abbreviated this method + * will always return true. Make sure that the verkey you pass in this method belongs to the + * did passed in + * + * @return Boolean indicating whether the did is self certifying + */ +export function isSelfCertifiedIndyDid(did: string, verkey: string): boolean { + const { namespace } = parseIndyDid(did) + const { did: didFromVerkey } = indyDidFromNamespaceAndInitialKey( + namespace, + Key.fromPublicKeyBase58(verkey, KeyType.Ed25519) + ) + + if (didFromVerkey === did) { + return true + } + + return false +} + +export function indyDidFromNamespaceAndInitialKey(namespace: string, initialKey: Key) { + const buffer = Hasher.hash(initialKey.publicKey, 'sha2-256') + + const id = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + const verkey = initialKey.publicKeyBase58 + const did = `did:indy:${namespace}:${id}` + + return { did, id, verkey } +} diff --git a/packages/indy-vdr/src/dids/didSovUtil.ts b/packages/indy-vdr/src/dids/didSovUtil.ts index 9fbe414b78..b836eb2eae 100644 --- a/packages/indy-vdr/src/dids/didSovUtil.ts +++ b/packages/indy-vdr/src/dids/didSovUtil.ts @@ -5,11 +5,14 @@ import { DidCommV1Service, DidCommV2Service, convertPublicKeyToX25519, + AriesFrameworkError, } from '@aries-framework/core' +export type CommEndpointType = 'endpoint' | 'did-communication' | 'DIDComm' + export interface IndyEndpointAttrib { endpoint?: string - types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> + types?: Array routingKeys?: string[] [key: string]: unknown } @@ -18,6 +21,8 @@ export interface GetNymResponseData { did: string verkey: string role: string + alias?: string + diddocContent?: Record } export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ @@ -102,6 +107,36 @@ function processEndpointTypes(types?: string[]) { return types } +export function endpointsAttribFromServices(services: DidDocumentService[]): IndyEndpointAttrib { + const commTypes: CommEndpointType[] = ['endpoint', 'did-communication', 'DIDComm'] + const commServices = services.filter((item) => commTypes.includes(item.type as CommEndpointType)) + + // Check that all services use the same endpoint, as only one is accepted + if (!commServices.every((item) => item.serviceEndpoint === services[0].serviceEndpoint)) { + throw new AriesFrameworkError('serviceEndpoint for all services must match') + } + + const types: CommEndpointType[] = [] + const routingKeys = new Set() + + for (const commService of commServices) { + const commServiceType = commService.type as CommEndpointType + if (types.includes(commServiceType)) { + throw new AriesFrameworkError('Only a single communication service per type is supported') + } else { + types.push(commServiceType) + } + + if (commService instanceof DidCommV1Service || commService instanceof DidCommV2Service) { + if (commService.routingKeys) { + commService.routingKeys.forEach((item) => routingKeys.add(item)) + } + } + } + + return { endpoint: services[0].serviceEndpoint, types, routingKeys: Array.from(routingKeys) } +} + export function addServicesFromEndpointsAttrib( builder: DidDocumentBuilder, did: string, @@ -138,6 +173,7 @@ export function addServicesFromEndpointsAttrib( ) // If 'DIDComm' included in types, add DIDComm v2 entry + // TODO: should it be DIDComm or DIDCommMessaging? (see https://github.com/sovrin-foundation/sovrin/issues/343) if (processedTypes.includes('DIDComm')) { builder .addService( diff --git a/packages/indy-vdr/src/dids/index.ts b/packages/indy-vdr/src/dids/index.ts index 7f9973684d..224dcb9d13 100644 --- a/packages/indy-vdr/src/dids/index.ts +++ b/packages/indy-vdr/src/dids/index.ts @@ -1 +1,3 @@ +export { IndyVdrIndyDidRegistrar } from './IndyVdrIndyDidRegistrar' +export { IndyVdrIndyDidResolver } from './IndyVdrIndyDidResolver' export { IndyVdrSovDidResolver } from './IndyVdrSovDidResolver' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index be45d47b96..fb687ae77f 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -1,4 +1,4 @@ -export { IndyVdrSovDidResolver } from './dids' +export { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from './dids' export * from './IndyVdrModule' export * from './IndyVdrModuleConfig' export * from './anoncreds' diff --git a/packages/indy-vdr/src/utils/did.ts b/packages/indy-vdr/src/utils/did.ts index 9cda8ee95d..44632246bb 100644 --- a/packages/indy-vdr/src/utils/did.ts +++ b/packages/indy-vdr/src/utils/did.ts @@ -17,7 +17,7 @@ import { TypedArrayEncoder } from '@aries-framework/core' -export const DID_INDY_REGEX = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)):([1-9A-HJ-NP-Za-km-z]{21,22})$/ +export const DID_INDY_REGEX = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22})$/ export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ /** diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts new file mode 100644 index 0000000000..6b24cc5c82 --- /dev/null +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -0,0 +1,342 @@ +import { + Key, + InjectionSymbols, + CacheModuleConfig, + InMemoryLruCache, + JsonTransformer, + KeyType, + SigningProviderRegistry, + TypedArrayEncoder, + DidCommV1Service, + DidCommV2Service, + DidDocumentService, + IndyWallet, +} from '@aries-framework/core' +import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' +import { Subject } from 'rxjs' + +import { IndyStorageService } from '../../core/src/storage/IndyStorageService' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import testLogger from '../../core/tests/logger' +import { IndyVdrIndyDidRegistrar } from '../src/dids/IndyVdrIndyDidRegistrar' +import { IndyVdrIndyDidResolver } from '../src/dids/IndyVdrIndyDidResolver' +import { indyDidFromNamespaceAndInitialKey } from '../src/dids/didIndyUtil' +import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' +import { DID_INDY_REGEX } from '../src/utils/did' + +import { indyVdrModuleConfig } from './helpers' + +const logger = testLogger +const wallet = new IndyWallet(agentDependencies, logger, new SigningProviderRegistry([])) + +const agentConfig = getAgentConfig('IndyVdrIndyDidRegistrar E2E', { logger }) + +const cache = new InMemoryLruCache({ limit: 200 }) +const indyVdrIndyDidResolver = new IndyVdrIndyDidResolver() +const indyVdrIndyDidRegistrar = new IndyVdrIndyDidRegistrar() + +let signerKey: Key + +const agentContext = getAgentContext({ + wallet, + agentConfig, + registerInstances: [ + [InjectionSymbols.Stop$, new Subject()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.StorageService, new IndyStorageService(agentDependencies)], + [IndyVdrPoolService, new IndyVdrPoolService(logger, indyVdrModuleConfig)], + [CacheModuleConfig, new CacheModuleConfig({ cache })], + ], +}) + +const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + +describe('Indy VDR registrar E2E', () => { + beforeAll(async () => { + await indyVdrPoolService.connectToPools() + + if (agentConfig.walletConfig) { + await wallet.createAndOpen(agentConfig.walletConfig) + } + + signerKey = await wallet.createKey({ + seed: '000000000000000000000000Trustee9', + keyType: KeyType.Ed25519, + }) + }) + + afterAll(async () => { + for (const pool of indyVdrPoolService.pools) { + pool.close() + } + + await wallet.delete() + }) + + test('can register a did:indy without services', async () => { + const didRegistrationResult = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + submitterDid: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', + submitterVerkey: signerKey.publicKeyBase58, + }, + }) + + expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: { + didIndyNamespace: 'pool:localtest', + }, + didState: { + state: 'finished', + did: expect.stringMatching(DID_INDY_REGEX), + didDocument: { + '@context': ['https://w3id.org/did/v1'], + id: expect.stringMatching(DID_INDY_REGEX), + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: expect.stringMatching(DID_INDY_REGEX), + id: expect.stringContaining('#verkey'), + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [expect.stringContaining('#verkey')], + service: undefined, + }, + }, + }) + + const did = didRegistrationResult.didState.did + if (!did) { + throw Error('did not defined') + } + + const didResolutionResult = await indyVdrIndyDidResolver.resolve(agentContext, did) + expect(JsonTransformer.toJSON(didResolutionResult)).toMatchObject({ + didDocument: { + '@context': ['https://w3id.org/did/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('can register a did:indy without services - did and verkey specified', async () => { + // Generate a seed and the indy did. This allows us to create a new did every time + // but still check if the created output document is as expected. + const seed = Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + + const keyPair = generateKeyPairFromSeed(TypedArrayEncoder.fromString(seed)) + const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(keyPair.publicKey) + + const { did, verkey } = indyDidFromNamespaceAndInitialKey( + 'pool:localtest', + Key.fromPublicKey(keyPair.publicKey, KeyType.Ed25519) + ) + const didRegistrationResult = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + did, + options: { + submitterDid: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', + submitterVerkey: signerKey.publicKeyBase58, + verkey, + }, + }) + + expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: { + didIndyNamespace: 'pool:localtest', + }, + didState: { + state: 'finished', + did, + didDocument: { + '@context': ['https://w3id.org/did/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, + }, + }, + }) + + const didResult = await indyVdrIndyDidResolver.resolve(agentContext, did) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': ['https://w3id.org/did/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('can register a did:indy with services - did and verkey specified - using attrib endpoint', async () => { + // Generate a seed and the indy did. This allows us to create a new did every time + // but still check if the created output document is as expected. + const seed = Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + + const key = await wallet.createKey({ seed: seed, keyType: KeyType.Ed25519 }) + const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(key.publicKey)) + const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(key.publicKey) + + const { did, verkey } = indyDidFromNamespaceAndInitialKey( + 'pool:localtest', + Key.fromPublicKey(key.publicKey, KeyType.Ed25519) + ) + + const didRegistrationResult = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + did, + options: { + submitterDid: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', + submitterVerkey: signerKey.publicKeyBase58, + useEndpointAttrib: true, + verkey, + services: [ + new DidDocumentService({ + id: `${did}#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }), + new DidCommV1Service({ + id: `${did}#did-communication`, + priority: 0, + recipientKeys: [`${did}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + accept: ['didcomm/aip2;env=rfc19'], + }), + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `${did}#didcomm-1`, + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + }), + ], + }, + }) + + const expectedDidDocument = { + '@context': ['https://w3id.org/did/v1', 'https://didcomm.org/messaging/contexts/v2'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + { + type: 'X25519KeyAgreementKey2019', + controller: did, + id: `${did}#key-agreement-1`, + publicKeyBase58: x25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: [ + { + id: `${did}#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + accept: ['didcomm/aip2;env=rfc19'], + id: `${did}#did-communication`, + priority: 0, + recipientKeys: [`${did}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + }, + { + accept: ['didcomm/v2'], + id: `${did}#didcomm-1`, + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + }, + ], + } + + expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: { + didIndyNamespace: 'pool:localtest', + }, + didState: { + state: 'finished', + did, + didDocument: expectedDidDocument, + }, + }) + + const didResult = await indyVdrIndyDidResolver.resolve(agentContext, did) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: expectedDidDocument, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) +}) diff --git a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts index e9f3aa4eab..c3b0eaacbe 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts @@ -1,10 +1,10 @@ import type { Key } from '@aries-framework/core' import { + IndyWallet, CacheModuleConfig, InMemoryLruCache, JsonTransformer, - IndyWallet, KeyType, SigningProviderRegistry, } from '@aries-framework/core' @@ -38,7 +38,7 @@ const agentContext = getAgentContext({ const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) -describe('IndyVdrSov', () => { +describe('indy-vdr DID Resolver E2E', () => { beforeAll(async () => { await indyVdrPoolService.connectToPools() @@ -46,7 +46,10 @@ describe('IndyVdrSov', () => { await wallet.createAndOpen(agentConfig.walletConfig) } - signerKey = await wallet.createKey({ seed: '000000000000000000000000Trustee9', keyType: KeyType.Ed25519 }) + signerKey = await wallet.createKey({ + seed: '000000000000000000000000Trustee9', + keyType: KeyType.Ed25519, + }) }) afterAll(async () => { diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index fea5b8fe0f..2ea85b5e04 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -13,6 +13,7 @@ import { indyVdrModuleConfig } from './helpers' const indyVdrPoolService = new IndyVdrPoolService(testLogger, indyVdrModuleConfig) const wallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) + const agentConfig = getAgentConfig('IndyVdrPoolService') const agentContext = getAgentContext({ wallet, agentConfig }) @@ -33,7 +34,10 @@ describe('IndyVdrPoolService', () => { await wallet.createAndOpen(agentConfig.walletConfig) } - signerKey = await wallet.createKey({ seed: '000000000000000000000000Trustee9', keyType: KeyType.Ed25519 }) + signerKey = await wallet.createKey({ + seed: '000000000000000000000000Trustee9', + keyType: KeyType.Ed25519, + }) }) afterAll(async () => { From 4ac533231ff8126c73ccc071adbf5a415fd3d6e9 Mon Sep 17 00:00:00 2001 From: "Jason C. Leach" Date: Wed, 15 Feb 2023 14:11:41 -0800 Subject: [PATCH 045/139] feat: add devcontainer support (#1282) Signed-off-by: Jason C. Leach --- .devcontainer/Dockerfile | 18 ++++++++++++++++++ .devcontainer/devcontainer.env | 7 +++++++ .devcontainer/devcontainer.json | 8 ++++++++ DEVREADME.md | 14 ++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.env create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000..3d51f0a5a0 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,18 @@ +# arm + amd compatible Dockerfile +FROM ghcr.io/findy-network/findy-base:indy-1.16.ubuntu-18.04 AS indy-base + +FROM ubuntu:18.04 + +# install indy deps and files from base +RUN apt-get update && apt-get install -y libsodium23 libssl1.1 libzmq5 git zsh + +COPY --from=indy-base /usr/include/indy /usr/include/indy +COPY --from=indy-base /usr/lib/libindy.a /usr/lib/libindy.a +COPY --from=indy-base /usr/lib/libindy.so /usr/lib/libindy.so + +RUN apt-get install -y curl python3 build-essential ca-certificates && \ + curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash && \ + export NVM_DIR="$HOME/.nvm" && \ + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ + nvm install v16 && \ + npm install yarn -g diff --git a/.devcontainer/devcontainer.env b/.devcontainer/devcontainer.env new file mode 100644 index 0000000000..1b31a5ab62 --- /dev/null +++ b/.devcontainer/devcontainer.env @@ -0,0 +1,7 @@ +# +# Any environment variables that the container needs +# go in here. +# +# Example(s) +# GENESIS_TXN_PATH=/work/network/genesis/local-genesis.txn +# diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..1728c1a7cc --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,8 @@ +{ + "build": { + "dockerfile": "Dockerfile" + }, + "runArgs": ["--env-file", ".devcontainer/devcontainer.env"], + "workspaceMount": "source=${localWorkspaceFolder},target=/work,type=bind", + "workspaceFolder": "/work" +} diff --git a/DEVREADME.md b/DEVREADME.md index cea95a96da..73550b193e 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -2,6 +2,20 @@ This file is intended for developers working on the internals of the framework. If you're just looking how to get started with the framework, see the [docs](./docs) +# Environment Setup + +## VSCode devContainer + +This project comes with a [.devcontainer](./devcontainer) to make it as easy as possible to setup your dev environment and begin contributing to this project. + +All the [environment variables](https://code.visualstudio.com/remote/advancedcontainers/environment-variables) noted below can be added to [devcontainer.env](./devcontainer.env) and exposed to the development docker container. + +When running in a container your project root directory will be `/work`. Use this to correctly path any environment variables, for example: + +```console +GENESIS_TXN_PATH=/work/network/genesis/local-genesis.txn +``` + ## Running tests Test are executed using jest. Some test require either the **mediator agents** or the **ledger** to be running. When running tests that require a connection to the ledger pool, you need to set the `TEST_AGENT_PUBLIC_DID_SEED` and `GENESIS_TXN_PATH` environment variables. From 3e02227a7b23677e9886eb1c03d1a3ec154947a9 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 15 Feb 2023 20:14:47 -0300 Subject: [PATCH 046/139] fix: imports from core (#1303) Signed-off-by: Ariel Gentile --- .../src/services/AnonCredsRsHolderService.ts | 7 ++- packages/askar/src/AskarModuleConfig.ts | 44 ------------------- packages/askar/src/index.ts | 1 - packages/askar/src/wallet/AskarWallet.ts | 5 +-- .../AskarWalletPostgresStorageConfig.ts | 2 +- packages/core/src/index.ts | 1 + 6 files changed, 7 insertions(+), 53 deletions(-) delete mode 100644 packages/askar/src/AskarModuleConfig.ts diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index 5f6530c58a..4a34024851 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -24,7 +24,7 @@ import { AnonCredsLinkSecretRepository, AnonCredsCredentialRepository, } from '@aries-framework/anoncreds' -import { injectable } from '@aries-framework/core' +import { utils, injectable } from '@aries-framework/core' import { CredentialRequestMetadata, Credential, @@ -40,7 +40,6 @@ import { Schema, } from '@hyperledger/anoncreds-shared' -import { uuid } from '../../../core/src/utils/uuid' import { AnonCredsRsError } from '../errors/AnonCredsRsError' @injectable() @@ -51,7 +50,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { ): Promise { try { return { - linkSecretId: options?.linkSecretId ?? uuid(), + linkSecretId: options?.linkSecretId ?? utils.uuid(), linkSecretValue: JSON.parse(MasterSecret.create().toJson()).value.ms, } } catch (error) { @@ -230,7 +229,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { ? RevocationRegistryDefinition.load(JSON.stringify(revocationRegistry.definition)) : undefined - const credentialId = options.credentialId ?? uuid() + const credentialId = options.credentialId ?? utils.uuid() const processedCredential = Credential.load(JSON.stringify(credential)).process({ credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), credentialRequestMetadata: CredentialRequestMetadata.load(JSON.stringify(credentialRequestMetadata)), diff --git a/packages/askar/src/AskarModuleConfig.ts b/packages/askar/src/AskarModuleConfig.ts deleted file mode 100644 index c2104eff8e..0000000000 --- a/packages/askar/src/AskarModuleConfig.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { AriesAskar } from './types' - -/** - * AskarModuleConfigOptions defines the interface for the options of the AskarModuleConfig class. - */ -export interface AskarModuleConfigOptions { - /** - * Implementation of the Askar interface according to aries-askar JavaScript wrapper. - * - * - * ## Node.JS - * - * ```ts - * import { NodeJSAriesAskar } from 'aries-askar-nodejs' - * - * const askarModule = new AskarModule({ - * askar: new NodeJSAriesAskar() - * }) - * ``` - * - * ## React Native - * - * ```ts - * import { ReactNativeAriesAskar } from 'aries-askar-react-native' - * - * const askarModule = new AskarModule({ - * askar: new ReactNativeAriesAskar() - * }) - * ``` - */ - askar: AriesAskar -} - -export class AskarModuleConfig { - private options: AskarModuleConfigOptions - - public constructor(options: AskarModuleConfigOptions) { - this.options = options - } - - public get askar() { - return this.options.askar - } -} diff --git a/packages/askar/src/index.ts b/packages/askar/src/index.ts index d7afa60eab..ed7baf9247 100644 --- a/packages/askar/src/index.ts +++ b/packages/askar/src/index.ts @@ -6,4 +6,3 @@ export { AskarStorageService } from './storage' // Module export { AskarModule } from './AskarModule' -export { AskarModuleConfig } from './AskarModuleConfig' diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index c84370cadf..baa17838a4 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -49,7 +49,6 @@ const isError = (error: unknown): error is Error => error instanceof Error import { inject, injectable } from 'tsyringe' -import { encodeToBase58, decodeFromBase58 } from '../../../core/src/utils/base58' import { askarErrors, isAskarError, @@ -364,7 +363,7 @@ export class AskarWallet implements Wallet { const key = seed ? AskarKey.fromSeed({ seed: Buffer.from(seed), algorithm }) : AskarKey.generate(algorithm) // Store key - await this.session.insertKey({ key, name: encodeToBase58(key.publicBytes) }) + await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(key.publicBytes) }) return Key.fromPublicKey(key.publicBytes, keyType) } else { // Check if there is a signing key provider for the specified key type. @@ -633,7 +632,7 @@ export class AskarWallet implements Wallet { ) const sender_x = AskarKey.fromPublicBytes({ algorithm: KeyAlgs.Ed25519, - publicKey: decodeFromBase58(senderKey), + publicKey: TypedArrayEncoder.fromBase58(senderKey), }).convertkey({ algorithm: KeyAlgs.X25519 }) payloadKey = CryptoBox.open({ diff --git a/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts b/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts index a9a9aab91f..2ca48f0c56 100644 --- a/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts +++ b/packages/askar/src/wallet/AskarWalletPostgresStorageConfig.ts @@ -1,4 +1,4 @@ -import type { WalletStorageConfig } from '../../../core/src/types' +import type { WalletStorageConfig } from '@aries-framework/core' export interface AskarWalletPostgresConfig { host: string diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 42cf5984b1..38f4bc2fa8 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -26,6 +26,7 @@ export type { JsonValue, WalletConfigRekey, WalletExportImportConfig, + WalletStorageConfig, } from './types' export { DidCommMimeType, KeyDerivationMethod } from './types' export type { FileSystem, DownloadToFileOptions } from './storage/FileSystem' From 1d782f54bbb4abfeb6b6db6cd4f7164501b6c3d9 Mon Sep 17 00:00:00 2001 From: Victor Anene <62852943+Vickysomtee@users.noreply.github.com> Date: Fri, 17 Feb 2023 20:01:35 +0100 Subject: [PATCH 047/139] feat: add fetch indy schema method (#1290) Signed-off-by: Victor Anene --- packages/core/package.json | 2 +- packages/indy-sdk/package.json | 2 +- .../services/IndySdkAnonCredsRegistry.ts | 101 ++++++++-- packages/indy-sdk/src/ledger/IndySdkPool.ts | 2 +- .../indy-sdk-anoncreds-registry.e2e.test.ts | 187 ++++++++++++++++++ .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 49 ++++- .../indy-vdr-anoncreds-registry.e2e.test.ts | 3 +- packages/react-native/package.json | 6 +- 8 files changed, 324 insertions(+), 28 deletions(-) create mode 100644 packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts diff --git a/packages/core/package.json b/packages/core/package.json index 8a54af7192..e4295e0d2a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,7 +30,7 @@ "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", - "@types/indy-sdk": "1.16.24", + "@types/indy-sdk": "1.16.26", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.6", "abort-controller": "^3.0.0", diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index d996bcd771..7646c74776 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -26,7 +26,7 @@ "dependencies": { "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", - "@types/indy-sdk": "1.16.24", + "@types/indy-sdk": "1.16.26", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 7ddb4a5db5..664b86e4de 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -160,7 +160,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { schemaMetadata: { // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. // For this reason we return it in the metadata. - indyLedgerSeqNo: schema.seqNo, + indyLedgerSeqNo: response.result.txnMetadata.seqNo, didIndyNamespace: pool.didIndyNamespace, }, } @@ -212,26 +212,43 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) const [, credentialDefinition] = await indySdk.parseGetCredDefResponse(response) - agentContext.config.logger.debug( - `Got credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, - { - credentialDefinition, + const { schema } = await this.fetchIndySchemaWithSeqNo(agentContext, Number(credentialDefinition.schemaId), did) + + if (credentialDefinition && schema) { + agentContext.config.logger.debug( + `Got credential definition '${credentialDefinitionId}' from ledger '${pool.didIndyNamespace}'`, + { + credentialDefinition, + } + ) + + return { + credentialDefinitionId: credentialDefinition.id, + credentialDefinition: { + issuerId: didFromCredentialDefinitionId(credentialDefinition.id), + schemaId: schema.schemaId, + tag: credentialDefinition.tag, + type: 'CL', + value: credentialDefinition.value, + }, + credentialDefinitionMetadata: { + didIndyNamespace: pool.didIndyNamespace, + }, + resolutionMetadata: {}, } - ) + } + + agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { + credentialDefinitionId, + }) return { - credentialDefinitionId: credentialDefinition.id, - credentialDefinition: { - issuerId: didFromCredentialDefinitionId(credentialDefinition.id), - schemaId: credentialDefinition.schemaId, - tag: credentialDefinition.tag, - type: 'CL', - value: credentialDefinition.value, - }, - credentialDefinitionMetadata: { - didIndyNamespace: pool.didIndyNamespace, + credentialDefinitionId, + credentialDefinitionMetadata: {}, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition`, }, - resolutionMetadata: {}, } } catch (error) { agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { @@ -305,7 +322,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const request = await indySdk.buildCredDefRequest(options.credentialDefinition.issuerId, { id: credentialDefinitionId, - schemaId: options.credentialDefinition.schemaId, + schemaId: schemaMetadata.indyLedgerSeqNo.toString(), tag: options.credentialDefinition.tag, type: options.credentialDefinition.type, value: options.credentialDefinition.value, @@ -509,6 +526,54 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } } } + + private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, seqNo: number, did: string) { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) + agentContext.config.logger.debug(`Getting transaction with seqNo '${seqNo}' from ledger '${pool.didIndyNamespace}'`) + + const request = await indySdk.buildGetTxnRequest(did, 'DOMAIN', seqNo) + + agentContext.config.logger.trace(`Submitting get transaction request to ledger '${pool.didIndyNamespace}'`) + const response = await indySdkPoolService.submitReadRequest(pool, request) + + const schema = response.result.data as SchemaType + + if (schema.txn.type !== '101') { + agentContext.config.logger.error(`Could not get schema from ledger for seq no ${seqNo}'`) + return {} + } + + const schemaId = getLegacySchemaId(did, schema.txn.data.data.name, schema.txn.data.data.version) + + return { + schema: { + schemaId, + attr_name: schema.txn.data.data.attr_names, + name: schema.txn.data.data.name, + version: schema.txn.data.data.version, + issuerId: did, + seqNo, + }, + indyNamespace: pool.didIndyNamespace, + } + } +} + +interface SchemaType { + txn: { + data: { + data: { + attr_names: string[] + version: string + name: string + } + } + + type: string + } } export interface IndySdkRegisterSchemaOptions extends RegisterSchemaOptions { diff --git a/packages/indy-sdk/src/ledger/IndySdkPool.ts b/packages/indy-sdk/src/ledger/IndySdkPool.ts index a24a1c7ba5..bae9d8e17b 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPool.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPool.ts @@ -54,7 +54,7 @@ export class IndySdkPool { } public get didIndyNamespace(): string { - return this.didIndyNamespace + return this.config.indyNamespace } public get id() { diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts new file mode 100644 index 0000000000..c6b7ced0f1 --- /dev/null +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -0,0 +1,187 @@ +import { Agent } from '@aries-framework/core' +import indySdk from 'indy-sdk' +import { Subject } from 'rxjs' + +import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' +import { IndySdkModule } from '../src' +import { IndySdkAnonCredsRegistry } from '../src/anoncreds/services/IndySdkAnonCredsRegistry' +import { IndySdkPoolService } from '../src/ledger/IndySdkPoolService' + +const agentConfig = getAgentConfig('IndySdkAnonCredsRegistry') + +const indySdkPoolService = new IndySdkPoolService( + indySdk, + agentConfig.logger, + new Subject(), + new agentConfig.agentDependencies.FileSystem() +) + +const agent = new Agent({ + config: agentConfig, + dependencies: agentDependencies, + modules: { + indySdk: new IndySdkModule({ + indySdk, + }), + }, +}) + +agent.dependencyManager.registerInstance(IndySdkPoolService, indySdkPoolService) +indySdkPoolService.setPools(agentConfig.indyLedgers) +const indySdkAnonCredsRegistry = new IndySdkAnonCredsRegistry() + +describe('IndySdkAnonCredsRegistry', () => { + beforeAll(async () => { + await agent.initialize() + await indySdkPoolService.connectToPools() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + test('it works! :)', async () => { + const dynamicVersion = `1.${Math.random() * 100}` + + const schemaResult = await indySdkAnonCredsRegistry.registerSchema(agent.context, { + schema: { + attrNames: ['name'], + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + name: 'test - 11', + version: dynamicVersion, + }, + options: { + didIndyNamespace: agentConfig.indyLedgers[0].indyNamespace, + }, + }) + + expect(schemaResult).toMatchObject({ + schemaState: { + state: 'finished', + schema: { + attrNames: ['name'], + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + name: 'test - 11', + version: dynamicVersion, + }, + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + }, + registrationMetadata: {}, + schemaMetadata: { + indyLedgerSeqNo: expect.any(Number), + didIndyNamespace: 'pool:localtest', + }, + }) + + const schemaResponse = await indySdkAnonCredsRegistry.getSchema( + agent.context, + `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}` + ) + + expect(schemaResponse).toMatchObject({ + schema: { + attrNames: ['name'], + name: 'test - 11', + version: dynamicVersion, + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + }, + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: 'pool:localtest', + indyLedgerSeqNo: expect.any(Number), + }, + }) + + const credentialDefinitionResult = await indySdkAnonCredsRegistry.registerCredentialDefinition(agent.context, { + credentialDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + tag: 'TAG', + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + options: { + didIndyNamespace: 'pool:localtest', + }, + }) + + expect(credentialDefinitionResult).toMatchObject({ + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + credentialDefinitionState: { + credentialDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + tag: 'TAG', + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + state: 'finished', + }, + registrationMetadata: {}, + }) + + const credentialDefinitionResponse = await indySdkAnonCredsRegistry.getCredentialDefinition( + agent.context, + credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId as string + ) + + expect(credentialDefinitionResponse).toMatchObject({ + credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + credentialDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + tag: 'TAG', + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + }) +}) diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 1106b498cd..ba105104fa 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -17,6 +17,7 @@ import { SchemaRequest, GetCredentialDefinitionRequest, CredentialDefinitionRequest, + GetTransactionRequest, } from '@hyperledger/indy-vdr-shared' import { IndyVdrPoolService } from '../pool' @@ -218,12 +219,14 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const response = await pool.submitReadRequest(request) - if (response.result.data) { + const schema = await this.fetchIndySchemaWithSeqNo(agentContext, response.result.ref, did) + + if (response.result.data && schema) { return { credentialDefinitionId: credentialDefinitionId, credentialDefinition: { issuerId: didFromCredentialDefinitionId(credentialDefinitionId), - schemaId: response.result.ref.toString(), + schemaId: schema.schema.schemaId, tag: response.result.tag, type: 'CL', value: response.result.data, @@ -416,6 +419,48 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { revocationRegistryDefinitionMetadata: {}, } } + + private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, seqNo: number, did: string) { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug(`Getting transaction with seqNo '${seqNo}' from ledger '${pool.indyNamespace}'`) + // ledgerType 1 is domain ledger + const request = new GetTransactionRequest({ ledgerType: 1, seqNo }) + + agentContext.config.logger.trace(`Submitting get transaction request to ledger '${pool.indyNamespace}'`) + const response = await pool.submitReadRequest(request) + + if (response.result.data?.txn.type !== '101') { + agentContext.config.logger.error(`Could not get schema from ledger for seq no ${seqNo}'`) + return null + } + + const schema = response.result.data?.txn.data as SchemaType + + const schemaId = getLegacySchemaId(did, schema.data.name, schema.data.version) + + return { + schema: { + schemaId, + attr_name: schema.data.attr_names, + name: schema.data.name, + version: schema.data.version, + issuerId: did, + seqNo, + }, + indyNamespace: pool.indyNamespace, + } + } +} + +interface SchemaType { + data: { + attr_names: string[] + version: string + name: string + } } export interface IndyVdrRegisterSchemaOptions extends RegisterSchemaOptions { diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index d45597ce8f..f7d3bbc9fa 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -154,8 +154,7 @@ describe('IndyVdrAnonCredsRegistry', () => { credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, credentialDefinition: { issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - // FIXME: this will change when https://github.com/hyperledger/aries-framework-javascript/issues/1259 is merged - schemaId: `${schemaResponse.schemaMetadata.indyLedgerSeqNo}`, + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, tag: 'TAG', type: 'CL', value: { diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 5eee471421..3fa303bf91 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -29,9 +29,9 @@ "events": "^3.3.0" }, "devDependencies": { - "@types/indy-sdk-react-native": "npm:@types/indy-sdk@1.16.24", + "@types/indy-sdk-react-native": "npm:@types/indy-sdk@1.16.26", "@types/react-native": "^0.64.10", - "indy-sdk-react-native": "^0.3.0", + "indy-sdk-react-native": "^0.3.1", "react": "17.0.1", "react-native": "0.64.2", "react-native-fs": "^2.18.0", @@ -40,7 +40,7 @@ "typescript": "~4.9.4" }, "peerDependencies": { - "indy-sdk-react-native": "^0.3.0", + "indy-sdk-react-native": "^0.3.1", "react-native-fs": "^2.18.0", "react-native-get-random-values": "^1.7.0" } From 229ed1b9540ca0c9380b5cca6c763fefd6628960 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 17 Feb 2023 21:31:47 +0100 Subject: [PATCH 048/139] fix: thread id improvements (#1311) Signed-off-by: Timo Glastra --- .../src/services/ActionMenuService.ts | 8 ++++---- packages/core/src/agent/Dispatcher.ts | 19 ++++++++++++++++--- packages/core/src/agent/MessageReceiver.ts | 2 +- .../messages/DidExchangeRequestMessage.ts | 1 - .../connections/services/ConnectionService.ts | 4 ++-- .../connections/services/TrustPingService.ts | 2 +- .../protocol/v1/V1CredentialProtocol.ts | 2 +- .../oob/messages/HandshakeReuseMessage.ts | 1 - packages/core/src/types.ts | 3 +++ .../src/services/QuestionAnswerService.ts | 4 ++-- .../dummy/services/DummyService.ts | 4 ++-- 11 files changed, 32 insertions(+), 18 deletions(-) diff --git a/packages/action-menu/src/services/ActionMenuService.ts b/packages/action-menu/src/services/ActionMenuService.ts index 8282bc60bf..42da57e3ad 100644 --- a/packages/action-menu/src/services/ActionMenuService.ts +++ b/packages/action-menu/src/services/ActionMenuService.ts @@ -63,7 +63,7 @@ export class ActionMenuService { connectionId: options.connection.id, role: ActionMenuRole.Requester, state: ActionMenuState.AwaitingRootMenu, - threadId: menuRequestMessage.id, + threadId: menuRequestMessage.threadId, }) await this.actionMenuRepository.save(agentContext, actionMenuRecord) @@ -102,7 +102,7 @@ export class ActionMenuService { connectionId: connection.id, role: ActionMenuRole.Responder, state: ActionMenuState.PreparingRootMenu, - threadId: menuRequestMessage.id, + threadId: menuRequestMessage.threadId, }) await this.actionMenuRepository.save(agentContext, actionMenuRecord) @@ -157,7 +157,7 @@ export class ActionMenuService { role: ActionMenuRole.Responder, state: ActionMenuState.AwaitingSelection, menu: options.menu, - threadId: menuMessage.id, + threadId: menuMessage.threadId, }) await this.actionMenuRepository.save(agentContext, actionMenuRecord) @@ -203,7 +203,7 @@ export class ActionMenuService { connectionId: connection.id, role: ActionMenuRole.Requester, state: ActionMenuState.PreparingSelection, - threadId: menuMessage.id, + threadId: menuMessage.threadId, menu: new ActionMenu({ title: menuMessage.title, description: menuMessage.description, diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index ee04bc0e3c..50ff6da42e 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -7,6 +7,7 @@ import { InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error/AriesFrameworkError' import { Logger } from '../logger' import { injectable, inject } from '../plugins' +import { parseMessageType } from '../utils/messageType' import { ProblemReportMessage } from './../modules/problem-reports/messages/ProblemReportMessage' import { EventEmitter } from './EventEmitter' @@ -57,9 +58,21 @@ class Dispatcher { const problemReportMessage = error.problemReport if (problemReportMessage instanceof ProblemReportMessage && messageContext.connection) { - problemReportMessage.setThread({ - threadId: message.threadId, - }) + const { protocolUri: problemReportProtocolUri } = parseMessageType(problemReportMessage.type) + const { protocolUri: inboundProtocolUri } = parseMessageType(messageContext.message.type) + + // If the inbound protocol uri is the same as the problem report protocol uri, we can see the interaction as the same thread + // However if it is no the same we should see it as a new thread, where the inbound message `@id` is the parentThreadId + if (inboundProtocolUri === problemReportProtocolUri) { + problemReportMessage.setThread({ + threadId: message.threadId, + }) + } else { + problemReportMessage.setThread({ + parentThreadId: message.id, + }) + } + outboundMessage = new OutboundMessageContext(problemReportMessage, { agentContext, connection: messageContext.connection, diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 55d1368fc0..6dfd073c3f 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -278,7 +278,7 @@ export class MessageReceiver { }, }) problemReportMessage.setThread({ - threadId: plaintextMessage['@id'], + parentThreadId: plaintextMessage['@id'], }) const outboundMessageContext = new OutboundMessageContext(problemReportMessage, { agentContext, connection }) if (outboundMessageContext) { diff --git a/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts b/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts index 4687bc0da4..c22729a272 100644 --- a/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts +++ b/packages/core/src/modules/connections/messages/DidExchangeRequestMessage.ts @@ -35,7 +35,6 @@ export class DidExchangeRequestMessage extends AgentMessage { this.did = options.did this.setThread({ - threadId: this.id, parentThreadId: options.parentThreadId, }) } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 26e971e393..539fabac8c 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -120,7 +120,7 @@ export class ConnectionService { }) connectionRequest.setThread({ - threadId: connectionRequest.id, + threadId: connectionRequest.threadId, parentThreadId: outOfBandInvitation.id, }) @@ -136,7 +136,7 @@ export class ConnectionService { outOfBandId: outOfBandRecord.id, invitationDid, imageUrl: outOfBandInvitation.imageUrl, - threadId: connectionRequest.id, + threadId: connectionRequest.threadId, }) await this.updateState(agentContext, connectionRecord, DidExchangeState.RequestSent) diff --git a/packages/core/src/modules/connections/services/TrustPingService.ts b/packages/core/src/modules/connections/services/TrustPingService.ts index 5d4b10eb20..5236a2c83d 100644 --- a/packages/core/src/modules/connections/services/TrustPingService.ts +++ b/packages/core/src/modules/connections/services/TrustPingService.ts @@ -28,7 +28,7 @@ export class TrustPingService { if (message.responseRequested) { const response = new TrustPingResponseMessage({ - threadId: message.id, + threadId: message.threadId, }) return new OutboundMessageContext(response, { agentContext, connection }) diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts index 6ee07a7588..1c0320070f 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts @@ -547,7 +547,7 @@ export class V1CredentialProtocol // No credential record exists with thread id credentialRecord = new CredentialExchangeRecord({ connectionId: connection?.id, - threadId: offerMessage.id, + threadId: offerMessage.threadId, state: CredentialState.OfferReceived, protocolVersion: 'v1', }) diff --git a/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts b/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts index 363e4bf0fe..c70e8b2832 100644 --- a/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts +++ b/packages/core/src/modules/oob/messages/HandshakeReuseMessage.ts @@ -13,7 +13,6 @@ export class HandshakeReuseMessage extends AgentMessage { if (options) { this.id = options.id ?? this.generateId() this.setThread({ - threadId: this.id, parentThreadId: options.parentThreadId, }) } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index b454c7963e..23fe599c84 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -162,6 +162,9 @@ export type ProtocolVersion = `${number}.${number}` export interface PlaintextMessage { '@type': string '@id': string + '~thread'?: { + thid?: string + } [key: string]: unknown } diff --git a/packages/question-answer/src/services/QuestionAnswerService.ts b/packages/question-answer/src/services/QuestionAnswerService.ts index c471ca3cca..5223ab520f 100644 --- a/packages/question-answer/src/services/QuestionAnswerService.ts +++ b/packages/question-answer/src/services/QuestionAnswerService.ts @@ -53,7 +53,7 @@ export class QuestionAnswerService { const questionAnswerRecord = await this.createRecord({ questionText: questionMessage.questionText, questionDetail: questionMessage.questionDetail, - threadId: questionMessage.id, + threadId: questionMessage.threadId, connectionId: connectionId, role: QuestionAnswerRole.Questioner, signatureRequired: false, @@ -97,7 +97,7 @@ export class QuestionAnswerService { questionText: questionMessage.questionText, questionDetail: questionMessage.questionDetail, connectionId: connection?.id, - threadId: questionMessage.id, + threadId: questionMessage.threadId, role: QuestionAnswerRole.Responder, signatureRequired: false, state: QuestionAnswerState.QuestionReceived, diff --git a/samples/extension-module/dummy/services/DummyService.ts b/samples/extension-module/dummy/services/DummyService.ts index 0aa7a56747..580565296c 100644 --- a/samples/extension-module/dummy/services/DummyService.ts +++ b/samples/extension-module/dummy/services/DummyService.ts @@ -41,7 +41,7 @@ export class DummyService { // Create record const record = new DummyRecord({ connectionId: connectionRecord.id, - threadId: message.id, + threadId: message.threadId, state: DummyState.Init, }) @@ -79,7 +79,7 @@ export class DummyService { // Create record const record = new DummyRecord({ connectionId: connectionRecord.id, - threadId: messageContext.message.id, + threadId: messageContext.message.threadId, state: DummyState.RequestReceived, }) From af384e8a92f877c647999f9356b72a8017308230 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 18 Feb 2023 01:32:45 +0100 Subject: [PATCH 049/139] fix: loosen base64 validation (#1312) Signed-off-by: Timo Glastra --- .../core/src/decorators/attachment/Attachment.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/packages/core/src/decorators/attachment/Attachment.ts b/packages/core/src/decorators/attachment/Attachment.ts index 50a91e8edb..53ea3caf9f 100644 --- a/packages/core/src/decorators/attachment/Attachment.ts +++ b/packages/core/src/decorators/attachment/Attachment.ts @@ -1,17 +1,7 @@ import type { JwsGeneralFormat } from '../../crypto/JwsTypes' import { Expose, Type } from 'class-transformer' -import { - IsBase64, - IsDate, - IsHash, - IsInstance, - IsInt, - IsMimeType, - IsOptional, - IsString, - ValidateNested, -} from 'class-validator' +import { IsDate, IsHash, IsInstance, IsInt, IsMimeType, IsOptional, IsString, ValidateNested } from 'class-validator' import { Jws } from '../../crypto/JwsTypes' import { AriesFrameworkError } from '../../error' @@ -44,7 +34,7 @@ export class AttachmentData { * Base64-encoded data, when representing arbitrary content inline instead of via links. Optional. */ @IsOptional() - @IsBase64() + @IsString() public base64?: string /** From ff5596d0631e93746494c017797d0191b6bdb0b1 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 17 Feb 2023 23:10:09 -0300 Subject: [PATCH 050/139] feat!: add data, cache and temp dirs to FileSystem (#1306) Signed-off-by: Ariel Gentile BREAKING CHANGE: Agent-produced files will now be divided in different system paths depending on their nature: data, temp and cache. Previously, they were located at a single location, defaulting to a temporary directory. If you specified a custom path in `FileSystem` object constructor, you now must provide an object containing `baseDataPath`, `baseTempPath` and `baseCachePath`. They can point to the same path, although it's recommended to specify different path to avoid future file clashes. --- packages/anoncreds/src/utils/tails.ts | 6 ++-- packages/askar/src/utils/askarWalletConfig.ts | 13 ++++++-- packages/askar/src/wallet/AskarWallet.ts | 4 +-- .../indy/services/IndyUtilitiesService.ts | 2 +- packages/core/src/modules/ledger/IndyPool.ts | 2 +- packages/core/src/storage/FileSystem.ts | 5 ++- .../src/storage/migration/UpdateAssistant.ts | 7 +++- .../storage/migration/__tests__/0.1.test.ts | 24 -------------- .../storage/migration/__tests__/0.2.test.ts | 19 ----------- .../storage/migration/__tests__/0.3.test.ts | 4 --- .../migration/__tests__/backup.test.ts | 15 ++++++--- packages/indy-sdk/src/ledger/IndySdkPool.ts | 2 +- packages/node/src/NodeFileSystem.ts | 33 +++++++++++++------ packages/node/tests/NodeFileSystem.test.ts | 2 +- packages/react-native/package.json | 4 +-- .../react-native/src/ReactNativeFileSystem.ts | 30 ++++++++++++++--- 16 files changed, 91 insertions(+), 81 deletions(-) diff --git a/packages/anoncreds/src/utils/tails.ts b/packages/anoncreds/src/utils/tails.ts index 9ae29aa8e4..e706f00914 100644 --- a/packages/anoncreds/src/utils/tails.ts +++ b/packages/anoncreds/src/utils/tails.ts @@ -2,11 +2,11 @@ import type { AgentContext, FileSystem } from '@aries-framework/core' import { TypedArrayEncoder, InjectionSymbols } from '@aries-framework/core' -const getTailsFilePath = (basePath: string, tailsHash: string) => `${basePath}/afj/anoncreds/tails/${tailsHash}` +const getTailsFilePath = (cachePath: string, tailsHash: string) => `${cachePath}/anoncreds/tails/${tailsHash}` export function tailsFileExists(agentContext: AgentContext, tailsHash: string): Promise { const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) - const tailsFilePath = getTailsFilePath(fileSystem.basePath, tailsHash) + const tailsFilePath = getTailsFilePath(fileSystem.cachePath, tailsHash) return fileSystem.exists(tailsFilePath) } @@ -27,7 +27,7 @@ export async function downloadTailsFile( // hash is used as file identifier const tailsExists = await tailsFileExists(agentContext, tailsHashBase58) - const tailsFilePath = getTailsFilePath(fileSystem.basePath, tailsHashBase58) + const tailsFilePath = getTailsFilePath(fileSystem.cachePath, tailsHashBase58) agentContext.config.logger.debug( `Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${tailsFilePath}` ) diff --git a/packages/askar/src/utils/askarWalletConfig.ts b/packages/askar/src/utils/askarWalletConfig.ts index 2337988f26..b6e885ebe5 100644 --- a/packages/askar/src/utils/askarWalletConfig.ts +++ b/packages/askar/src/utils/askarWalletConfig.ts @@ -18,7 +18,16 @@ export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod?: KeyDer return correspondenceTable[keyDerivationMethod] as StoreKeyMethod } -export const uriFromWalletConfig = (walletConfig: WalletConfig, basePath: string): { uri: string; path?: string } => { +/** + * Creates a proper askar wallet URI value based on walletConfig + * @param walletConfig WalletConfig object + * @param afjDataPath framework data path (used in case walletConfig.storage.path is undefined) + * @returns string containing the askar wallet URI + */ +export const uriFromWalletConfig = ( + walletConfig: WalletConfig, + afjDataPath: string +): { uri: string; path?: string } => { let uri = '' let path @@ -31,7 +40,7 @@ export const uriFromWalletConfig = (walletConfig: WalletConfig, basePath: string if (walletConfig.storage.inMemory) { uri = 'sqlite://:memory:' } else { - path = `${(walletConfig.storage.path as string) ?? basePath + '/wallet'}/${walletConfig.id}/sqlite.db` + path = (walletConfig.storage.path as string) ?? `${afjDataPath}/wallet/${walletConfig.id}/sqlite.db` uri = `sqlite://${path}` } } else if (walletConfig.storage.type === 'postgres') { diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index baa17838a4..38030c6ab7 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -289,7 +289,7 @@ export class AskarWallet implements Wallet { } try { - const { uri } = uriFromWalletConfig(this.walletConfig, this.fileSystem.basePath) + const { uri } = uriFromWalletConfig(this.walletConfig, this.fileSystem.dataPath) await Store.remove(uri) } catch (error) { const errorMessage = `Error deleting wallet '${this.walletConfig.id}': ${error.message}` @@ -689,7 +689,7 @@ export class AskarWallet implements Wallet { } private async getAskarWalletConfig(walletConfig: WalletConfig) { - const { uri, path } = uriFromWalletConfig(walletConfig, this.fileSystem.basePath) + const { uri, path } = uriFromWalletConfig(walletConfig, this.fileSystem.dataPath) // Make sure path exists before creating the wallet if (path) { diff --git a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts index eef01ccfd2..44b9713352 100644 --- a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts +++ b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts @@ -63,7 +63,7 @@ export class IndyUtilitiesService { public async downloadTails(hash: string, tailsLocation: string): Promise { try { this.logger.debug(`Checking to see if tails file for URL ${tailsLocation} has been stored in the FileSystem`) - const filePath = `${this.fileSystem.basePath}/afj/tails/${hash}` + const filePath = `${this.fileSystem.cachePath}/tails/${hash}` const tailsExists = await this.fileSystem.exists(filePath) this.logger.debug(`Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${filePath}`) diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts index c3d2249799..d8abffc113 100644 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ b/packages/core/src/modules/ledger/IndyPool.ts @@ -181,7 +181,7 @@ export class IndyPool { if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath // Determine the genesisPath - const genesisPath = this.fileSystem.basePath + `/afj/genesis-${this.poolConfig.id}.txn` + const genesisPath = this.fileSystem.tempPath + `/genesis-${this.poolConfig.id}.txn` // Store genesis data if provided if (this.poolConfig.genesisTransactions) { await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) diff --git a/packages/core/src/storage/FileSystem.ts b/packages/core/src/storage/FileSystem.ts index c5996e78b2..a6eeb08e48 100644 --- a/packages/core/src/storage/FileSystem.ts +++ b/packages/core/src/storage/FileSystem.ts @@ -5,11 +5,14 @@ export interface DownloadToFileOptions { } export interface FileSystem { - readonly basePath: string + readonly dataPath: string + readonly cachePath: string + readonly tempPath: string exists(path: string): Promise createDirectory(path: string): Promise write(path: string, data: string): Promise read(path: string): Promise + delete(path: string): Promise downloadToFile(url: string, path: string, options?: DownloadToFileOptions): Promise } diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index f4647d4891..3c08c2fe64 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -157,6 +157,8 @@ export class UpdateAssistant = BaseAgent> { `Successfully updated agent storage from version ${update.fromVersion} to version ${update.toVersion}` ) } + // Delete backup file, as it is not needed anymore + await this.fileSystem.delete(this.getBackupPath(updateIdentifier)) } catch (error) { this.agent.config.logger.fatal('An error occurred while updating the wallet. Restoring backup', { error, @@ -164,6 +166,9 @@ export class UpdateAssistant = BaseAgent> { // In the case of an error we want to restore the backup await this.restoreBackup(updateIdentifier) + // Delete backup file, as wallet was already restored (backup-error file will persist though) + await this.fileSystem.delete(this.getBackupPath(updateIdentifier)) + throw error } } catch (error) { @@ -192,7 +197,7 @@ export class UpdateAssistant = BaseAgent> { } private getBackupPath(backupIdentifier: string) { - return `${this.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + return `${this.fileSystem.dataPath}/migration/backup/${backupIdentifier}` } private async createBackup(backupIdentifier: string) { diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index ad2dd0b837..f38ba94a87 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -48,8 +48,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy, @@ -79,10 +77,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot(mediationRoleUpdateStrategy) - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() } @@ -110,8 +104,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', @@ -142,10 +134,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() @@ -174,8 +162,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', @@ -206,10 +192,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() @@ -242,8 +224,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', @@ -274,10 +254,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index 08ed9dce64..9fb991253b 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -13,7 +13,6 @@ import { UpdateAssistant } from '../UpdateAssistant' const backupDate = new Date('2022-01-21T22:50:20.522Z') jest.useFakeTimers().setSystemTime(backupDate) -const backupIdentifier = backupDate.getTime() const walletConfig = { id: `Wallet: 0.2 Update`, @@ -46,8 +45,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', @@ -83,10 +80,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() @@ -119,8 +112,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - // We need to manually initialize the wallet as we're using the in memory wallet service // When we call agent.initialize() it will create the wallet and store the current framework // version in the in memory storage service. We need to manually set the records between initializing @@ -137,10 +128,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() @@ -170,8 +157,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - // We need to manually initialize the wallet as we're using the in memory wallet service // When we call agent.initialize() it will create the wallet and store the current framework // version in the in memory storage service. We need to manually set the records between initializing @@ -189,10 +174,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index b797fc7c97..124cdf0a88 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -75,10 +75,6 @@ describe('UpdateAssistant | v0.3 - v0.3.1', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index 73aba5823f..8a8b351a38 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -32,7 +32,7 @@ describe('UpdateAssistant | Backup', () => { beforeEach(async () => { agent = new Agent(agentOptions) const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + backupPath = `${fileSystem.dataPath}/migration/backup/${backupIdentifier}` // If tests fail it's possible the cleanup has been skipped. So remove before running tests const doesFileSystemExist = await fileSystem.exists(backupPath) @@ -85,11 +85,16 @@ describe('UpdateAssistant | Backup', () => { // Backup should not exist before update expect(await fileSystem.exists(backupPath)).toBe(false) + const walletSpy = jest.spyOn(agent.wallet, 'export') + // Create update await updateAssistant.update() - // Backup should exist after update - expect(await fileSystem.exists(backupPath)).toBe(true) + // A wallet export should have been initiated + expect(walletSpy).toHaveBeenCalledWith({ key: agent.wallet.walletConfig?.key, path: backupPath }) + + // Backup should be cleaned after update + expect(await fileSystem.exists(backupPath)).toBe(false) expect( (await credentialRepository.getAll(agent.context)).sort((a, b) => a.id.localeCompare(b.id)) @@ -142,8 +147,8 @@ describe('UpdateAssistant | Backup', () => { expect(updateError?.cause?.message).toEqual("Uh oh I'm broken") - // Backup should exist after update - expect(await fileSystem.exists(backupPath)).toBe(true) + // Only backup error should exist after update + expect(await fileSystem.exists(backupPath)).toBe(false) expect(await fileSystem.exists(`${backupPath}-error`)).toBe(true) // Wallet should be same as when we started because of backup diff --git a/packages/indy-sdk/src/ledger/IndySdkPool.ts b/packages/indy-sdk/src/ledger/IndySdkPool.ts index bae9d8e17b..dfa25feb37 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPool.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPool.ts @@ -180,7 +180,7 @@ export class IndySdkPool { if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath // Determine the genesisPath - const genesisPath = this.fileSystem.basePath + `/afj/genesis-${this.poolConfig.id}.txn` + const genesisPath = this.fileSystem.tempPath + `/genesis-${this.poolConfig.id}.txn` // Store genesis data if provided if (this.poolConfig.genesisTransactions) { await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) diff --git a/packages/node/src/NodeFileSystem.ts b/packages/node/src/NodeFileSystem.ts index a5caf0d070..75ebb01b73 100644 --- a/packages/node/src/NodeFileSystem.ts +++ b/packages/node/src/NodeFileSystem.ts @@ -5,21 +5,30 @@ import { createHash } from 'crypto' import fs, { promises } from 'fs' import http from 'http' import https from 'https' -import { tmpdir } from 'os' +import { tmpdir, homedir } from 'os' import { dirname } from 'path' -const { access, readFile, writeFile } = promises +const { access, readFile, writeFile, mkdir, rm, unlink } = promises export class NodeFileSystem implements FileSystem { - public readonly basePath + public readonly dataPath + public readonly cachePath + public readonly tempPath /** * Create new NodeFileSystem class instance. * - * @param basePath The base path to use for reading and writing files. process.cwd() if not specified + * @param baseDataPath The base path to use for reading and writing data files used within the framework. + * Files will be created under baseDataPath/.afj directory. If not specified, it will be set to homedir() + * @param baseCachePath The base path to use for reading and writing cache files used within the framework. + * Files will be created under baseCachePath/.afj directory. If not specified, it will be set to homedir() + * @param baseTempPath The base path to use for reading and writing temporary files within the framework. + * Files will be created under baseTempPath/.afj directory. If not specified, it will be set to tmpdir() */ - public constructor(basePath?: string) { - this.basePath = basePath ?? tmpdir() + public constructor(options?: { baseDataPath?: string; baseCachePath?: string; baseTempPath?: string }) { + this.dataPath = options?.baseDataPath ? `${options?.baseDataPath}/.afj` : `${homedir()}/.afj/data` + this.cachePath = options?.baseCachePath ? `${options?.baseCachePath}/.afj` : `${homedir()}/.afj/cache` + this.tempPath = `${options?.baseTempPath ?? tmpdir()}/.afj` } public async exists(path: string) { @@ -32,12 +41,12 @@ export class NodeFileSystem implements FileSystem { } public async createDirectory(path: string): Promise { - await promises.mkdir(dirname(path), { recursive: true }) + await mkdir(dirname(path), { recursive: true }) } public async write(path: string, data: string): Promise { // Make sure parent directories exist - await promises.mkdir(dirname(path), { recursive: true }) + await mkdir(dirname(path), { recursive: true }) return writeFile(path, data, { encoding: 'utf-8' }) } @@ -46,11 +55,15 @@ export class NodeFileSystem implements FileSystem { return readFile(path, { encoding: 'utf-8' }) } + public async delete(path: string): Promise { + await rm(path, { recursive: true, force: true }) + } + public async downloadToFile(url: string, path: string, options: DownloadToFileOptions) { const httpMethod = url.startsWith('https') ? https : http // Make sure parent directories exist - await promises.mkdir(dirname(path), { recursive: true }) + await mkdir(dirname(path), { recursive: true }) const file = fs.createWriteStream(path) const hash = options.verifyHash ? createHash('sha256') : undefined @@ -88,7 +101,7 @@ export class NodeFileSystem implements FileSystem { }) .on('error', async (error) => { // Handle errors - await fs.promises.unlink(path) // Delete the file async. (But we don't check the result) + await unlink(path) // Delete the file async. (But we don't check the result) reject(`Unable to download file from url: ${url}. ${error.message}`) }) }) diff --git a/packages/node/tests/NodeFileSystem.test.ts b/packages/node/tests/NodeFileSystem.test.ts index f031ee32e5..f62f4d8e00 100644 --- a/packages/node/tests/NodeFileSystem.test.ts +++ b/packages/node/tests/NodeFileSystem.test.ts @@ -28,7 +28,7 @@ describe('@aries-framework/file-system-node', () => { await fileSystem.downloadToFile( 'https://tails.prod.absa.africa/api/public/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p', - `${fileSystem.basePath}/afj/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p`, + `${fileSystem.dataPath}/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p`, { verifyHash: { algorithm: 'sha256', diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 3fa303bf91..1da533811d 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -26,11 +26,11 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@azure/core-asynciterator-polyfill": "^1.0.0", - "events": "^3.3.0" + "events": "^3.3.0", + "@types/react-native": "^0.64.10" }, "devDependencies": { "@types/indy-sdk-react-native": "npm:@types/indy-sdk@1.16.26", - "@types/react-native": "^0.64.10", "indy-sdk-react-native": "^0.3.1", "react": "17.0.1", "react-native": "0.64.2", diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index bf8e9fb353..48588fad88 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -1,20 +1,38 @@ import type { FileSystem, DownloadToFileOptions } from '@aries-framework/core' import { TypedArrayEncoder, AriesFrameworkError, getDirFromFilePath, Buffer } from '@aries-framework/core' +import { Platform } from 'react-native' import * as RNFS from 'react-native-fs' export class ReactNativeFileSystem implements FileSystem { - public readonly basePath + public readonly dataPath + public readonly cachePath + public readonly tempPath /** * Create new ReactNativeFileSystem class instance. * - * @param basePath The base path to use for reading and writing files. RNFS.TemporaryDirectoryPath if not specified + * @param baseDataPath The base path to use for reading and writing data files used within the framework. + * Files will be created under baseDataPath/.afj directory. If not specified, it will be set to + * RNFS.DocumentDirectoryPath + * @param baseCachePath The base path to use for reading and writing cache files used within the framework. + * Files will be created under baseCachePath/.afj directory. If not specified, it will be set to + * RNFS.CachesDirectoryPath + * @param baseTempPath The base path to use for reading and writing temporary files within the framework. + * Files will be created under baseTempPath/.afj directory. If not specified, it will be set to + * RNFS.TemporaryDirectoryPath * * @see https://github.com/itinance/react-native-fs#constants */ - public constructor(basePath?: string) { - this.basePath = basePath ?? RNFS.TemporaryDirectoryPath + public constructor(options?: { baseDataPath?: string; baseCachePath?: string; baseTempPath?: string }) { + this.dataPath = `${options?.baseDataPath ?? RNFS.DocumentDirectoryPath}/.afj` + // In Android, TemporaryDirectoryPath falls back to CachesDirectoryPath + this.cachePath = options?.baseCachePath + ? `${options?.baseCachePath}/.afj` + : `${RNFS.CachesDirectoryPath}/.afj${Platform.OS === 'android' ? '/cache' : ''}` + this.tempPath = options?.baseTempPath + ? `${options?.baseTempPath}/.afj` + : `${RNFS.TemporaryDirectoryPath}/.afj${Platform.OS === 'android' ? '/temp' : ''}` } public async exists(path: string): Promise { @@ -36,6 +54,10 @@ export class ReactNativeFileSystem implements FileSystem { return RNFS.readFile(path, 'utf8') } + public async delete(path: string): Promise { + await RNFS.unlink(path) + } + public async downloadToFile(url: string, path: string, options?: DownloadToFileOptions) { // Make sure parent directories exist await RNFS.mkdir(getDirFromFilePath(path)) From 64a5da937059d25e693e2491af329548b2975ef6 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 18 Feb 2023 16:54:25 -0300 Subject: [PATCH 051/139] fix(samples): dummy module response message type (#1321) Signed-off-by: Ariel Gentile --- samples/extension-module/dummy/messages/DummyResponseMessage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/extension-module/dummy/messages/DummyResponseMessage.ts b/samples/extension-module/dummy/messages/DummyResponseMessage.ts index 421243d516..560183d95c 100644 --- a/samples/extension-module/dummy/messages/DummyResponseMessage.ts +++ b/samples/extension-module/dummy/messages/DummyResponseMessage.ts @@ -19,5 +19,5 @@ export class DummyResponseMessage extends AgentMessage { @IsValidMessageType(DummyResponseMessage.type) public readonly type = DummyResponseMessage.type.messageTypeUri - public static readonly type = parseMessageType('https://2060.io/didcomm/dummy/1.0/response') + public static readonly type = parseMessageType('https://didcomm.org/dummy/1.0/response') } From 616b908a8e6788657f79dce3830dd498e14e7109 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sun, 19 Feb 2023 13:27:16 -0300 Subject: [PATCH 052/139] feat(wallet)!: createKey from private key (#1301) Signed-off-by: Ariel Gentile --- packages/askar/src/wallet/AskarWallet.ts | 20 +++++--- .../src/wallet/__tests__/AskarWallet.test.ts | 47 ++++++++++++++----- .../src/Bls12381g2SigningProvider.ts | 9 ++-- .../tests/bbs-signatures.e2e.test.ts | 9 +++- .../tests/bbs-signing-provider.e2e.test.ts | 2 +- ...proof.credentials.propose-offerBbs.test.ts | 6 ++- .../src/crypto/__tests__/JwsService.test.ts | 12 +++-- .../signing-provider/SigningProvider.ts | 3 +- .../signature/SignatureDecoratorUtils.test.ts | 5 +- ...ldproof.connectionless-credentials.test.ts | 5 +- ...v2.ldproof.credentials-auto-accept.test.ts | 7 +-- ...f.credentials.propose-offerED25519.test.ts | 5 +- .../dids/__tests__/dids-registrar.e2e.test.ts | 22 +++++---- .../modules/dids/__tests__/peer-did.test.ts | 9 ++-- .../dids/methods/key/KeyDidRegistrar.ts | 22 +++++++-- .../key/__tests__/KeyDidRegistrar.test.ts | 19 ++++---- .../dids/methods/peer/PeerDidRegistrar.ts | 22 +++++++-- .../peer/__tests__/PeerDidRegistrar.test.ts | 17 +++---- .../methods/sov/IndySdkSovDidRegistrar.ts | 13 ++--- .../__tests__/IndySdkSovDidRegistrar.test.ts | 25 +++++----- .../vc/__tests__/W3cCredentialService.test.ts | 8 +++- packages/core/src/wallet/IndyWallet.test.ts | 19 +++++--- packages/core/src/wallet/IndyWallet.ts | 27 ++++++++--- packages/core/src/wallet/Wallet.ts | 3 +- .../src/dids/IndySdkSovDidRegistrar.ts | 12 ++--- packages/indy-sdk/src/wallet/IndySdkWallet.ts | 26 +++++++--- .../wallet/__tests__/IndySdkWallet.test.ts | 15 ++++-- .../src/dids/IndyVdrIndyDidRegistrar.ts | 29 +++++++++--- .../tests/indy-vdr-did-registrar.e2e.test.ts | 14 +++--- .../tests/indy-vdr-did-resolver.e2e.test.ts | 3 +- .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 4 +- .../tests/openid4vc-client.e2e.test.ts | 4 +- 32 files changed, 300 insertions(+), 143 deletions(-) diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 38030c6ab7..baa95e5324 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -346,7 +346,8 @@ export class AskarWallet implements Wallet { * Create a key with an optional seed and keyType. * The keypair is also automatically stored in the wallet afterwards * - * @param seed string The seed for creating a key + * @param privateKey Buffer Optional privateKey for creating a key + * @param seed string Optional seed for creating a key * @param keyType KeyType the type of key that should be created * * @returns a Key instance with a publicKeyBase58 @@ -354,13 +355,21 @@ export class AskarWallet implements Wallet { * @throws {WalletError} When an unsupported keytype is requested * @throws {WalletError} When the key could not be created */ - public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { + public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { + if (seed && privateKey) { + throw new AriesFrameworkError('Only one of seed and privateKey can be set') + } + if (keyTypeSupportedByAskar(keyType)) { const algorithm = keyAlgFromString(keyType) - // Create key from seed - const key = seed ? AskarKey.fromSeed({ seed: Buffer.from(seed), algorithm }) : AskarKey.generate(algorithm) + // Create key + const key = privateKey + ? AskarKey.fromSecretBytes({ secretKey: privateKey, algorithm }) + : seed + ? AskarKey.fromSeed({ seed, algorithm }) + : AskarKey.generate(algorithm) // Store key await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(key.publicBytes) }) @@ -370,7 +379,7 @@ export class AskarWallet implements Wallet { if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) - const keyPair = await signingKeyProvider.createKeyPair({ seed }) + const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) await this.storeKeyPair(keyPair) return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) } @@ -398,7 +407,6 @@ export class AskarWallet implements Wallet { if (!TypedArrayEncoder.isTypedArray(data)) { throw new WalletError(`Currently not supporting signing of multiple messages`) } - const keyEntry = await this.session.fetchKey({ name: key.publicKeyBase58 }) if (!keyEntry) { diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 94732a15b0..40dea0cbc8 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -36,7 +36,8 @@ const walletConfig: WalletConfig = { describe('AskarWallet basic operations', () => { let askarWallet: AskarWallet - const seed = 'sample-seed' + const seed = TypedArrayEncoder.fromString('sample-seed') + const privateKey = TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { @@ -62,12 +63,36 @@ describe('AskarWallet basic operations', () => { expect(nonce).toMatch(/[0-9]+/) }) - test('Create ed25519 keypair', async () => { - await expect( - askarWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) - ).resolves.toMatchObject({ + test('Create ed25519 keypair from seed', async () => { + const key = await askarWallet.createKey({ + seed, + keyType: KeyType.Ed25519, + }) + + expect(key).toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Create ed25519 keypair from private key', async () => { + const key = await askarWallet.createKey({ + privateKey, keyType: KeyType.Ed25519, }) + + expect(key).toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Attempt to create ed25519 keypair from both seed and private key', async () => { + await expect( + askarWallet.createKey({ + privateKey, + seed, + keyType: KeyType.Ed25519, + }) + ).rejects.toThrowError() }) test('Create x25519 keypair', async () => { @@ -109,7 +134,7 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { describe('AskarWallet with custom signing provider', () => { let askarWallet: AskarWallet - const seed = 'sample-seed' + const seed = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') class DummySigningProvider implements SigningProvider { @@ -117,7 +142,7 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { public async createKeyPair(options: CreateKeyPairOptions): Promise { return { - publicKeyBase58: encodeToBase58(Buffer.from(options.seed || 'publicKeyBase58')), + publicKeyBase58: encodeToBase58(Buffer.from(options.seed || TypedArrayEncoder.fromString('publicKeyBase58'))), privateKeyBase58: 'privateKeyBase58', keyType: KeyType.Bls12381g1g2, } @@ -175,11 +200,11 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { }) test('Attempt to create the same custom keypair twice', async () => { - await askarWallet.createKey({ seed: 'keybase58', keyType: KeyType.Bls12381g1g2 }) + await askarWallet.createKey({ seed: TypedArrayEncoder.fromString('keybase58'), keyType: KeyType.Bls12381g1g2 }) - await expect(askarWallet.createKey({ seed: 'keybase58', keyType: KeyType.Bls12381g1g2 })).rejects.toThrow( - WalletError - ) + await expect( + askarWallet.createKey({ seed: TypedArrayEncoder.fromString('keybase58'), keyType: KeyType.Bls12381g1g2 }) + ).rejects.toThrow(WalletError) }) }) }) diff --git a/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts b/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts index fd7c1de9cf..b74730af47 100644 --- a/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts +++ b/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts @@ -15,11 +15,12 @@ export class Bls12381g2SigningProvider implements SigningProvider { * * @throws {SigningProviderError} When a key could not be created */ - public async createKeyPair({ seed }: CreateKeyPairOptions): Promise { - // Generate bytes from the seed as required by the bbs-signatures libraries - const seedBytes = seed ? TypedArrayEncoder.fromString(seed) : undefined + public async createKeyPair({ seed, privateKey }: CreateKeyPairOptions): Promise { + if (privateKey) { + throw new SigningProviderError('Cannot create keypair from private key') + } - const blsKeyPair = await generateBls12381G2KeyPair(seedBytes) + const blsKeyPair = await generateBls12381G2KeyPair(seed) return { keyType: KeyType.Bls12381g2, diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index 936dfbe22e..efbc08027a 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -19,6 +19,7 @@ import { W3cVerifiablePresentation, IndyWallet, Ed25519Signature2018, + TypedArrayEncoder, } from '@aries-framework/core' import { SignatureSuiteRegistry } from '../../core/src/modules/vc/SignatureSuiteRegistry' @@ -62,7 +63,8 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { let wallet: IndyWallet let agentContext: AgentContext let w3cCredentialService: W3cCredentialService - const seed = 'testseed000000000000000000000001' + const seed = TypedArrayEncoder.fromString('testseed000000000000000000000001') + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, signingProviderRegistry) @@ -219,7 +221,10 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { describe('signPresentation', () => { it('should sign the presentation successfully', async () => { - const signingKey = await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + const signingKey = await wallet.createKey({ + privateKey, + keyType: KeyType.Ed25519, + }) const signingDidKey = new DidKey(signingKey) const verificationMethod = `${signingDidKey.did}#${signingDidKey.key.fingerprint}` const presentation = JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VP_DOCUMENT, W3cPresentation) diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index db67e0c5a1..d428dd65be 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -26,7 +26,7 @@ const walletConfig: WalletConfig = { describeSkipNode17And18('BBS Signing Provider', () => { let indyWallet: IndyWallet - const seed = 'sample-seed' + const seed = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index a22408ce87..b445f0f24b 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -3,6 +3,8 @@ import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../core/sr import type { Wallet } from '../../core/src/wallet' import type { CredentialTestsAgent } from '../../core/tests/helpers' +import { TypedArrayEncoder } from '@aries-framework/core' + import { InjectionSymbols } from '../../core/src/constants' import { KeyType } from '../../core/src/crypto' import { CredentialState } from '../../core/src/modules/credentials/models' @@ -29,14 +31,14 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { let issuerDidKey: DidKey let didCommMessageRepository: DidCommMessageRepository let signCredentialOptions: JsonLdCredentialDetailFormat - const seed = 'testseed000000000000000000000001' + const seed = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { ;({ faberAgent, aliceAgent, aliceConnection } = await setupCredentialTests( 'Faber Agent Credentials LD BBS+', 'Alice Agent Credentials LD BBS+' )) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ keyType: KeyType.Ed25519, seed }) + await wallet.createKey({ keyType: KeyType.Ed25519, privateKey: seed }) const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, seed }) issuerDidKey = new DidKey(key) diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index 6b5d5fb258..4080ab2f24 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -3,7 +3,7 @@ import type { Key, Wallet } from '@aries-framework/core' import { getAgentConfig, getAgentContext } from '../../../tests/helpers' import { DidKey } from '../../modules/dids' -import { Buffer, JsonEncoder } from '../../utils' +import { Buffer, JsonEncoder, TypedArrayEncoder } from '../../utils' import { IndyWallet } from '../../wallet/IndyWallet' import { JwsService } from '../JwsService' import { KeyType } from '../KeyType' @@ -28,8 +28,14 @@ describe('JwsService', () => { await wallet.createAndOpen(config.walletConfig!) jwsService = new JwsService() - didJwsz6MkfKey = await wallet.createKey({ seed: didJwsz6Mkf.SEED, keyType: KeyType.Ed25519 }) - didJwsz6MkvKey = await wallet.createKey({ seed: didJwsz6Mkv.SEED, keyType: KeyType.Ed25519 }) + didJwsz6MkfKey = await wallet.createKey({ + privateKey: TypedArrayEncoder.fromString(didJwsz6Mkf.SEED), + keyType: KeyType.Ed25519, + }) + didJwsz6MkvKey = await wallet.createKey({ + privateKey: TypedArrayEncoder.fromString(didJwsz6Mkv.SEED), + keyType: KeyType.Ed25519, + }) }) afterAll(async () => { diff --git a/packages/core/src/crypto/signing-provider/SigningProvider.ts b/packages/core/src/crypto/signing-provider/SigningProvider.ts index 2f2c31e701..3e70d67694 100644 --- a/packages/core/src/crypto/signing-provider/SigningProvider.ts +++ b/packages/core/src/crypto/signing-provider/SigningProvider.ts @@ -20,7 +20,8 @@ export interface VerifyOptions { } export interface CreateKeyPairOptions { - seed?: string + seed?: Buffer + privateKey?: Buffer } export interface SigningProvider { diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index 894520edaf..5336162c59 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -1,6 +1,7 @@ import { getAgentConfig } from '../../../tests/helpers' import { KeyType } from '../../crypto' import { SigningProviderRegistry } from '../../crypto/signing-provider' +import { TypedArrayEncoder } from '../../utils' import { IndyWallet } from '../../wallet/IndyWallet' import { SignatureDecorator } from './SignatureDecorator' @@ -53,8 +54,8 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { }) test('signData signs json object and returns SignatureDecorator', async () => { - const seed1 = '00000000000000000000000000000My1' - const key = await wallet.createKey({ seed: seed1, keyType: KeyType.Ed25519 }) + const privateKey = TypedArrayEncoder.fromString('00000000000000000000000000000My1') + const key = await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) const result = await signData(data, wallet, key.publicKeyBase58) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts index 2e40d6234f..ea148db552 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts @@ -13,6 +13,7 @@ import testLogger from '../../../../../../tests/logger' import { Agent } from '../../../../../agent/Agent' import { InjectionSymbols } from '../../../../../constants' import { KeyType } from '../../../../../crypto' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonEncoder } from '../../../../../utils/JsonEncoder' import { W3cVcModule } from '../../../../vc' import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader' @@ -62,7 +63,7 @@ describe('credentials', () => { let aliceAgent: Agent<(typeof aliceAgentOptions)['modules']> let faberReplay: ReplaySubject let aliceReplay: ReplaySubject - const seed = 'testseed000000000000000000000001' + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') const TEST_LD_DOCUMENT: JsonCredential = { '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], type: ['VerifiableCredential', 'UniversityDegreeCredential'], @@ -106,7 +107,7 @@ describe('credentials', () => { .subscribe(aliceReplay) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index ad08852a17..d8a3521a27 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -8,6 +8,7 @@ import testLogger from '../../../../../../tests/logger' import { InjectionSymbols } from '../../../../../constants' import { KeyType } from '../../../../../crypto' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { TypedArrayEncoder } from '../../../../../utils' import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc/constants' import { AutoAcceptCredential, CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -33,7 +34,7 @@ describe('credentials', () => { let aliceCredentialRecord: CredentialExchangeRecord let signCredentialOptions: JsonLdCredentialDetailFormat let wallet - const seed = 'testseed000000000000000000000001' + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') describe('Auto accept on `always`', () => { beforeAll(async () => { @@ -44,7 +45,7 @@ describe('credentials', () => { )) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, options: { @@ -143,7 +144,7 @@ describe('credentials', () => { AutoAcceptCredential.ContentApproved )) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, options: { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index c8f9a64d20..98621d7a40 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -8,6 +8,7 @@ import testLogger from '../../../../../../tests/logger' import { InjectionSymbols } from '../../../../../constants' import { KeyType } from '../../../../../crypto' import { DidCommMessageRepository } from '../../../../../storage' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -57,7 +58,7 @@ describe('credentials', () => { let signCredentialOptions: JsonLdCredentialDetailFormat let wallet - const seed = 'testseed000000000000000000000001' + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') let credDefId: string beforeAll(async () => { @@ -66,7 +67,7 @@ describe('credentials', () => { 'Alice Agent Credentials LD' )) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: inputDocAsJson, options: { diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index 9599d06c92..ccd60edf71 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -47,7 +47,7 @@ describe('dids', () => { keyType: KeyType.Ed25519, }, secret: { - seed: '96213c3d7fc8d4d6754c7a0fd969598e', + privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), }, }) @@ -97,7 +97,7 @@ describe('dids', () => { ], id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', }, - secret: { seed: '96213c3d7fc8d4d6754c7a0fd969598e' }, + secret: { privateKey: '96213c3d7fc8d4d6754c7a0fd969598e' }, }, }) }) @@ -110,7 +110,7 @@ describe('dids', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - seed: 'e008ef10b7c163114b3857542b3736eb', + privateKey: TypedArrayEncoder.fromString('e008ef10b7c163114b3857542b3736eb'), }, }) @@ -160,7 +160,7 @@ describe('dids', () => { ], id: 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', }, - secret: { seed: 'e008ef10b7c163114b3857542b3736eb' }, + secret: { privateKey: 'e008ef10b7c163114b3857542b3736eb' }, }, }) }) @@ -168,11 +168,13 @@ describe('dids', () => { it('should create a did:sov did', async () => { // Generate a seed and the indy did. This allows us to create a new did every time // but still check if the created output document is as expected. - const seed = Array(32 + 1) - .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) - .slice(0, 32) + const privateKey = TypedArrayEncoder.fromString( + Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + ) - const publicKeyEd25519 = generateKeyPairFromSeed(TypedArrayEncoder.fromString(seed)).publicKey + const publicKeyEd25519 = generateKeyPairFromSeed(privateKey).publicKey const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(publicKeyEd25519)) const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) const indyDid = indyDidFromPublicKeyBase58(ed25519PublicKeyBase58) @@ -193,7 +195,7 @@ describe('dids', () => { }, }, secret: { - seed, + privateKey, }, }) @@ -261,7 +263,7 @@ describe('dids', () => { id: `did:sov:${indyDid}`, }, secret: { - seed, + privateKey: privateKey.toString(), }, }, }) diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 5467b601c9..c352fc0383 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -8,7 +8,7 @@ import { InjectionSymbols } from '../../../constants' import { Key, KeyType } from '../../../crypto' import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { IndyStorageService } from '../../../storage/IndyStorageService' -import { JsonTransformer } from '../../../utils' +import { JsonTransformer, TypedArrayEncoder } from '../../../utils' import { IndyWallet } from '../../../wallet/IndyWallet' import { DidsModuleConfig } from '../DidsModuleConfig' import { DidCommV1Service, DidDocument, DidDocumentBuilder } from '../domain' @@ -62,9 +62,12 @@ describe('peer dids', () => { test('create a peer did method 1 document from ed25519 keys with a service', async () => { // The following scenario show how we could create a key and create a did document from it for DID Exchange - const ed25519Key = await wallet.createKey({ seed: 'astringoftotalin32characterslong', keyType: KeyType.Ed25519 }) + const ed25519Key = await wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('astringoftotalin32characterslong'), + keyType: KeyType.Ed25519, + }) const mediatorEd25519Key = await wallet.createKey({ - seed: 'anotherstringof32characterslong1', + privateKey: TypedArrayEncoder.fromString('anotherstringof32characterslong1'), keyType: KeyType.Ed25519, }) diff --git a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts index 6a4def3cd2..645deee3a1 100644 --- a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts @@ -1,5 +1,6 @@ import type { AgentContext } from '../../../../agent' import type { KeyType } from '../../../../crypto' +import type { Buffer } from '../../../../utils' import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' @@ -16,6 +17,7 @@ export class KeyDidRegistrar implements DidRegistrar { const keyType = options.options.keyType const seed = options.secret?.seed + const privateKey = options.secret?.privateKey if (!keyType) { return { @@ -28,7 +30,7 @@ export class KeyDidRegistrar implements DidRegistrar { } } - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + if (seed && (typeof seed !== 'object' || seed.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -39,10 +41,22 @@ export class KeyDidRegistrar implements DidRegistrar { } } + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid private key provided', + }, + } + } + try { const key = await agentContext.wallet.createKey({ keyType, seed, + privateKey, }) const didKey = new DidKey(key) @@ -67,7 +81,8 @@ export class KeyDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + seed: options.secret?.seed?.toString(), + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -115,7 +130,8 @@ export interface KeyDidCreateOptions extends DidCreateOptions { keyType: KeyType } secret?: { - seed?: string + seed?: Buffer + privateKey?: Buffer } } diff --git a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts index e859dcf795..9f084a5b8d 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts @@ -3,6 +3,7 @@ import type { Wallet } from '../../../../../wallet' import { getAgentContext, mockFunction } from '../../../../../../tests/helpers' import { KeyType } from '../../../../../crypto' import { Key } from '../../../../../crypto/Key' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { DidDocumentRole } from '../../../domain/DidDocumentRole' import { DidRepository } from '../../../repository/DidRepository' @@ -32,7 +33,7 @@ describe('DidRegistrar', () => { describe('KeyDidRegistrar', () => { it('should correctly create a did:key document using Ed25519 key type', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const result = await keyDidRegistrar.create(agentContext, { method: 'key', @@ -40,7 +41,7 @@ describe('DidRegistrar', () => { keyType: KeyType.Ed25519, }, secret: { - seed, + privateKey, }, }) @@ -52,12 +53,12 @@ describe('DidRegistrar', () => { did: 'did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU', didDocument: didKeyz6MksLeFixture, secret: { - seed: '96213c3d7fc8d4d6754c712fd969598e', + privateKey: '96213c3d7fc8d4d6754c712fd969598e', }, }, }) - expect(walletMock.createKey).toHaveBeenCalledWith({ keyType: KeyType.Ed25519, seed }) + expect(walletMock.createKey).toHaveBeenCalledWith({ keyType: KeyType.Ed25519, privateKey }) }) it('should return an error state if no key type is provided', async () => { @@ -77,7 +78,7 @@ describe('DidRegistrar', () => { }) }) - it('should return an error state if an invalid seed is provided', async () => { + it('should return an error state if an invalid private key is provided', async () => { const result = await keyDidRegistrar.create(agentContext, { method: 'key', @@ -85,7 +86,7 @@ describe('DidRegistrar', () => { keyType: KeyType.Ed25519, }, secret: { - seed: 'invalid', + privateKey: TypedArrayEncoder.fromString('invalid'), }, }) @@ -94,13 +95,13 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, }) }) it('should store the did document', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const did = 'did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU' await keyDidRegistrar.create(agentContext, { @@ -110,7 +111,7 @@ describe('DidRegistrar', () => { keyType: KeyType.Ed25519, }, secret: { - seed, + privateKey, }, }) diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts index 057ed96c9d..83b171e978 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts @@ -1,5 +1,6 @@ import type { AgentContext } from '../../../../agent' import type { KeyType } from '../../../../crypto' +import type { Buffer } from '../../../../utils' import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' @@ -27,6 +28,7 @@ export class PeerDidRegistrar implements DidRegistrar { if (isPeerDidNumAlgo0CreateOptions(options)) { const keyType = options.options.keyType const seed = options.secret?.seed + const privateKey = options.secret?.privateKey if (!keyType) { return { @@ -39,7 +41,7 @@ export class PeerDidRegistrar implements DidRegistrar { } } - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + if (seed && (typeof seed !== 'object' || seed.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -50,9 +52,21 @@ export class PeerDidRegistrar implements DidRegistrar { } } + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid private key provided', + }, + } + } + const key = await agentContext.wallet.createKey({ keyType, seed, + privateKey, }) // TODO: validate did:peer document @@ -105,7 +119,8 @@ export class PeerDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + seed: options.secret?.seed?.toString(), + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -170,7 +185,8 @@ export interface PeerDidNumAlgo0CreateOptions extends DidCreateOptions { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc } secret?: { - seed?: string + seed?: Buffer + privateKey?: Buffer } } diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts index f20079cd4f..b974cff303 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts @@ -3,6 +3,7 @@ import type { Wallet } from '../../../../../wallet' import { getAgentContext, mockFunction } from '../../../../../../tests/helpers' import { KeyType } from '../../../../../crypto' import { Key } from '../../../../../crypto/Key' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { DidCommV1Service, DidDocumentBuilder } from '../../../domain' import { DidDocumentRole } from '../../../domain/DidDocumentRole' @@ -32,7 +33,7 @@ describe('DidRegistrar', () => { describe('PeerDidRegistrar', () => { describe('did:peer:0', () => { it('should correctly create a did:peer:0 document using Ed25519 key type', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const result = await peerDidRegistrar.create(agentContext, { method: 'peer', @@ -41,7 +42,7 @@ describe('DidRegistrar', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - seed, + privateKey, }, }) @@ -53,7 +54,7 @@ describe('DidRegistrar', () => { did: 'did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU', didDocument: didPeer0z6MksLeFixture, secret: { - seed: '96213c3d7fc8d4d6754c712fd969598e', + privateKey: '96213c3d7fc8d4d6754c712fd969598e', }, }, }) @@ -78,7 +79,7 @@ describe('DidRegistrar', () => { }) }) - it('should return an error state if an invalid seed is provided', async () => { + it('should return an error state if an invalid private key is provided', async () => { const result = await peerDidRegistrar.create(agentContext, { method: 'peer', options: { @@ -86,7 +87,7 @@ describe('DidRegistrar', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - seed: 'invalid', + privateKey: TypedArrayEncoder.fromString('invalid'), }, }) @@ -95,13 +96,13 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, }) }) it('should store the did without the did document', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const did = 'did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU' await peerDidRegistrar.create(agentContext, { @@ -111,7 +112,7 @@ describe('DidRegistrar', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - seed, + privateKey, }, }) diff --git a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts index 763f97b622..21781642ab 100644 --- a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts @@ -1,4 +1,5 @@ import type { AgentContext } from '../../../../agent' +import type { Buffer } from '../../../../utils' import type { IndyEndpointAttrib, IndyPool } from '../../../ledger' import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' @@ -24,15 +25,15 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { const didRepository = agentContext.dependencyManager.resolve(DidRepository) const { alias, role, submitterDid, indyNamespace } = options.options - const seed = options.secret?.seed + const privateKey = options.secret?.privateKey - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, } } @@ -56,7 +57,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // FIXME: once askar/indy-vdr is supported we need to adjust this to work with both indy-sdk and askar assertIndyWallet(agentContext.wallet) const [unqualifiedIndyDid, verkey] = await indy.createAndStoreMyDid(agentContext.wallet.handle, { - seed, + seed: privateKey?.toString(), }) const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` @@ -115,7 +116,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -237,7 +238,7 @@ export interface SovDidCreateOptions extends DidCreateOptions { submitterDid: string } secret?: { - seed?: string + privateKey?: Buffer } } diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts index 761a2956b6..7837772932 100644 --- a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts @@ -5,6 +5,7 @@ import type * as Indy from 'indy-sdk' import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../../../tests/helpers' import { SigningProviderRegistry } from '../../../../../crypto/signing-provider' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { IndyWallet } from '../../../../../wallet/IndyWallet' import { IndyPoolService } from '../../../../ledger/services/IndyPoolService' @@ -52,7 +53,7 @@ describe('DidRegistrar', () => { jest.clearAllMocks() }) - it('should return an error state if an invalid seed is provided', async () => { + it('should return an error state if an invalid private key is provided', async () => { const result = await indySdkSovDidRegistrar.create(agentContext, { method: 'sov', @@ -61,7 +62,7 @@ describe('DidRegistrar', () => { alias: 'Hello', }, secret: { - seed: 'invalid', + privateKey: TypedArrayEncoder.fromString('invalid'), }, }) @@ -70,7 +71,7 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, }) }) @@ -89,7 +90,7 @@ describe('DidRegistrar', () => { alias: 'Hello', }, secret: { - seed: '12345678901234567890123456789012', + privateKey: TypedArrayEncoder.fromString('12345678901234567890123456789012'), }, }) @@ -123,7 +124,7 @@ describe('DidRegistrar', () => { }) it('should correctly create a did:sov document without services', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) @@ -136,7 +137,7 @@ describe('DidRegistrar', () => { role: 'STEWARD', }, secret: { - seed, + privateKey, }, }) @@ -191,14 +192,14 @@ describe('DidRegistrar', () => { keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], }, secret: { - seed, + privateKey: privateKey.toString(), }, }, }) }) it('should correctly create a did:sov document with services', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) @@ -219,7 +220,7 @@ describe('DidRegistrar', () => { }, }, secret: { - seed, + privateKey, }, }) @@ -298,14 +299,14 @@ describe('DidRegistrar', () => { keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], }, secret: { - seed, + privateKey: privateKey.toString(), }, }, }) }) it('should store the did document', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('did')) @@ -326,7 +327,7 @@ describe('DidRegistrar', () => { }, }, secret: { - seed, + privateKey, }, }) diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index d6f3eb266f..06e0b42245 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -3,6 +3,7 @@ import type { AgentContext } from '../../../agent' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' import { KeyType } from '../../../crypto' import { SigningProviderRegistry } from '../../../crypto/signing-provider' +import { TypedArrayEncoder } from '../../../utils' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IndyWallet } from '../../../wallet/IndyWallet' import { WalletError } from '../../../wallet/error' @@ -67,7 +68,7 @@ describe('W3cCredentialService', () => { let agentContext: AgentContext let w3cCredentialService: W3cCredentialService let w3cCredentialRepository: W3cCredentialRepository - const seed = 'testseed000000000000000000000001' + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, signingProviderRegistry) @@ -116,7 +117,10 @@ describe('W3cCredentialService', () => { let verificationMethod: string beforeAll(async () => { // TODO: update to use did registrar - const issuerKey = await wallet.createKey({ keyType: KeyType.Ed25519, seed }) + const issuerKey = await wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey, + }) issuerDidKey = new DidKey(issuerKey) verificationMethod = `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}` }) diff --git a/packages/core/src/wallet/IndyWallet.test.ts b/packages/core/src/wallet/IndyWallet.test.ts index 07c5e74978..8a600a2592 100644 --- a/packages/core/src/wallet/IndyWallet.test.ts +++ b/packages/core/src/wallet/IndyWallet.test.ts @@ -31,7 +31,7 @@ const walletConfigWithMasterSecretId: WalletConfig = { describe('IndyWallet', () => { let indyWallet: IndyWallet - const seed = 'sample-seed' + const privateKey = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { @@ -72,16 +72,23 @@ describe('IndyWallet', () => { await expect(indyWallet.generateNonce()).resolves.toEqual(expect.any(String)) }) - test('Create ed25519 keypair', async () => { - await expect( - indyWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) - ).resolves.toMatchObject({ + test('Create ed25519 keypair from private key', async () => { + const key = await indyWallet.createKey({ + privateKey: TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67'), keyType: KeyType.Ed25519, }) + + expect(key).toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Fail to create ed25519 keypair from seed', async () => { + await expect(indyWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError(WalletError) }) test('Fail to create x25519 keypair', async () => { - await expect(indyWallet.createKey({ seed, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) + await expect(indyWallet.createKey({ privateKey, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) }) test('Create a signature with a ed25519 keypair', async () => { diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 0bef6447d6..8c8e83d575 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -465,12 +465,12 @@ export class IndyWallet implements Wallet { } /** - * Create a key with an optional seed and keyType. + * Create a key with an optional private key and keyType. * The keypair is also automatically stored in the wallet afterwards * * Bls12381g1g2 and X25519 are not supported. * - * @param seed string The seed for creating a key + * @param privateKey Buffer Private key (formerly called 'seed') * @param keyType KeyType the type of key that should be created * * @returns a Key instance with a publicKeyBase58 @@ -478,13 +478,26 @@ export class IndyWallet implements Wallet { * @throws {WalletError} When an unsupported keytype is requested * @throws {WalletError} When the key could not be created */ - public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { + public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { + if (seed && privateKey) { + throw new AriesFrameworkError('Only one of seed and privateKey can be set') + } + // Ed25519 is supported natively in Indy wallet if (keyType === KeyType.Ed25519) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - const verkey = await this.indy.createKey(this.handle, { seed, crypto_type: 'ed25519' }) + if (seed) { + throw new AriesFrameworkError( + 'IndyWallet does not support seed. You may rather want to specify a private key for deterministic ed25519 key generation' + ) + } + + const verkey = await this.indy.createKey(this.handle, { + seed: privateKey?.toString(), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + crypto_type: 'ed25519', + }) return Key.fromPublicKeyBase58(verkey, keyType) } @@ -492,7 +505,7 @@ export class IndyWallet implements Wallet { if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) - const keyPair = await signingKeyProvider.createKeyPair({ seed }) + const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) await this.storeKeyPair(keyPair) return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) } diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 6c5cff6388..4fcf70f5c2 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -55,7 +55,8 @@ export interface DidInfo { export interface WalletCreateKeyOptions { keyType: KeyType - seed?: string + seed?: Buffer + privateKey?: Buffer } export interface WalletSignOptions { diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts index 8553af9295..c043673cd1 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -40,15 +40,15 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { public async create(agentContext: AgentContext, options: IndySdkSovDidCreateOptions): Promise { const { alias, role, submitterDid, indyNamespace } = options.options - const seed = options.secret?.seed + const privateKey = options.secret?.privateKey - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, } } @@ -71,7 +71,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // to rely directly on the indy SDK, as we don't want to expose a createDid method just for. assertIndySdkWallet(agentContext.wallet) const [unqualifiedIndyDid, verkey] = await this.indySdk.createAndStoreMyDid(agentContext.wallet.handle, { - seed, + seed: privateKey?.toString(), }) const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` @@ -131,7 +131,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -256,7 +256,7 @@ export interface IndySdkSovDidCreateOptions extends DidCreateOptions { submitterDid: string } secret?: { - seed?: string + privateKey?: Buffer } } diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index 9230ed5f28..66d75e8933 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -461,12 +461,12 @@ export class IndySdkWallet implements Wallet { } /** - * Create a key with an optional seed and keyType. + * Create a key with an optional private key and keyType. * The keypair is also automatically stored in the wallet afterwards * * Bls12381g1g2 and X25519 are not supported. * - * @param seed string The seed for creating a key + * @param privateKey Buffer Private key (formerly called 'seed') * @param keyType KeyType the type of key that should be created * * @returns a Key instance with a publicKeyBase58 @@ -474,13 +474,25 @@ export class IndySdkWallet implements Wallet { * @throws {WalletError} When an unsupported keytype is requested * @throws {WalletError} When the key could not be created */ - public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { + public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { + if (seed && privateKey) { + throw new AriesFrameworkError('Only one of seed and privateKey can be set') + } + // Ed25519 is supported natively in Indy wallet if (keyType === KeyType.Ed25519) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - const verkey = await this.indySdk.createKey(this.handle, { seed, crypto_type: 'ed25519' }) + if (seed) { + throw new AriesFrameworkError( + 'IndySdkWallet does not support seed. You may rather want to specify a private key for deterministic ed25519 key generation' + ) + } + const verkey = await this.indySdk.createKey(this.handle, { + seed: privateKey?.toString(), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + crypto_type: 'ed25519', + }) return Key.fromPublicKeyBase58(verkey, keyType) } @@ -488,7 +500,7 @@ export class IndySdkWallet implements Wallet { if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) - const keyPair = await signingKeyProvider.createKeyPair({ seed }) + const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) await this.storeKeyPair(keyPair) return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) } diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts index 4b7f822f0e..1bb5447031 100644 --- a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -31,7 +31,7 @@ const walletConfigWithMasterSecretId: WalletConfig = { describe('IndySdkWallet', () => { let indySdkWallet: IndySdkWallet - const seed = 'sample-seed' + const privateKey = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { @@ -72,16 +72,23 @@ describe('IndySdkWallet', () => { await expect(indySdkWallet.generateNonce()).resolves.toEqual(expect.any(String)) }) - test('Create ed25519 keypair', async () => { + test('Create ed25519 keypair from private key', async () => { await expect( - indySdkWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) + indySdkWallet.createKey({ + privateKey: TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67'), + keyType: KeyType.Ed25519, + }) ).resolves.toMatchObject({ keyType: KeyType.Ed25519, }) }) + test('Fail to create ed25519 keypair from seed', async () => { + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError(WalletError) + }) + test('Fail to create x25519 keypair', async () => { - await expect(indySdkWallet.createKey({ seed, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) }) test('Create a signature with a ed25519 keypair', async () => { diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index 7505c09280..cf41881cad 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -2,6 +2,7 @@ import type { IndyEndpointAttrib } from './didSovUtil' import type { IndyVdrPool } from '../pool' import type { AgentContext, + Buffer, DidRegistrar, DidCreateOptions, DidCreateResult, @@ -41,7 +42,20 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { public async create(agentContext: AgentContext, options: IndyVdrDidCreateOptions): Promise { const seed = options.secret?.seed - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + const privateKey = options.secret?.privateKey + + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid privateKey provided', + }, + } + } + + if (seed && (typeof seed !== 'object' || seed.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -57,13 +71,14 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { let did = options.did let id - if (seed && did) { + const allowOne = [privateKey, seed, did].filter((e) => e !== undefined) + if (allowOne.length > 1) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, didState: { state: 'failed', - reason: `Only one of 'seed' and 'did' must be provided`, + reason: `Only one of 'seed', 'privateKey' and 'did' must be provided`, }, } } @@ -88,7 +103,7 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { } } else { // Create a new key and calculate did according to the rules for indy did method - const key = await agentContext.wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + const key = await agentContext.wallet.createKey({ privateKey, seed, keyType: KeyType.Ed25519 }) const buffer = Hasher.hash(key.publicKey, 'sha2-256') id = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) @@ -187,7 +202,8 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + seed: options.secret?.seed?.toString(), + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -325,7 +341,8 @@ export interface IndyVdrDidCreateOptions extends DidCreateOptions { verkey?: string } secret?: { - seed?: string + seed?: Buffer + privateKey?: Buffer } } diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index 6b24cc5c82..d148744502 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -60,7 +60,7 @@ describe('Indy VDR registrar E2E', () => { } signerKey = await wallet.createKey({ - seed: '000000000000000000000000Trustee9', + privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), keyType: KeyType.Ed25519, }) }) @@ -224,13 +224,15 @@ describe('Indy VDR registrar E2E', () => { }) test('can register a did:indy with services - did and verkey specified - using attrib endpoint', async () => { - // Generate a seed and the indy did. This allows us to create a new did every time + // Generate a private key and the indy did. This allows us to create a new did every time // but still check if the created output document is as expected. - const seed = Array(32 + 1) - .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) - .slice(0, 32) + const privateKey = TypedArrayEncoder.fromString( + Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + ) - const key = await wallet.createKey({ seed: seed, keyType: KeyType.Ed25519 }) + const key = await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(key.publicKey)) const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(key.publicKey) diff --git a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts index c3b0eaacbe..a40d4f72b4 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts @@ -1,6 +1,7 @@ import type { Key } from '@aries-framework/core' import { + TypedArrayEncoder, IndyWallet, CacheModuleConfig, InMemoryLruCache, @@ -47,7 +48,7 @@ describe('indy-vdr DID Resolver E2E', () => { } signerKey = await wallet.createKey({ - seed: '000000000000000000000000Trustee9', + privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), keyType: KeyType.Ed25519, }) }) diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index 2ea85b5e04..ee1faad9dc 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -1,6 +1,6 @@ import type { Key } from '@aries-framework/core' -import { IndyWallet, KeyType, SigningProviderRegistry } from '@aries-framework/core' +import { TypedArrayEncoder, IndyWallet, KeyType, SigningProviderRegistry } from '@aries-framework/core' import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from '@hyperledger/indy-vdr-shared' import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' @@ -35,7 +35,7 @@ describe('IndyVdrPoolService', () => { } signerKey = await wallet.createKey({ - seed: '000000000000000000000000Trustee9', + privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), keyType: KeyType.Ed25519, }) }) diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index 8b01c14c17..20b2898c84 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -1,6 +1,6 @@ import type { KeyDidCreateOptions } from '@aries-framework/core' -import { Agent, KeyType, W3cCredentialRecord, W3cVcModule } from '@aries-framework/core' +import { Agent, KeyType, W3cCredentialRecord, W3cVcModule, TypedArrayEncoder } from '@aries-framework/core' import nock, { cleanAll, enableNetConnect } from 'nock' import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' @@ -76,7 +76,7 @@ describe('OpenId4VcClient', () => { keyType: KeyType.Ed25519, }, secret: { - seed: '96213c3d7fc8d4d6754c7a0fd969598e', + privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), }, }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion From b6d66b1e9a75b4070dbfa0499ba972392c8d7b86 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 20 Feb 2023 00:59:42 +0100 Subject: [PATCH 053/139] refactor!: remove indy from core (#1286) Signed-off-by: Timo Glastra --- demo/package.json | 6 + demo/src/Alice.ts | 2 +- demo/src/BaseAgent.ts | 178 ++++- demo/src/Faber.ts | 87 ++- package.json | 4 +- .../action-menu/tests/action-menu.e2e.test.ts | 48 +- packages/anoncreds-rs/package.json | 4 +- .../AnonCredsRsHolderService.test.ts | 2 +- .../__tests__/AnonCredsRsServices.test.ts | 2 +- packages/anoncreds/package.json | 1 + packages/anoncreds/src/AnonCredsApi.ts | 8 +- packages/anoncreds/src/AnonCredsApiOptions.ts | 4 + .../src/formats/LegacyIndyCredentialFormat.ts | 2 +- .../LegacyIndyCredentialFormatService.ts | 24 +- .../src/formats/LegacyIndyProofFormat.ts | 8 +- .../formats/LegacyIndyProofFormatService.ts | 27 +- .../legacy-indy-format-services.test.ts | 2 +- packages/anoncreds/src/index.ts | 3 +- .../src/models/AnonCredsProofRequest.ts | 7 +- packages/anoncreds/src/models/exchange.ts | 1 + .../credentials}/v1/V1CredentialProtocol.ts | 190 +++-- .../V1CredentialProtocolCred.test.ts | 157 ++-- .../V1CredentialProtocolProposeOffer.test.ts | 77 +- .../v1-connectionless-credentials.e2e.test.ts | 95 +-- .../v1-credentials-auto-accept.e2e.test.ts | 312 ++++---- .../v1/__tests__/v1-credentials.e2e.test.ts | 57 +- .../errors/V1CredentialProblemReportError.ts | 23 + .../protocols/credentials/v1/errors/index.ts | 1 + .../v1/handlers/V1CredentialAckHandler.ts | 2 +- .../V1CredentialProblemReportHandler.ts | 2 +- .../v1/handlers/V1IssueCredentialHandler.ts | 7 +- .../v1/handlers/V1OfferCredentialHandler.ts | 20 +- .../v1/handlers/V1ProposeCredentialHandler.ts | 6 +- .../v1/handlers/V1RequestCredentialHandler.ts | 7 +- .../credentials}/v1/handlers/index.ts | 1 - .../src/protocols/credentials}/v1/index.ts | 0 .../v1/messages/V1CredentialAckMessage.ts | 5 +- .../v1/messages/V1CredentialPreview.ts | 15 +- .../V1CredentialProblemReportMessage.ts | 5 +- .../v1/messages/V1IssueCredentialMessage.ts | 13 +- .../v1/messages/V1OfferCredentialMessage.ts | 13 +- .../v1/messages/V1ProposeCredentialMessage.ts | 22 +- .../v1/messages/V1RequestCredentialMessage.ts | 11 +- .../credentials}/v1/messages/index.ts | 0 packages/anoncreds/src/protocols/index.ts | 2 + .../protocols/proofs}/v1/V1ProofProtocol.ts | 164 ++-- .../v1/__tests__/V1ProofProtocol.test.ts | 43 +- .../v1-connectionless-proofs.e2e.test.ts | 433 +++++++++++ .../v1-indy-proof-negotiation.e2e.test.ts} | 231 +++--- .../v1-indy-proof-presentation.e2e.test.ts} | 143 ++-- .../v1-indy-proof-proposal.e2e.test.ts | 106 +++ .../v1-indy-proof-request.e2e.test.ts | 81 +- .../v1/__tests__/v1-indy-proofs.e2e.test.ts | 318 ++++---- .../v1-proofs-auto-accept.e2e.test.ts | 272 +++++++ .../V1PresentationProblemReportError.ts | 8 +- .../src/protocols/proofs}/v1/errors/index.ts | 0 .../v1/handlers/V1PresentationAckHandler.ts | 2 +- .../v1/handlers/V1PresentationHandler.ts | 7 +- .../V1PresentationProblemReportHandler.ts | 2 +- .../handlers/V1ProposePresentationHandler.ts | 6 +- .../handlers/V1RequestPresentationHandler.ts | 15 +- .../protocols/proofs}/v1/handlers/index.ts | 0 .../src/protocols/proofs}/v1/index.ts | 0 .../v1/messages/V1PresentationAckMessage.ts | 5 +- .../v1/messages/V1PresentationMessage.ts | 11 +- .../V1PresentationProblemReportMessage.ts | 5 +- .../messages/V1ProposePresentationMessage.ts | 3 +- .../messages/V1RequestPresentationMessage.ts | 11 +- .../protocols/proofs}/v1/messages/index.ts | 0 .../v1/models/V1PresentationPreview.ts | 19 +- .../src/protocols/proofs}/v1/models/index.ts | 0 .../registry/CredentialDefinitionOptions.ts | 2 +- .../credentialPreviewAttributes.test.ts | 143 ++++ .../__tests__/hasDuplicateGroupNames.test.ts | 10 +- .../__tests__/legacyIndyIdentifiers.test.ts | 34 + .../anoncreds/src/utils/composeAutoAccept.ts | 21 + .../src/utils/credentialPreviewAttributes.ts | 27 + .../src/utils/hasDuplicateGroupNames.ts | 12 +- packages/anoncreds/src/utils/index.ts | 10 +- .../src/utils/legacyIndyIdentifiers.ts | 5 + packages/anoncreds/tests/anoncreds.test.ts | 3 +- .../anoncreds/tests/legacyAnonCredsSetup.ts | 438 +++++++++++ packages/askar/package.json | 4 +- packages/askar/src/wallet/AskarWallet.ts | 8 +- packages/askar/tests/helpers.ts | 9 - .../tests/bbs-signatures.e2e.test.ts | 12 +- .../tests/bbs-signing-provider.e2e.test.ts | 32 +- ...proof.credentials.propose-offerBbs.test.ts | 169 ++--- packages/core/package.json | 3 +- packages/core/src/agent/Agent.ts | 22 +- packages/core/src/agent/AgentConfig.ts | 14 - packages/core/src/agent/AgentDependencies.ts | 2 - packages/core/src/agent/AgentModules.ts | 8 - packages/core/src/agent/BaseAgent.ts | 4 - packages/core/src/agent/MessageSender.ts | 2 +- .../core/src/agent/__tests__/Agent.test.ts | 23 +- .../src/agent/__tests__/AgentModules.test.ts | 8 - .../src/crypto/__tests__/JwsService.test.ts | 9 +- .../signature/SignatureDecoratorUtils.test.ts | 9 +- packages/core/src/index.ts | 8 +- .../__tests__/basic-messages.e2e.test.ts | 23 +- .../src/modules/cache/InMemoryLruCache.ts | 4 + .../SingleContextLruCacheRecord.ts | 3 + .../__tests__/ConnectionService.test.ts | 8 +- .../__tests__/connection-manual.e2e.test.ts | 62 +- .../modules/credentials/CredentialsModule.ts | 22 +- .../credentials/CredentialsModuleConfig.ts | 4 +- .../__tests__/CredentialsModule.test.ts | 9 +- .../modules/credentials/__tests__/fixtures.ts | 41 - .../errors/CredentialProblemReportError.ts | 23 - .../src/modules/credentials/errors/index.ts | 2 - .../IndyCredentialFormatService.test.ts | 443 ----------- .../src/modules/credentials/formats/index.ts | 1 - .../formats/indy/IndyCredentialFormat.ts | 65 -- .../indy/IndyCredentialFormatService.ts | 594 --------------- .../formats/indy/IndyCredentialUtils.ts | 207 ------ .../__tests__/IndyCredentialUtils.test.ts | 224 ------ .../modules/credentials/formats/indy/index.ts | 3 - .../formats/indy/models/IndyCredPropose.ts | 81 -- .../formats/indy/models/IndyCredential.ts | 34 - .../formats/indy/models/IndyCredentialInfo.ts | 60 -- .../formats/indy/models/IndyCredentialView.ts | 24 - .../indy/models/IndyRevocationInterval.ts | 18 - .../__tests__/IndyCredentialView.test.ts | 24 - .../credentials/formats/indy/models/index.ts | 5 - .../jsonld/JsonLdCredentialFormatService.ts | 2 +- .../JsonLdCredentialFormatService.test.ts | 70 +- .../core/src/modules/credentials/index.ts | 1 - .../CredentialProblemReportReason.ts | 0 .../src/modules/credentials/models/index.ts | 1 + .../src/modules/credentials/protocol/index.ts | 10 +- .../services/RevocationNotificationService.ts | 19 +- .../RevocationNotificationService.test.ts | 32 +- .../protocol/v2/V2CredentialProtocol.ts | 5 +- .../V2CredentialProtocolCred.test.ts | 161 ++-- .../V2CredentialProtocolOffer.test.ts | 134 ++-- .../v2-connectionless-credentials.e2e.test.ts | 50 +- .../v2-credentials-auto-accept.e2e.test.ts | 385 +++++----- .../v2/__tests__/v2-credentials.e2e.test.ts | 134 ++-- ...ldproof.connectionless-credentials.test.ts | 174 ++--- ...v2.ldproof.credentials-auto-accept.test.ts | 153 ++-- ...f.credentials.propose-offerED25519.test.ts | 665 +++++++++-------- .../errors/V2CredentialProblemReportError.ts | 23 + .../credentials/protocol/v2/errors/index.ts | 1 + .../modules/credentials/protocol/v2/index.ts | 1 + .../repository/CredentialExchangeRecord.ts | 33 +- .../repository/CredentialMetadataTypes.ts | 16 - .../CredentialExchangeRecord.test.ts | 42 -- .../modules/credentials/repository/index.ts | 1 - .../core/src/modules/dids/DidsModuleConfig.ts | 27 +- .../dids/__tests__/DidsModuleConfig.test.ts | 17 +- .../dids/__tests__/dids-registrar.e2e.test.ts | 139 +--- .../dids/__tests__/dids-resolver.e2e.test.ts | 79 +- .../modules/dids/__tests__/peer-did.test.ts | 15 +- .../core/src/modules/dids/methods/index.ts | 1 - .../methods/sov/IndySdkSovDidRegistrar.ts | 247 ------ .../dids/methods/sov/IndySdkSovDidResolver.ts | 88 --- .../__tests__/IndySdkSovDidRegistrar.test.ts | 374 ---------- .../__tests__/IndySdkSovDidResolver.test.ts | 123 --- .../src/modules/dids/methods/sov/index.ts | 2 - .../core/src/modules/dids/methods/sov/util.ts | 123 --- .../dids/services/DidResolverService.ts | 5 +- .../__tests__/DidResolverService.test.ts | 1 + .../v1-discover-features.e2e.test.ts | 64 +- .../v2-discover-features.e2e.test.ts | 59 +- packages/core/src/modules/indy/IndyModule.ts | 16 - .../modules/indy/__tests__/IndyModule.test.ts | 27 - packages/core/src/modules/indy/index.ts | 2 - .../AnonCredsCredentialDefinitionRecord.ts | 31 - ...AnonCredsCredentialDefinitionRepository.ts | 27 - .../indy/repository/AnonCredsSchemaRecord.ts | 43 -- .../repository/AnonCredsSchemaRepository.ts | 27 - .../indy/services/IndyHolderService.ts | 294 -------- .../indy/services/IndyIssuerService.ts | 166 ----- .../indy/services/IndyRevocationService.ts | 198 ----- .../indy/services/IndyUtilitiesService.ts | 88 --- .../indy/services/IndyVerifierService.ts | 85 --- .../services/__mocks__/IndyHolderService.ts | 20 - .../services/__mocks__/IndyIssuerService.ts | 25 - .../services/__mocks__/IndyVerifierService.ts | 1 - .../core/src/modules/indy/services/index.ts | 5 - packages/core/src/modules/ledger/IndyPool.ts | 209 ------ packages/core/src/modules/ledger/LedgerApi.ts | 217 ------ .../core/src/modules/ledger/LedgerModule.ts | 37 - .../src/modules/ledger/LedgerModuleConfig.ts | 43 -- .../ledger/__tests__/IndyPoolService.test.ts | 427 ----------- .../ledger/__tests__/LedgerApi.test.ts | 399 ---------- .../ledger/__tests__/LedgerModule.test.ts | 26 - .../ledger/__tests__/ledgerUtils.test.ts | 45 -- .../src/modules/ledger/error/LedgerError.ts | 7 - .../ledger/error/LedgerNotConfiguredError.ts | 7 - .../ledger/error/LedgerNotFoundError.ts | 7 - .../core/src/modules/ledger/error/index.ts | 3 - packages/core/src/modules/ledger/index.ts | 4 - .../core/src/modules/ledger/ledgerUtil.ts | 9 - .../ledger/services/IndyLedgerService.ts | 503 ------------- .../ledger/services/IndyPoolService.ts | 349 --------- .../core/src/modules/ledger/services/index.ts | 2 - .../oob/__tests__/OutOfBandService.test.ts | 22 +- .../oob/__tests__/connect-to-self.e2e.test.ts | 14 +- .../core/src/modules/proofs/ProofsModule.ts | 23 +- .../src/modules/proofs/ProofsModuleConfig.ts | 6 +- .../proofs/__tests__/ProofsModule.test.ts | 5 +- .../src/modules/proofs/__tests__/fixtures.ts | 47 -- .../errors/InvalidEncodedValueError.ts | 3 - .../errors/MissingIndyProofMessageError.ts | 3 - .../core/src/modules/proofs/formats/index.ts | 2 - .../proofs/formats/indy/IndyProofFormat.ts | 80 -- .../formats/indy/IndyProofFormatService.ts | 584 --------------- .../formats/indy/__tests__/groupKeys.ts | 31 - .../formats/indy/__tests__/util.test.ts | 541 -------------- .../indy/errors/InvalidEncodedValueError.ts | 3 - .../proofs/formats/indy/errors/index.ts | 1 - .../src/modules/proofs/formats/indy/index.ts | 2 - .../formats/indy/models/AttributeFilter.ts | 145 ---- .../formats/indy/models/PartialProof.ts | 24 - .../formats/indy/models/PredicateType.ts | 6 - .../formats/indy/models/ProofAttribute.ts | 22 - .../formats/indy/models/ProofAttributeInfo.ts | 42 -- .../formats/indy/models/ProofIdentifier.ts | 33 - .../formats/indy/models/ProofPredicateInfo.ts | 53 -- .../formats/indy/models/ProofRequest.ts | 94 --- .../formats/indy/models/RequestedAttribute.ts | 47 -- .../indy/models/RequestedCredentials.ts | 81 -- .../formats/indy/models/RequestedPredicate.ts | 42 -- .../formats/indy/models/RequestedProof.ts | 24 - .../indy/models/RetrievedCredentials.ts | 20 - .../models/__tests__/ProofRequest.test.ts | 79 -- .../proofs/formats/indy/models/index.ts | 9 - .../src/modules/proofs/formats/indy/util.ts | 266 ------- .../sortRequestedCredentials.test.ts | 48 -- .../indy/util/sortRequestedCredentials.ts | 33 - .../core/src/modules/proofs/protocol/index.ts | 8 +- .../__tests__/indy-proof-proposal.test.e2e.ts | 103 --- .../v1-connectionless-proofs.e2e.test.ts | 379 ---------- .../v1-proofs-auto-accept.e2e.test.ts | 205 ----- .../v2/__tests__/V2ProofProtocol.test.ts | 10 +- .../v2-indy-connectionless-proofs.e2e.test.ts | 394 +++++----- .../v2-indy-proof-negotiation.test.ts | 332 ++++----- .../v2-indy-proof-presentation.e2e.test.ts | 157 ++-- .../v2-indy-proof-proposal.e2e.test.ts | 94 --- .../v2-indy-proof-request.e2e.test.ts | 132 ++-- .../v2-indy-proofs-auto-accept.2e.test.ts | 252 ++++--- .../v2/__tests__/v2-indy-proofs.e2e.test.ts | 334 +++++---- .../routing/__tests__/mediation.test.ts | 40 +- .../modules/routing/__tests__/pickup.test.ts | 25 +- .../MediationRecipientService.test.ts | 11 - .../services/__tests__/RoutingService.test.ts | 17 +- .../vc/__tests__/W3cCredentialService.test.ts | 13 +- packages/core/src/plugins/Module.ts | 2 + packages/core/src/storage/BaseRecord.ts | 5 +- .../core/src/storage/IndyStorageService.ts | 327 -------- .../DidCommMessageRepository.test.ts | 10 +- .../__tests__/IndyStorageService.test.ts | 323 -------- .../src/storage/__tests__/Repository.test.ts | 9 +- .../storage/migration/__tests__/0.1.test.ts | 15 + .../storage/migration/__tests__/0.2.test.ts | 12 + .../storage/migration/__tests__/0.3.test.ts | 6 + .../__tests__/UpdateAssistant.test.ts | 8 +- .../__tests__/__snapshots__/0.1.test.ts.snap | 16 - .../migration/__tests__/backup.test.ts | 3 +- .../migration/updates/0.1-0.2/credential.ts | 25 +- packages/core/src/types.ts | 15 - packages/core/src/utils/__tests__/did.test.ts | 188 ----- .../utils/__tests__/indyIdentifiers.test.ts | 62 -- .../core/src/utils/__tests__/regex.test.ts | 29 - packages/core/src/utils/did.ts | 154 ---- packages/core/src/utils/index.ts | 3 - packages/core/src/utils/indyIdentifiers.ts | 53 -- packages/core/src/utils/indyProofRequest.ts | 32 - packages/core/src/utils/regex.ts | 4 - packages/core/src/utils/transformers.ts | 39 - packages/core/src/utils/type.ts | 4 - packages/core/src/wallet/IndyWallet.test.ts | 132 ---- packages/core/src/wallet/IndyWallet.ts | 703 ------------------ packages/core/src/wallet/index.ts | 1 - .../core/src/wallet/util/assertIndyWallet.ts | 12 - packages/core/tests/agents.test.ts | 45 +- packages/core/tests/connections.test.ts | 79 +- packages/core/tests/events.ts | 21 + packages/core/tests/generic-records.test.ts | 11 +- packages/core/tests/helpers.ts | 507 ++----------- packages/core/tests/index.ts | 9 + packages/core/tests/indySdk.ts | 3 + packages/core/tests/jsonld.ts | 171 +++++ packages/core/tests/ledger.test.ts | 170 ----- packages/core/tests/migration.test.ts | 3 +- .../core/tests/multi-protocol-version.test.ts | 53 +- .../tests/oob-mediation-provision.test.ts | 68 +- packages/core/tests/oob-mediation.test.ts | 39 +- packages/core/tests/oob.test.ts | 81 +- .../core/tests/proofs-sub-protocol.test.ts | 209 +++--- packages/core/tests/transport.ts | 18 + packages/core/tests/wallet.test.ts | 5 +- packages/indy-sdk/package.json | 1 + packages/indy-sdk/src/IndySdkModule.ts | 33 +- packages/indy-sdk/src/IndySdkModuleConfig.ts | 25 + .../services/IndySdkAnonCredsRegistry.ts | 3 +- .../src/dids/IndySdkSovDidRegistrar.ts | 47 +- .../src/dids/IndySdkSovDidResolver.ts | 27 +- .../__tests__/IndySdkSovDidRegistrar.test.ts | 383 ++++++++++ .../__tests__/IndySdkSovDidResolver.test.ts | 128 ++++ .../didSovR1xKJw17sUoXhejEpugMYJ.json | 0 .../didSovWJz9mHyW9BZksioQnRsrAo.json | 0 packages/indy-sdk/src/ledger/IndySdkPool.ts | 34 +- .../indy-sdk/src/ledger/IndySdkPoolService.ts | 58 +- .../__tests__/IndySdkPoolService.test.ts | 108 ++- .../src}/ledger/__tests__/didResponses.ts | 6 +- .../src/storage/IndySdkStorageService.ts | 4 + .../__tests__/IndySdkStorageService.test.ts | 57 +- packages/indy-sdk/src/wallet/IndySdkWallet.ts | 2 +- .../wallet/__tests__/IndySdkWallet.test.ts | 6 +- .../indy-sdk-anoncreds-registry.e2e.test.ts | 24 +- .../tests/postgres.e2e.test.ts | 50 +- packages/indy-sdk/tests/setupIndySdkModule.ts | 29 + .../tests/sov-did-registrar.e2e.test.ts | 125 ++++ .../tests/sov-did-resolver.e2e.test.ts | 74 ++ packages/indy-vdr/package.json | 4 +- packages/indy-vdr/src/IndyVdrModule.ts | 12 +- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 6 +- packages/indy-vdr/src/pool/IndyVdrPool.ts | 22 +- .../indy-vdr/src/pool/IndyVdrPoolService.ts | 21 +- .../indy-vdr-anoncreds-registry.e2e.test.ts | 34 +- .../tests/indy-vdr-did-registrar.e2e.test.ts | 15 +- .../tests/indy-vdr-did-resolver.e2e.test.ts | 13 +- .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 15 +- packages/node/bin/is-indy-installed.js | 17 - packages/node/package.json | 7 +- packages/node/src/PostgresPlugin.ts | 19 +- packages/node/src/index.ts | 10 +- .../tests/openid4vc-client.e2e.test.ts | 29 +- .../__tests__/QuestionAnswerService.test.ts | 16 +- .../tests/question-answer.e2e.test.ts | 46 +- packages/react-native/package.json | 4 +- packages/react-native/src/index.ts | 5 - .../tenants/src/__tests__/TenantAgent.test.ts | 5 + .../tenants/src/__tests__/TenantsApi.test.ts | 5 +- .../tenants/tests/tenant-sessions.e2e.test.ts | 4 +- packages/tenants/tests/tenants.e2e.test.ts | 5 +- .../extension-module/tests/dummy.e2e.test.ts | 23 +- tests/InMemoryStorageService.ts | 5 +- .../e2e-askar-indy-sdk-wallet-subject.test.ts | 60 +- tests/e2e-http.test.ts | 53 +- tests/e2e-subject.test.ts | 54 +- tests/e2e-test.ts | 86 ++- tests/e2e-ws-pickup-v2.test.ts | 57 +- tests/e2e-ws.test.ts | 53 +- yarn.lock | 79 +- 348 files changed, 7161 insertions(+), 16681 deletions(-) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/V1CredentialProtocol.ts (89%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/__tests__/V1CredentialProtocolCred.test.ts (84%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts (79%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/__tests__/v1-connectionless-credentials.e2e.test.ts (66%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts (58%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/__tests__/v1-credentials.e2e.test.ts (86%) create mode 100644 packages/anoncreds/src/protocols/credentials/v1/errors/V1CredentialProblemReportError.ts create mode 100644 packages/anoncreds/src/protocols/credentials/v1/errors/index.ts rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/V1CredentialAckHandler.ts (94%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/V1CredentialProblemReportHandler.ts (94%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/V1IssueCredentialHandler.ts (88%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/V1OfferCredentialHandler.ts (82%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/V1ProposeCredentialHandler.ts (86%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/V1RequestCredentialHandler.ts (88%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/handlers/index.ts (75%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/index.ts (100%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1CredentialAckMessage.ts (75%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1CredentialPreview.ts (80%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1CredentialProblemReportMessage.ts (70%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1IssueCredentialMessage.ts (75%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1OfferCredentialMessage.ts (80%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1ProposeCredentialMessage.ts (86%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/V1RequestCredentialMessage.ts (80%) rename packages/{core/src/modules/credentials/protocol => anoncreds/src/protocols/credentials}/v1/messages/index.ts (100%) create mode 100644 packages/anoncreds/src/protocols/index.ts rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/V1ProofProtocol.ts (88%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/__tests__/V1ProofProtocol.test.ts (83%) create mode 100644 packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts rename packages/{core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts => anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-negotiation.e2e.test.ts} (53%) rename packages/{core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts => anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-presentation.e2e.test.ts} (60%) create mode 100644 packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-proposal.e2e.test.ts rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/__tests__/v1-indy-proof-request.e2e.test.ts (59%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/__tests__/v1-indy-proofs.e2e.test.ts (72%) create mode 100644 packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/errors/V1PresentationProblemReportError.ts (62%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/errors/index.ts (100%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/handlers/V1PresentationAckHandler.ts (93%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/handlers/V1PresentationHandler.ts (90%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/handlers/V1PresentationProblemReportHandler.ts (94%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/handlers/V1ProposePresentationHandler.ts (86%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/handlers/V1RequestPresentationHandler.ts (86%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/handlers/index.ts (100%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/index.ts (100%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/messages/V1PresentationAckMessage.ts (64%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/messages/V1PresentationMessage.ts (84%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/messages/V1PresentationProblemReportMessage.ts (72%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/messages/V1ProposePresentationMessage.ts (91%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/messages/V1RequestPresentationMessage.ts (82%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/messages/index.ts (100%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/models/V1PresentationPreview.ts (87%) rename packages/{core/src/modules/proofs/protocol => anoncreds/src/protocols/proofs}/v1/models/index.ts (100%) create mode 100644 packages/anoncreds/src/utils/__tests__/credentialPreviewAttributes.test.ts create mode 100644 packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts create mode 100644 packages/anoncreds/src/utils/composeAutoAccept.ts create mode 100644 packages/anoncreds/src/utils/credentialPreviewAttributes.ts create mode 100644 packages/anoncreds/src/utils/legacyIndyIdentifiers.ts create mode 100644 packages/anoncreds/tests/legacyAnonCredsSetup.ts delete mode 100644 packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts delete mode 100644 packages/core/src/modules/credentials/errors/index.ts delete mode 100644 packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/index.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/IndyCredPropose.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/IndyCredential.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/IndyCredentialInfo.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/IndyCredentialView.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/IndyRevocationInterval.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/__tests__/IndyCredentialView.test.ts delete mode 100644 packages/core/src/modules/credentials/formats/indy/models/index.ts rename packages/core/src/modules/credentials/formats/{ => jsonld}/__tests__/JsonLdCredentialFormatService.test.ts (90%) rename packages/core/src/modules/credentials/{errors => models}/CredentialProblemReportReason.ts (100%) create mode 100644 packages/core/src/modules/credentials/protocol/v2/errors/V2CredentialProblemReportError.ts create mode 100644 packages/core/src/modules/credentials/protocol/v2/errors/index.ts delete mode 100644 packages/core/src/modules/credentials/repository/CredentialMetadataTypes.ts delete mode 100644 packages/core/src/modules/credentials/repository/__tests__/CredentialExchangeRecord.test.ts delete mode 100644 packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts delete mode 100644 packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts delete mode 100644 packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts delete mode 100644 packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts delete mode 100644 packages/core/src/modules/dids/methods/sov/index.ts delete mode 100644 packages/core/src/modules/dids/methods/sov/util.ts delete mode 100644 packages/core/src/modules/indy/IndyModule.ts delete mode 100644 packages/core/src/modules/indy/__tests__/IndyModule.test.ts delete mode 100644 packages/core/src/modules/indy/index.ts delete mode 100644 packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts delete mode 100644 packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRepository.ts delete mode 100644 packages/core/src/modules/indy/repository/AnonCredsSchemaRecord.ts delete mode 100644 packages/core/src/modules/indy/repository/AnonCredsSchemaRepository.ts delete mode 100644 packages/core/src/modules/indy/services/IndyHolderService.ts delete mode 100644 packages/core/src/modules/indy/services/IndyIssuerService.ts delete mode 100644 packages/core/src/modules/indy/services/IndyRevocationService.ts delete mode 100644 packages/core/src/modules/indy/services/IndyUtilitiesService.ts delete mode 100644 packages/core/src/modules/indy/services/IndyVerifierService.ts delete mode 100644 packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts delete mode 100644 packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts delete mode 100644 packages/core/src/modules/indy/services/__mocks__/IndyVerifierService.ts delete mode 100644 packages/core/src/modules/indy/services/index.ts delete mode 100644 packages/core/src/modules/ledger/IndyPool.ts delete mode 100644 packages/core/src/modules/ledger/LedgerApi.ts delete mode 100644 packages/core/src/modules/ledger/LedgerModule.ts delete mode 100644 packages/core/src/modules/ledger/LedgerModuleConfig.ts delete mode 100644 packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts delete mode 100644 packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts delete mode 100644 packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts delete mode 100644 packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts delete mode 100644 packages/core/src/modules/ledger/error/LedgerError.ts delete mode 100644 packages/core/src/modules/ledger/error/LedgerNotConfiguredError.ts delete mode 100644 packages/core/src/modules/ledger/error/LedgerNotFoundError.ts delete mode 100644 packages/core/src/modules/ledger/error/index.ts delete mode 100644 packages/core/src/modules/ledger/index.ts delete mode 100644 packages/core/src/modules/ledger/ledgerUtil.ts delete mode 100644 packages/core/src/modules/ledger/services/IndyLedgerService.ts delete mode 100644 packages/core/src/modules/ledger/services/IndyPoolService.ts delete mode 100644 packages/core/src/modules/ledger/services/index.ts delete mode 100644 packages/core/src/modules/proofs/__tests__/fixtures.ts delete mode 100644 packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts delete mode 100644 packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/errors/InvalidEncodedValueError.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/errors/index.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/index.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/AttributeFilter.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/PartialProof.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/PredicateType.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/ProofAttribute.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/ProofIdentifier.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/RequestedProof.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/RetrievedCredentials.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/models/index.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/util.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts delete mode 100644 packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts delete mode 100644 packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts delete mode 100644 packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts delete mode 100644 packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts delete mode 100644 packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts delete mode 100644 packages/core/src/storage/IndyStorageService.ts delete mode 100644 packages/core/src/storage/__tests__/IndyStorageService.test.ts delete mode 100644 packages/core/src/utils/__tests__/did.test.ts delete mode 100644 packages/core/src/utils/__tests__/indyIdentifiers.test.ts delete mode 100644 packages/core/src/utils/__tests__/regex.test.ts delete mode 100644 packages/core/src/utils/indyIdentifiers.ts delete mode 100644 packages/core/src/utils/indyProofRequest.ts delete mode 100644 packages/core/src/utils/regex.ts delete mode 100644 packages/core/src/wallet/IndyWallet.test.ts delete mode 100644 packages/core/src/wallet/IndyWallet.ts delete mode 100644 packages/core/src/wallet/util/assertIndyWallet.ts create mode 100644 packages/core/tests/events.ts create mode 100644 packages/core/tests/index.ts create mode 100644 packages/core/tests/indySdk.ts create mode 100644 packages/core/tests/jsonld.ts delete mode 100644 packages/core/tests/ledger.test.ts create mode 100644 packages/core/tests/transport.ts create mode 100644 packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts create mode 100644 packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts rename packages/{core/src/modules => indy-sdk/src}/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json (100%) rename packages/{core/src/modules => indy-sdk/src}/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json (100%) rename packages/{core/src/modules => indy-sdk/src}/ledger/__tests__/didResponses.ts (94%) rename packages/{core => indy-sdk}/tests/postgres.e2e.test.ts (73%) create mode 100644 packages/indy-sdk/tests/setupIndySdkModule.ts create mode 100644 packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts create mode 100644 packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts delete mode 100755 packages/node/bin/is-indy-installed.js diff --git a/demo/package.json b/demo/package.json index b41cae2066..247a4513cd 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,9 +14,15 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "devDependencies": { + "@aries-framework/anoncreds": "*", + "@aries-framework/anoncreds-rs": "*", + "@aries-framework/askar": "*", "@aries-framework/core": "*", + "@aries-framework/indy-sdk": "*", + "@aries-framework/indy-vdr": "*", "@aries-framework/node": "*", "@types/figlet": "^1.5.4", + "@types/indy-sdk": "^1.16.26", "@types/inquirer": "^8.1.3", "clear": "^0.1.0", "commander": "^8.3.0", diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index aa705ca7a4..2de378d8c1 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -8,7 +8,7 @@ export class Alice extends BaseAgent { public connectionRecordFaberId?: string public constructor(port: number, name: string) { - super(port, name) + super({ port, name, useLegacyIndySdk: true }) this.connected = false } diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index abf507014e..26429ca358 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -1,7 +1,34 @@ +import type { IndySdkPoolConfig } from '../../packages/indy-sdk/src/ledger' +import type { IndyVdrPoolConfig } from '../../packages/indy-vdr/src/pool' import type { InitConfig } from '@aries-framework/core' -import { Agent, AutoAcceptCredential, AutoAcceptProof, HttpOutboundTransport } from '@aries-framework/core' +import { + AnonCredsModule, + LegacyIndyCredentialFormatService, + LegacyIndyProofFormatService, + V1CredentialProtocol, + V1ProofProtocol, +} from '@aries-framework/anoncreds' +import { AnonCredsRsModule } from '@aries-framework/anoncreds-rs' +import { AskarModule } from '@aries-framework/askar' +import { + TypedArrayEncoder, + KeyType, + DidsModule, + V2ProofProtocol, + V2CredentialProtocol, + ProofsModule, + AutoAcceptProof, + AutoAcceptCredential, + CredentialsModule, + Agent, + HttpOutboundTransport, +} from '@aries-framework/core' +import { IndySdkAnonCredsRegistry, IndySdkModule, IndySdkSovDidResolver } from '@aries-framework/indy-sdk' +import { IndyVdrAnonCredsRegistry, IndyVdrModule, IndyVdrSovDidResolver } from '@aries-framework/indy-vdr' import { agentDependencies, HttpInboundTransport } from '@aries-framework/node' +import { randomUUID } from 'crypto' +import indySdk from 'indy-sdk' import { greenText } from './OutputClass' @@ -10,46 +37,163 @@ const bcovrin = `{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","blsk {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node3","blskey":"3WFpdbg7C5cnLYZwFZevJqhubkFALBfCBBok15GdrKMUhUjGsk3jV6QKj6MZgEubF7oqCafxNdkm7eswgA4sdKTRc82tLGzZBd6vNqU8dupzup6uYUf32KTHTPQbuUM8Yk4QFXjEf2Usu2TJcNkdgpyeUSX42u5LqdDDpNSWUK5deC5","blskey_pop":"QwDeb2CkNSx6r8QC8vGQK3GRv7Yndn84TGNijX8YXHPiagXajyfTjoR87rXUu4G4QLk2cF8NNyqWiYMus1623dELWwx57rLCFqGh7N4ZRbGDRP4fnVcaKg1BcUxQ866Ven4gw8y4N56S5HzxXNBZtLYmhGHvDtk6PFkFwCvxYrNYjh","client_ip":"138.197.138.255","client_port":9706,"node_ip":"138.197.138.255","node_port":9705,"services":["VALIDATOR"]},"dest":"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya"},"metadata":{"from":"4cU41vWW82ArfxJxHkzXPG"},"type":"0"},"txnMetadata":{"seqNo":3,"txnId":"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4"},"ver":"1"} {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node4","blskey":"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw","blskey_pop":"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP","client_ip":"138.197.138.255","client_port":9708,"node_ip":"138.197.138.255","node_port":9707,"services":["VALIDATOR"]},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"},"metadata":{"from":"TWwCRQRZ2ZHMJFn9TzLp7W"},"type":"0"},"txnMetadata":{"seqNo":4,"txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"},"ver":"1"}` +const indyNetworkConfig = { + // Need unique network id as we will have multiple agent processes in the agent + id: randomUUID(), + genesisTransactions: bcovrin, + indyNamespace: 'bcovrin:test', + isProduction: false, + connectOnStartup: true, +} satisfies IndySdkPoolConfig | IndyVdrPoolConfig + +type DemoAgent = Agent | ReturnType> + export class BaseAgent { public port: number public name: string public config: InitConfig - public agent: Agent + public agent: DemoAgent + public anonCredsIssuerId: string + public useLegacyIndySdk: boolean - public constructor(port: number, name: string) { + public constructor({ + port, + name, + useLegacyIndySdk = false, + }: { + port: number + name: string + useLegacyIndySdk?: boolean + }) { this.name = name this.port = port - const config: InitConfig = { + const config = { label: name, walletConfig: { id: name, key: name, }, - publicDidSeed: '6b8b882e2618fa5d45ee7229ca880083', - indyLedgers: [ - { - genesisTransactions: bcovrin, - id: 'greenlights' + name, - indyNamespace: 'greenlights' + name, - isProduction: false, - }, - ], + publicDidSeed: 'afjdemoverysercure00000000000000', endpoints: [`http://localhost:${this.port}`], autoAcceptConnections: true, - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - autoAcceptProofs: AutoAcceptProof.ContentApproved, - } + } satisfies InitConfig this.config = config - this.agent = new Agent({ config, dependencies: agentDependencies }) + // TODO: do not hardcode this + this.anonCredsIssuerId = '2jEvRuKmfBJTRa7QowDpNN' + this.useLegacyIndySdk = useLegacyIndySdk + + this.agent = new Agent({ + config, + dependencies: agentDependencies, + modules: useLegacyIndySdk ? getLegacyIndySdkModules() : getAskarAnonCredsIndyModules(), + }) this.agent.registerInboundTransport(new HttpInboundTransport({ port })) this.agent.registerOutboundTransport(new HttpOutboundTransport()) } public async initializeAgent() { await this.agent.initialize() + + // FIXME: + // We need to make sure the key to submit transactions is created. We should update this to use the dids module, and allow + // to add an existing did based on a seed/secretKey, and not register it on the the ledger. However for Indy SDK we currently + // use the deprecated publicDidSeed property (which will register the did in the wallet), and for Askar we manually create the key + // in the wallet. + if (!this.useLegacyIndySdk) { + try { + await this.agent.context.wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey: TypedArrayEncoder.fromString('afjdemoverysercure00000000000000'), + }) + } catch (error) { + // We assume the key already exists, and that's why askar failed + } + } + console.log(greenText(`\nAgent ${this.name} created!\n`)) } } + +function getAskarAnonCredsIndyModules() { + const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() + const legacyIndyProofFormatService = new LegacyIndyProofFormatService() + + return { + credentials: new CredentialsModule({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + credentialProtocols: [ + new V1CredentialProtocol({ + indyCredentialFormat: legacyIndyCredentialFormatService, + }), + new V2CredentialProtocol({ + credentialFormats: [legacyIndyCredentialFormatService], + }), + ], + }), + proofs: new ProofsModule({ + autoAcceptProofs: AutoAcceptProof.ContentApproved, + proofProtocols: [ + new V1ProofProtocol({ + indyProofFormat: legacyIndyProofFormatService, + }), + new V2ProofProtocol({ + proofFormats: [legacyIndyProofFormatService], + }), + ], + }), + anoncreds: new AnonCredsModule({ + registries: [new IndyVdrAnonCredsRegistry()], + }), + anoncredsRs: new AnonCredsRsModule(), + indyVdr: new IndyVdrModule({ + networks: [indyNetworkConfig], + }), + dids: new DidsModule({ + resolvers: [new IndyVdrSovDidResolver()], + }), + askar: new AskarModule(), + } as const +} + +function getLegacyIndySdkModules() { + const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() + const legacyIndyProofFormatService = new LegacyIndyProofFormatService() + + return { + credentials: new CredentialsModule({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + credentialProtocols: [ + new V1CredentialProtocol({ + indyCredentialFormat: legacyIndyCredentialFormatService, + }), + new V2CredentialProtocol({ + credentialFormats: [legacyIndyCredentialFormatService], + }), + ], + }), + proofs: new ProofsModule({ + autoAcceptProofs: AutoAcceptProof.ContentApproved, + proofProtocols: [ + new V1ProofProtocol({ + indyProofFormat: legacyIndyProofFormatService, + }), + new V2ProofProtocol({ + proofFormats: [legacyIndyProofFormatService], + }), + ], + }), + anoncreds: new AnonCredsModule({ + registries: [new IndySdkAnonCredsRegistry()], + }), + indySdk: new IndySdkModule({ + indySdk, + networks: [indyNetworkConfig], + }), + dids: new DidsModule({ + resolvers: [new IndySdkSovDidResolver()], + }), + } as const +} diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index a19906d0fa..4585f82c7d 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -1,8 +1,8 @@ +import type { RegisterCredentialDefinitionReturnStateFinished } from '../../packages/anoncreds/src' import type { ConnectionRecord, ConnectionStateChangedEvent } from '@aries-framework/core' -import type { CredDef, Schema } from 'indy-sdk' import type BottomBar from 'inquirer/lib/ui/bottom-bar' -import { utils, V1CredentialPreview, ConnectionEventTypes } from '@aries-framework/core' +import { utils, ConnectionEventTypes } from '@aries-framework/core' import { ui } from 'inquirer' import { BaseAgent } from './BaseAgent' @@ -10,11 +10,11 @@ import { Color, greenText, Output, purpleText, redText } from './OutputClass' export class Faber extends BaseAgent { public outOfBandId?: string - public credentialDefinition?: CredDef + public credentialDefinition?: RegisterCredentialDefinitionReturnStateFinished public ui: BottomBar public constructor(port: number, name: string) { - super(port, name) + super({ port, name }) this.ui = new ui.BottomBar() } @@ -105,39 +105,57 @@ export class Faber extends BaseAgent { const schemaTemplate = { name: 'Faber College' + utils.uuid(), version: '1.0.0', - attributes: ['name', 'degree', 'date'], + attrNames: ['name', 'degree', 'date'], + issuerId: this.anonCredsIssuerId, } - this.printSchema(schemaTemplate.name, schemaTemplate.version, schemaTemplate.attributes) + this.printSchema(schemaTemplate.name, schemaTemplate.version, schemaTemplate.attrNames) this.ui.updateBottomBar(greenText('\nRegistering schema...\n', false)) - const schema = await this.agent.ledger.registerSchema(schemaTemplate) + + const { schemaState } = await this.agent.modules.anoncreds.registerSchema({ + schema: schemaTemplate, + options: { + didIndyNamespace: 'bcovrin:test', + }, + }) + + if (schemaState.state !== 'finished') { + throw new Error( + `Error registering schema: ${schemaState.state === 'failed' ? schemaState.reason : 'Not Finished'}}` + ) + } this.ui.updateBottomBar('\nSchema registered!\n') - return schema + return schemaState } - private async registerCredentialDefinition(schema: Schema) { + private async registerCredentialDefinition(schemaId: string) { this.ui.updateBottomBar('\nRegistering credential definition...\n') - this.credentialDefinition = await this.agent.ledger.registerCredentialDefinition({ - schema, - tag: 'latest', - supportRevocation: false, + const { credentialDefinitionState } = await this.agent.modules.anoncreds.registerCredentialDefinition({ + credentialDefinition: { + schemaId, + issuerId: this.anonCredsIssuerId, + tag: 'latest', + }, + options: { + didIndyNamespace: 'bcovrin:test', + }, }) + + if (credentialDefinitionState.state !== 'finished') { + throw new Error( + `Error registering credential definition: ${ + credentialDefinitionState.state === 'failed' ? credentialDefinitionState.reason : 'Not Finished' + }}` + ) + } + + this.credentialDefinition = credentialDefinitionState this.ui.updateBottomBar('\nCredential definition registered!!\n') return this.credentialDefinition } - private getCredentialPreview() { - const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'Alice Smith', - degree: 'Computer Science', - date: '01/01/2022', - }) - return credentialPreview - } - public async issueCredential() { const schema = await this.registerSchema() - const credDef = await this.registerCredentialDefinition(schema) - const credentialPreview = this.getCredentialPreview() + const credentialDefinition = await this.registerCredentialDefinition(schema.schemaId) const connectionRecord = await this.getConnectionRecord() this.ui.updateBottomBar('\nSending credential offer...\n') @@ -147,8 +165,21 @@ export class Faber extends BaseAgent { protocolVersion: 'v1', credentialFormats: { indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: credDef.id, + attributes: [ + { + name: 'name', + value: 'Alice Smith', + }, + { + name: 'degree', + value: 'Computer Science', + }, + { + name: 'date', + value: '01/01/2022', + }, + ], + credentialDefinitionId: credentialDefinition.credentialDefinitionId, }, }, }) @@ -169,7 +200,7 @@ export class Faber extends BaseAgent { name: 'name', restrictions: [ { - credentialDefinitionId: this.credentialDefinition?.id, + cred_def_id: this.credentialDefinition?.credentialDefinitionId, }, ], }, @@ -190,7 +221,7 @@ export class Faber extends BaseAgent { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: proofAttribute, + requested_attributes: proofAttribute, }, }, }) diff --git a/package.json b/package.json index 582f91a77e..8f5fee182f 100644 --- a/package.json +++ b/package.json @@ -47,11 +47,11 @@ "eslint-plugin-import": "^2.23.4", "eslint-plugin-prettier": "^3.4.0", "express": "^4.17.1", - "indy-sdk": "^1.16.0-dev-1636", + "indy-sdk": "^1.16.0-dev-1655", "jest": "^27.0.4", "lerna": "^4.0.0", "prettier": "^2.3.1", - "rxjs": "^7.2.0", + "rxjs": "^7.8.0", "ts-jest": "^27.0.3", "ts-node": "^10.0.0", "tsconfig-paths": "^4.1.2", diff --git a/packages/action-menu/tests/action-menu.e2e.test.ts b/packages/action-menu/tests/action-menu.e2e.test.ts index 553d7e0c20..8ba99acdbc 100644 --- a/packages/action-menu/tests/action-menu.e2e.test.ts +++ b/packages/action-menu/tests/action-menu.e2e.test.ts @@ -1,13 +1,9 @@ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { ConnectionRecord } from '@aries-framework/core' import { Agent } from '@aries-framework/core' -import { Subject } from 'rxjs' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { getAgentOptions, makeConnection } from '../../core/tests/helpers' -import testLogger from '../../core/tests/logger' +import { getAgentOptions, makeConnection, testLogger, setupSubjectTransports, indySdk } from '../../core/tests' +import { IndySdkModule } from '../../indy-sdk/src' import { ActionMenu, @@ -19,14 +15,19 @@ import { import { waitForActionMenuRecord } from './helpers' +const modules = { + actionMenu: new ActionMenuModule(), + indySdk: new IndySdkModule({ + indySdk, + }), +} + const faberAgentOptions = getAgentOptions( 'Faber Action Menu', { endpoints: ['rxjs:faber'], }, - { - actionMenu: new ActionMenuModule(), - } + modules ) const aliceAgentOptions = getAgentOptions( @@ -34,18 +35,12 @@ const aliceAgentOptions = getAgentOptions( { endpoints: ['rxjs:alice'], }, - { - actionMenu: new ActionMenuModule(), - } + modules ) describe('Action Menu', () => { - let faberAgent: Agent<{ - actionMenu: ActionMenuModule - }> - let aliceAgent: Agent<{ - actionMenu: ActionMenuModule - }> + let faberAgent: Agent + let aliceAgent: Agent let faberConnection: ConnectionRecord let aliceConnection: ConnectionRecord @@ -84,21 +79,12 @@ describe('Action Menu', () => { }) beforeEach(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + + setupSubjectTransports([faberAgent, aliceAgent]) + + await faberAgent.initialize() await aliceAgent.initialize() ;[aliceConnection, faberConnection] = await makeConnection(aliceAgent, faberAgent) }) diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index af35fc561c..49622b9fb7 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.5", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.6", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.5", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.6", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index bdfac8c48a..23565f8e2b 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -240,7 +240,7 @@ describe('AnonCredsRsHolderService', () => { }, } - const proof = await anonCredsHolderService.createProof(agentContext, { + await anonCredsHolderService.createProof(agentContext, { credentialDefinitions: { 'personcreddef:uri': personCredentialDefinition as AnonCredsCredentialDefinition, 'phonecreddef:uri': phoneCredentialDefinition as AnonCredsCredentialDefinition, diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index f881d22fa3..019063bcbb 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -57,7 +57,7 @@ describe('AnonCredsRsServices', () => { version: '1.0.0', }) - const { schemaState, schemaMetadata } = await registry.registerSchema(agentContext, { + const { schemaState } = await registry.registerSchema(agentContext, { schema, options: {}, }) diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 27ddffa7d6..7f0dfd3bc2 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -33,6 +33,7 @@ "devDependencies": { "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.0.7", + "rxjs": "^7.8.0", "typescript": "~4.9.4" } } diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index b52f4dbc0f..9e56a51ea5 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -1,5 +1,7 @@ -import type { AnonCredsCreateLinkSecretOptions } from './AnonCredsApiOptions' -import type { AnonCredsCredentialDefinition } from './models' +import type { + AnonCredsCreateLinkSecretOptions, + AnonCredsRegisterCredentialDefinitionOptions, +} from './AnonCredsApiOptions' import type { GetCredentialDefinitionReturn, GetRevocationStatusListReturn, @@ -207,7 +209,7 @@ export class AnonCredsApi { } public async registerCredentialDefinition(options: { - credentialDefinition: Omit + credentialDefinition: AnonCredsRegisterCredentialDefinitionOptions // TODO: options should support supportsRevocation at some points options: Extensible }): Promise { diff --git a/packages/anoncreds/src/AnonCredsApiOptions.ts b/packages/anoncreds/src/AnonCredsApiOptions.ts index 78a8e77728..860ea059df 100644 --- a/packages/anoncreds/src/AnonCredsApiOptions.ts +++ b/packages/anoncreds/src/AnonCredsApiOptions.ts @@ -1,4 +1,8 @@ +import type { AnonCredsCredentialDefinition } from './models' + export interface AnonCredsCreateLinkSecretOptions { linkSecretId?: string setAsDefault?: boolean } + +export type AnonCredsRegisterCredentialDefinitionOptions = Omit diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts index 78342fe833..f4a6f2a0d2 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts @@ -21,7 +21,7 @@ export type LegacyIndyCredentialProposalFormat = Omit< * * NOTE: This doesn't include the `issuerId` and `schemaIssuerId` properties that are present in the newer format. */ -type LegacyIndyProposeCredentialFormat = Omit +export type LegacyIndyProposeCredentialFormat = Omit export interface LegacyIndyCredentialRequest extends AnonCredsCredentialRequest { // prover_did is optional in AnonCreds credential request, but required in legacy format diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 93e2151870..af8ee049c8 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -30,13 +30,13 @@ import type { } from '@aries-framework/core' import { + ProblemReportError, MessageValidator, CredentialFormatSpec, AriesFrameworkError, Attachment, JsonEncoder, utils, - CredentialProblemReportError, CredentialProblemReportReason, JsonTransformer, } from '@aries-framework/core' @@ -205,7 +205,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const credOffer = attachment.getDataAsJson() if (!credOffer.schema_id || !credOffer.cred_def_id) { - throw new CredentialProblemReportError('Invalid credential offer', { + throw new ProblemReportError('Invalid credential offer', { problemCode: CredentialProblemReportReason.IssuanceAbandoned, }) } @@ -289,9 +289,8 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic // Assert credential attributes const credentialAttributes = credentialRecord.credentialAttributes if (!credentialAttributes) { - throw new CredentialProblemReportError( - `Missing required credential attribute values on credential record with id ${credentialRecord.id}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + throw new AriesFrameworkError( + `Missing required credential attribute values on credential record with id ${credentialRecord.id}` ) } @@ -315,6 +314,10 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic credentialRevocationId: credentialRevocationId, revocationRegistryId: credential.rev_reg_id, }) + credentialRecord.setTags({ + anonCredsRevocationRegistryId: credential.rev_reg_id, + anonCredsCredentialRevocationId: credentialRevocationId, + }) } const format = new CredentialFormatSpec({ @@ -344,9 +347,8 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) if (!credentialRequestMetadata) { - throw new CredentialProblemReportError( - `Missing required request metadata for credential with id ${credentialRecord.id}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } + throw new AriesFrameworkError( + `Missing required request metadata for credential exchange with thread id with id ${credentialRecord.id}` ) } @@ -415,7 +417,11 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { credentialRevocationId: credential.credentialRevocationId, - revocationRegistryId: anonCredsCredential.rev_reg_id, + revocationRegistryId: credential.revocationRegistryId, + }) + credentialRecord.setTags({ + anonCredsRevocationRegistryId: credential.revocationRegistryId, + anonCredsCredentialRevocationId: credential.credentialRevocationId, }) } diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts index c2dfc2cf0d..a586e77b10 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormat.ts @@ -7,6 +7,9 @@ import type { import type { AnonCredsProof, AnonCredsProofRequest, AnonCredsSelectedCredentials } from '../models' import type { ProofFormat } from '@aries-framework/core' +// TODO: Custom restrictions to remove `_id` from restrictions? +export type LegacyIndyProofRequest = AnonCredsProofRequest + export interface LegacyIndyProofFormat extends ProofFormat { formatKey: 'indy' @@ -30,9 +33,8 @@ export interface LegacyIndyProofFormat extends ProofFormat { } formatData: { - // TODO: Custom restrictions to remove `_id` from restrictions? - proposal: AnonCredsProofRequest - request: AnonCredsProofRequest + proposal: LegacyIndyProofRequest + request: LegacyIndyProofRequest presentation: AnonCredsProof } } diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts index b75df46b52..c2e5e2d1d5 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -38,7 +38,7 @@ import type { ProofFormatSelectCredentialsForRequestReturn, ProofFormatAutoRespondProposalOptions, ProofFormatAutoRespondRequestOptions, - IndyGetCredentialsForProofRequestOptions, + ProofFormatAutoRespondPresentationOptions, } from '@aries-framework/core' import { @@ -56,12 +56,12 @@ import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistry import { sortRequestedCredentialsMatches, createRequestFromPreview, - hasDuplicateGroupsNamesInProofRequest, areAnonCredsProofRequestsEqual, assertRevocationInterval, downloadTailsFile, checkValidCredentialValueEncoding, encodeCredentialValue, + assertNoDuplicateGroupsNamesInProofRequest, } from '../utils' const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' @@ -104,9 +104,7 @@ export class LegacyIndyProofFormatService implements ProofFormatService { + public async shouldAutoRespondToPresentation( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _agentContext: AgentContext, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: ProofFormatAutoRespondPresentationOptions + ): Promise { // The presentation is already verified in processPresentation, so we can just return true here. // It's only an ack, so it's just that we received the presentation. return true @@ -333,7 +332,7 @@ export class LegacyIndyProofFormatService implements ProofFormatService { const credentialsForProofRequest: AnonCredsCredentialsForProofRequest = { attributes: {}, diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 60359bb3ae..33a306617a 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -34,7 +34,7 @@ const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], }) -const agentConfig = getAgentConfig('LegacyIndyProofFormatServiceTest') +const agentConfig = getAgentConfig('LegacyIndyFormatServicesTest') const anonCredsRevocationService = new IndySdkRevocationService(indySdk) const anonCredsVerifierService = new IndySdkVerifierService(indySdk) const anonCredsHolderService = new IndySdkHolderService(anonCredsRevocationService, indySdk) diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index ced98385f2..11e113699c 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -3,8 +3,9 @@ export * from './services' export * from './error' export * from './repository' export * from './formats' +export * from './protocols' export { AnonCredsModule } from './AnonCredsModule' export { AnonCredsModuleConfig, AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' export { AnonCredsApi } from './AnonCredsApi' -export { AnonCredsCreateLinkSecretOptions } from './AnonCredsApiOptions' +export * from './AnonCredsApiOptions' diff --git a/packages/anoncreds/src/models/AnonCredsProofRequest.ts b/packages/anoncreds/src/models/AnonCredsProofRequest.ts index 2f57e32af3..3448b71570 100644 --- a/packages/anoncreds/src/models/AnonCredsProofRequest.ts +++ b/packages/anoncreds/src/models/AnonCredsProofRequest.ts @@ -1,7 +1,6 @@ import type { AnonCredsRequestedAttributeOptions } from './AnonCredsRequestedAttribute' import type { AnonCredsRequestedPredicateOptions } from './AnonCredsRequestedPredicate' -import { IndyRevocationInterval } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsIn, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' @@ -73,10 +72,10 @@ export class AnonCredsProofRequest { @Expose({ name: 'non_revoked' }) @ValidateNested() - @Type(() => IndyRevocationInterval) + @Type(() => AnonCredsRevocationInterval) @IsOptional() - @IsInstance(IndyRevocationInterval) - public nonRevoked?: IndyRevocationInterval + @IsInstance(AnonCredsRevocationInterval) + public nonRevoked?: AnonCredsRevocationInterval @IsIn(['1.0', '2.0']) @IsOptional() diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index 7ec87b9ec7..82c76119c2 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -88,6 +88,7 @@ export interface AnonCredsProof { requested_predicates: Record } + // TODO: extend types for proof property proof: any identifiers: Array<{ schema_id: string diff --git a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts similarity index 89% rename from packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts rename to packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts index 1c0320070f..12f464d3f3 100644 --- a/packages/core/src/modules/credentials/protocol/v1/V1CredentialProtocol.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts @@ -1,82 +1,75 @@ -import type { AgentContext } from '../../../../agent' -import type { AgentMessage } from '../../../../agent/AgentMessage' -import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' -import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import type { DependencyManager } from '../../../../plugins' -import type { ProblemReportMessage } from '../../../problem-reports' -import type { GetCredentialFormatDataReturn } from '../../CredentialsApiOptions' -import type { CredentialFormatService, ExtractCredentialFormats, IndyCredentialFormat } from '../../formats' -import type { CredentialProtocol } from '../CredentialProtocol' +import type { LegacyIndyCredentialFormatService } from '../../../formats' import type { - AcceptCredentialOptions, - AcceptCredentialOfferOptions, - AcceptCredentialProposalOptions, - AcceptCredentialRequestOptions, - CreateCredentialOfferOptions, - CreateCredentialProblemReportOptions, - CreateCredentialProposalOptions, - CredentialProtocolMsgReturnType, - NegotiateCredentialOfferOptions, - NegotiateCredentialProposalOptions, -} from '../CredentialProtocolOptions' - -import { Protocol } from '../../../../agent/models/features' -import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../error' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' -import { JsonTransformer } from '../../../../utils' -import { isLinkedAttachment } from '../../../../utils/attachment' -import { uuid } from '../../../../utils/uuid' -import { AckStatus } from '../../../common' -import { ConnectionService } from '../../../connections/services' -import { CredentialsModuleConfig } from '../../CredentialsModuleConfig' -import { CredentialProblemReportReason } from '../../errors' -import { IndyCredPropose } from '../../formats/indy/models' -import { AutoAcceptCredential } from '../../models/CredentialAutoAcceptType' -import { CredentialState } from '../../models/CredentialState' -import { CredentialExchangeRecord, CredentialRepository } from '../../repository' -import { composeAutoAccept } from '../../util/composeAutoAccept' -import { arePreviewAttributesEqual } from '../../util/previewAttributes' -import { BaseCredentialProtocol } from '../BaseCredentialProtocol' + AgentContext, + AgentMessage, + DependencyManager, + FeatureRegistry, + CredentialProtocolOptions, + InboundMessageContext, + ProblemReportMessage, + ExtractCredentialFormats, + CredentialProtocol, +} from '@aries-framework/core' + +import { + Protocol, + CredentialRepository, + AriesFrameworkError, + CredentialExchangeRecord, + CredentialState, + JsonTransformer, + ConnectionService, + Attachment, + AttachmentData, + AckStatus, + CredentialProblemReportReason, + CredentialsModuleConfig, + AutoAcceptCredential, + utils, + DidCommMessageRepository, + DidCommMessageRole, + BaseCredentialProtocol, + isLinkedAttachment, +} from '@aries-framework/core' + +import { AnonCredsCredentialProposal } from '../../../models/AnonCredsCredentialProposal' +import { composeCredentialAutoAccept, areCredentialPreviewAttributesEqual } from '../../../utils' import { - V1CredentialAckHandler, - V1CredentialProblemReportHandler, - V1IssueCredentialHandler, - V1OfferCredentialHandler, V1ProposeCredentialHandler, + V1OfferCredentialHandler, V1RequestCredentialHandler, + V1IssueCredentialHandler, + V1CredentialAckHandler, + V1CredentialProblemReportHandler, } from './handlers' import { - INDY_CREDENTIAL_ATTACHMENT_ID, + V1CredentialPreview, + V1ProposeCredentialMessage, + V1OfferCredentialMessage, INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, + V1RequestCredentialMessage, INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, + V1IssueCredentialMessage, + INDY_CREDENTIAL_ATTACHMENT_ID, V1CredentialAckMessage, V1CredentialProblemReportMessage, - V1IssueCredentialMessage, - V1OfferCredentialMessage, - V1ProposeCredentialMessage, - V1RequestCredentialMessage, } from './messages' -import { V1CredentialPreview } from './messages/V1CredentialPreview' - -type IndyCredentialFormatServiceLike = CredentialFormatService export interface V1CredentialProtocolConfig { - // indyCredentialFormat must be a service that implements the `IndyCredentialFormat` interface, however it doesn't - // have to be the IndyCredentialFormatService implementation per se. - indyCredentialFormat: IndyCredentialFormatServiceLike + indyCredentialFormat: LegacyIndyCredentialFormatService } export class V1CredentialProtocol - extends BaseCredentialProtocol<[IndyCredentialFormatServiceLike]> - implements CredentialProtocol<[IndyCredentialFormatServiceLike]> + extends BaseCredentialProtocol<[LegacyIndyCredentialFormatService]> + implements CredentialProtocol<[LegacyIndyCredentialFormatService]> { - private indyCredentialFormat: IndyCredentialFormatServiceLike + private indyCredentialFormat: LegacyIndyCredentialFormatService public constructor({ indyCredentialFormat }: V1CredentialProtocolConfig) { super() + // TODO: just create a new instance of LegacyIndyCredentialFormatService here so it makes the setup easier this.indyCredentialFormat = indyCredentialFormat } @@ -123,8 +116,8 @@ export class V1CredentialProtocol credentialFormats, comment, autoAcceptCredential, - }: CreateCredentialProposalOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.CreateCredentialProposalOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { this.assertOnlyIndyFormat(credentialFormats) const credentialRepository = agentContext.dependencyManager.resolve(CredentialRepository) @@ -141,7 +134,7 @@ export class V1CredentialProtocol // Create record const credentialRecord = new CredentialExchangeRecord({ connectionId: connectionRecord.id, - threadId: uuid(), + threadId: utils.uuid(), state: CredentialState.ProposalSent, linkedAttachments: linkedAttachments?.map((linkedAttachment) => linkedAttachment.attachment), autoAcceptCredential, @@ -155,7 +148,7 @@ export class V1CredentialProtocol }) // Transform the attachment into the attachment payload and use that to construct the v1 message - const indyCredentialProposal = JsonTransformer.fromJSON(attachment.getDataAsJson(), IndyCredPropose) + const indyCredentialProposal = JsonTransformer.fromJSON(attachment.getDataAsJson(), AnonCredsCredentialProposal) const credentialProposal = previewAttributes ? new V1CredentialPreview({ @@ -291,8 +284,8 @@ export class V1CredentialProtocol credentialFormats, comment, autoAcceptCredential, - }: AcceptCredentialProposalOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.AcceptCredentialProposalOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalReceived) @@ -364,8 +357,8 @@ export class V1CredentialProtocol credentialRecord, comment, autoAcceptCredential, - }: NegotiateCredentialProposalOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.NegotiateCredentialProposalOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.ProposalReceived) @@ -421,8 +414,8 @@ export class V1CredentialProtocol autoAcceptCredential, comment, connectionRecord, - }: CreateCredentialOfferOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.CreateCredentialOfferOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { // Assert this.assertOnlyIndyFormat(credentialFormats) @@ -436,7 +429,7 @@ export class V1CredentialProtocol // Create record const credentialRecord = new CredentialExchangeRecord({ connectionId: connectionRecord?.id, - threadId: uuid(), + threadId: utils.uuid(), linkedAttachments: credentialFormats.indy.linkedAttachments?.map( (linkedAttachments) => linkedAttachments.attachment ), @@ -587,8 +580,8 @@ export class V1CredentialProtocol credentialFormats, comment, autoAcceptCredential, - }: AcceptCredentialOfferOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.AcceptCredentialOfferOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { // Assert credential credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferReceived) @@ -654,8 +647,8 @@ export class V1CredentialProtocol credentialRecord, autoAcceptCredential, comment, - }: NegotiateCredentialOfferOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.NegotiateCredentialOfferOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.OfferReceived) @@ -683,7 +676,7 @@ export class V1CredentialProtocol }) // Transform the attachment into the attachment payload and use that to construct the v1 message - const indyCredentialProposal = JsonTransformer.fromJSON(attachment.getDataAsJson(), IndyCredPropose) + const indyCredentialProposal = JsonTransformer.fromJSON(attachment.getDataAsJson(), AnonCredsCredentialProposal) const credentialProposal = previewAttributes ? new V1CredentialPreview({ @@ -719,21 +712,12 @@ export class V1CredentialProtocol * Starting from a request is not supported in v1 of the issue credential protocol * because indy doesn't allow to start from a request */ - public async createRequest(): Promise> { + public async createRequest(): Promise< + CredentialProtocolOptions.CredentialProtocolMsgReturnType + > { throw new AriesFrameworkError('Starting from a request is not supported for v1 issue credential protocol') } - /** - * Process a received {@link IssueCredentialMessage}. This will not accept the credential - * or send a credential acknowledgement. It will only update the existing credential record with - * the information from the issue credential message. Use {@link createAck} - * after calling this method to create a credential acknowledgement. - * - * @param messageContext The message context containing an issue credential message - * - * @returns credential record associated with the issue credential message - * - */ public async processRequest( messageContext: InboundMessageContext ): Promise { @@ -796,7 +780,7 @@ export class V1CredentialProtocol } /** - * Create a {@link IssueCredentialMessage} as response to a received credential request. + * Create a {@link V1IssueCredentialMessage} as response to a received credential request. * * @returns Object containing issue credential message and associated credential record * @@ -808,8 +792,8 @@ export class V1CredentialProtocol credentialFormats, comment, autoAcceptCredential, - }: AcceptCredentialRequestOptions<[IndyCredentialFormatServiceLike]> - ): Promise> { + }: CredentialProtocolOptions.AcceptCredentialRequestOptions<[LegacyIndyCredentialFormatService]> + ): Promise> { // Assert credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.RequestReceived) @@ -865,7 +849,7 @@ export class V1CredentialProtocol } /** - * Process an incoming {@link IssueCredentialMessage} + * Process an incoming {@link V1IssueCredentialMessage} * * @param messageContext The message context containing a credential acknowledgement message * @returns credential record associated with the credential acknowledgement message @@ -943,8 +927,8 @@ export class V1CredentialProtocol */ public async acceptCredential( agentContext: AgentContext, - { credentialRecord }: AcceptCredentialOptions - ): Promise> { + { credentialRecord }: CredentialProtocolOptions.AcceptCredentialOptions + ): Promise> { credentialRecord.assertProtocolVersion('v1') credentialRecord.assertState(CredentialState.CredentialReceived) @@ -1017,8 +1001,8 @@ export class V1CredentialProtocol */ public async createProblemReport( agentContext: AgentContext, - { credentialRecord, description }: CreateCredentialProblemReportOptions - ): Promise> { + { credentialRecord, description }: CredentialProtocolOptions.CreateCredentialProblemReportOptions + ): Promise> { const message = new V1CredentialProblemReportMessage({ description: { en: description, @@ -1041,7 +1025,7 @@ export class V1CredentialProtocol const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) - const autoAccept = composeAutoAccept( + const autoAccept = composeCredentialAutoAccept( credentialRecord.autoAcceptCredential, credentialsModuleConfig.autoAcceptCredentials ) @@ -1063,7 +1047,7 @@ export class V1CredentialProtocol if (credentialOfferJson.cred_def_id !== proposalMessage.credentialDefinitionId) return false // Check if preview values match - return arePreviewAttributesEqual( + return areCredentialPreviewAttributesEqual( proposalMessage.credentialPreview.attributes, offerMessage.credentialPreview.attributes ) @@ -1080,7 +1064,7 @@ export class V1CredentialProtocol const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) - const autoAccept = composeAutoAccept( + const autoAccept = composeCredentialAutoAccept( credentialRecord.autoAcceptCredential, credentialsModuleConfig.autoAcceptCredentials ) @@ -1102,7 +1086,7 @@ export class V1CredentialProtocol if (credentialOfferJson.cred_def_id !== proposalMessage.credentialDefinitionId) return false // Check if preview values match - return arePreviewAttributesEqual( + return areCredentialPreviewAttributesEqual( proposalMessage.credentialPreview.attributes, offerMessage.credentialPreview.attributes ) @@ -1119,7 +1103,7 @@ export class V1CredentialProtocol const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) - const autoAccept = composeAutoAccept( + const autoAccept = composeCredentialAutoAccept( credentialRecord.autoAcceptCredential, credentialsModuleConfig.autoAcceptCredentials ) @@ -1154,7 +1138,7 @@ export class V1CredentialProtocol const credentialsModuleConfig = agentContext.dependencyManager.resolve(CredentialsModuleConfig) - const autoAccept = composeAutoAccept( + const autoAccept = composeCredentialAutoAccept( credentialRecord.autoAcceptCredential, credentialsModuleConfig.autoAcceptCredentials ) @@ -1221,7 +1205,11 @@ export class V1CredentialProtocol public async getFormatData( agentContext: AgentContext, credentialExchangeId: string - ): Promise>> { + ): Promise< + CredentialProtocolOptions.GetCredentialFormatDataReturn< + ExtractCredentialFormats<[LegacyIndyCredentialFormatService]> + > + > { // TODO: we could looking at fetching all record using a single query and then filtering based on the type of the message. const [proposalMessage, offerMessage, requestMessage, credentialMessage] = await Promise.all([ this.findProposalMessage(agentContext, credentialExchangeId), @@ -1265,7 +1253,7 @@ export class V1CredentialProtocol } private rfc0592ProposalFromV1ProposeMessage(proposalMessage: V1ProposeCredentialMessage) { - const indyCredentialProposal = new IndyCredPropose({ + const indyCredentialProposal = new AnonCredsCredentialProposal({ credentialDefinitionId: proposalMessage.credentialDefinitionId, schemaId: proposalMessage.schemaId, issuerDid: proposalMessage.issuerDid, diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts similarity index 84% rename from packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts rename to packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts index d563555bd5..eb255070cc 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -1,37 +1,38 @@ -import type { AgentContext } from '../../../../../agent' -import type { AgentConfig } from '../../../../../agent/AgentConfig' -import type { GetAgentMessageOptions } from '../../../../../storage/didcomm/DidCommMessageRepository' -import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { IndyCredentialViewMetadata } from '../../../formats/indy/models' -import type { CredentialPreviewAttribute } from '../../../models' -import type { CustomCredentialTags } from '../../../repository/CredentialExchangeRecord' +import type { + AgentContext, + CustomCredentialTags, + CredentialPreviewAttribute, + AgentConfig, + CredentialStateChangedEvent, +} from '@aries-framework/core' +import { + EventEmitter, + DidExchangeState, + Attachment, + AttachmentData, + JsonEncoder, + DidCommMessageRecord, + DidCommMessageRole, + AriesFrameworkError, + CredentialState, + CredentialExchangeRecord, + CredentialFormatSpec, + AutoAcceptCredential, + JsonTransformer, + InboundMessageContext, + CredentialEventTypes, + AckStatus, + CredentialProblemReportReason, +} from '@aries-framework/core' import { Subject } from 'rxjs' -import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' -import { EventEmitter } from '../../../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../../error' -import { DidCommMessageRecord, DidCommMessageRole } from '../../../../../storage' -import { DidCommMessageRepository } from '../../../../../storage/didcomm/DidCommMessageRepository' -import { JsonTransformer } from '../../../../../utils' -import { JsonEncoder } from '../../../../../utils/JsonEncoder' -import { uuid } from '../../../../../utils/uuid' -import { AckStatus } from '../../../../common' -import { DidExchangeState } from '../../../../connections' -import { ConnectionService } from '../../../../connections/services/ConnectionService' -import { CredentialEventTypes } from '../../../CredentialEvents' -import { credDef, credReq } from '../../../__tests__/fixtures' -import { CredentialProblemReportReason } from '../../../errors/CredentialProblemReportReason' -import { IndyCredentialFormatService } from '../../../formats/indy/IndyCredentialFormatService' -import { IndyCredentialUtils } from '../../../formats/indy/IndyCredentialUtils' -import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' -import { CredentialFormatSpec } from '../../../models/CredentialFormatSpec' -import { CredentialState } from '../../../models/CredentialState' -import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { CredentialMetadataKeys } from '../../../repository/CredentialMetadataTypes' -import { CredentialRepository } from '../../../repository/CredentialRepository' +import { ConnectionService } from '../../../../../../core/src/modules/connections/services/ConnectionService' +import { CredentialRepository } from '../../../../../../core/src/modules/credentials/repository/CredentialRepository' +import { DidCommMessageRepository } from '../../../../../../core/src/storage/didcomm/DidCommMessageRepository' +import { getMockConnection, getAgentConfig, getAgentContext, mockFunction } from '../../../../../../core/tests/helpers' +import { LegacyIndyCredentialFormatService } from '../../../../formats/LegacyIndyCredentialFormatService' +import { convertAttributesToCredentialValues } from '../../../../utils/credential' import { V1CredentialProtocol } from '../V1CredentialProtocol' import { INDY_CREDENTIAL_ATTACHMENT_ID, @@ -47,25 +48,26 @@ import { } from '../messages' // Mock classes -jest.mock('../../../repository/CredentialRepository') -jest.mock('../../../formats/indy/IndyCredentialFormatService') -jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') -jest.mock('../../../../connections/services/ConnectionService') +jest.mock('../../../../../../core/src/modules/credentials/repository/CredentialRepository') +jest.mock('../../../../formats/LegacyIndyCredentialFormatService') +jest.mock('../../../../../../core/src/storage/didcomm/DidCommMessageRepository') +jest.mock('../../../../../../core/src/modules/connections/services/ConnectionService') // Mock typed object const CredentialRepositoryMock = CredentialRepository as jest.Mock -const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock +const LegacyIndyCredentialFormatServiceMock = + LegacyIndyCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const indyCredentialFormatService = new IndyCredentialFormatServiceMock() +const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatServiceMock() const connectionService = new ConnectionServiceMock() // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -indyCredentialFormatService.credentialRecordType = 'indy' +legacyIndyCredentialFormatService.credentialRecordType = 'anoncreds' const connection = getMockConnection({ id: '123', @@ -90,7 +92,7 @@ const requestAttachment = new Attachment({ id: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, mimeType: 'application/json', data: new AttachmentData({ - base64: JsonEncoder.toBase64(credReq), + base64: JsonEncoder.toBase64({}), }), }) @@ -99,14 +101,14 @@ const credentialAttachment = new Attachment({ mimeType: 'application/json', data: new AttachmentData({ base64: JsonEncoder.toBase64({ - values: IndyCredentialUtils.convertAttributesToValues(credentialPreview.attributes), + values: convertAttributesToCredentialValues(credentialPreview.attributes), }), }), }) const credentialProposalMessage = new V1ProposeCredentialMessage({ comment: 'comment', - credentialDefinitionId: credDef.id, + credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', }) const credentialRequestMessage = new V1RequestCredentialMessage({ comment: 'abcd', @@ -129,7 +131,7 @@ const didCommMessageRecord = new DidCommMessageRecord({ }) // eslint-disable-next-line @typescript-eslint/no-explicit-any -const getAgentMessageMock = async (agentContext: AgentContext, options: GetAgentMessageOptions) => { +const getAgentMessageMock = async (agentContext: AgentContext, options: { messageClass: any }) => { if (options.messageClass === V1ProposeCredentialMessage) { return credentialProposalMessage } @@ -150,35 +152,29 @@ const getAgentMessageMock = async (agentContext: AgentContext, options: GetAgent // object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. const mockCredentialRecord = ({ state, - metadata, threadId, connectionId, tags, id, credentialAttributes, - indyRevocationRegistryId, - indyCredentialRevocationId, }: { state?: CredentialState - metadata?: IndyCredentialViewMetadata & { indyRequest: Record } tags?: CustomCredentialTags threadId?: string connectionId?: string credentialId?: string id?: string credentialAttributes?: CredentialPreviewAttribute[] - indyRevocationRegistryId?: string - indyCredentialRevocationId?: string } = {}) => { const credentialRecord = new CredentialExchangeRecord({ id, credentialAttributes: credentialAttributes || credentialPreview.attributes, state: state || CredentialState.OfferSent, - threadId: threadId ?? uuid(), + threadId: threadId ?? '809dd7ec-f0e7-4b97-9231-7a3615af6139', connectionId: connectionId ?? '123', credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: '123456', }, ], @@ -186,29 +182,6 @@ const mockCredentialRecord = ({ protocolVersion: 'v1', }) - if (metadata?.indyRequest) { - credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, { ...metadata.indyRequest }) - } - - if (metadata?.schemaId) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - schemaId: metadata.schemaId, - }) - } - - if (metadata?.credentialDefinitionId) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - credentialDefinitionId: metadata.credentialDefinitionId, - }) - } - - if (indyCredentialRevocationId || indyRevocationRegistryId) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - indyCredentialRevocationId, - indyRevocationRegistryId, - }) - } - return credentialRecord } @@ -243,7 +216,7 @@ describe('V1CredentialProtocol', () => { didCommMessageRecord, ]) - credentialProtocol = new V1CredentialProtocol({ indyCredentialFormat: indyCredentialFormatService }) + credentialProtocol = new V1CredentialProtocol({ indyCredentialFormat: legacyIndyCredentialFormatService }) }) afterEach(() => { @@ -259,14 +232,8 @@ describe('V1CredentialProtocol', () => { connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - const credentialFormats = { - indy: { - holderDid: 'did:sov:123456789abcdefghi', - }, - } - // mock resolved format call - mockFunction(indyCredentialFormatService.acceptOffer).mockResolvedValue({ + mockFunction(legacyIndyCredentialFormatService.acceptOffer).mockResolvedValue({ attachment: requestAttachment, format: new CredentialFormatSpec({ format: 'indy', @@ -279,7 +246,6 @@ describe('V1CredentialProtocol', () => { comment: 'hello', autoAcceptCredential: AutoAcceptCredential.Never, credentialRecord, - credentialFormats, }) // then @@ -298,15 +264,10 @@ describe('V1CredentialProtocol', () => { 'requests~attach': [JsonTransformer.toJSON(requestAttachment)], }) expect(credentialRepository.update).toHaveBeenCalledTimes(1) - expect(indyCredentialFormatService.acceptOffer).toHaveBeenCalledWith(agentContext, { + expect(legacyIndyCredentialFormatService.acceptOffer).toHaveBeenCalledWith(agentContext, { credentialRecord, attachmentId: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, offerAttachment, - credentialFormats: { - indy: { - holderDid: 'did:sov:123456789abcdefghi', - }, - }, }) expect(didCommMessageRepository.saveOrUpdateAgentMessage).toHaveBeenCalledWith(agentContext, { agentMessage: message, @@ -323,7 +284,7 @@ describe('V1CredentialProtocol', () => { const updateStateSpy = jest.spyOn(credentialProtocol, 'updateState') // mock resolved format call - mockFunction(indyCredentialFormatService.acceptOffer).mockResolvedValue({ + mockFunction(legacyIndyCredentialFormatService.acceptOffer).mockResolvedValue({ attachment: requestAttachment, format: new CredentialFormatSpec({ format: 'indy', @@ -429,7 +390,7 @@ describe('V1CredentialProtocol', () => { connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ + mockFunction(legacyIndyCredentialFormatService.acceptRequest).mockResolvedValue({ attachment: credentialAttachment, format: new CredentialFormatSpec({ format: 'the-format', @@ -459,7 +420,7 @@ describe('V1CredentialProtocol', () => { mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) - mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ + mockFunction(legacyIndyCredentialFormatService.acceptRequest).mockResolvedValue({ attachment: credentialAttachment, format: new CredentialFormatSpec({ format: 'the-format', @@ -499,7 +460,7 @@ describe('V1CredentialProtocol', () => { mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) const comment = 'credential response comment' - mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ + mockFunction(legacyIndyCredentialFormatService.acceptRequest).mockResolvedValue({ attachment: credentialAttachment, format: new CredentialFormatSpec({ format: 'the-format', @@ -522,7 +483,7 @@ describe('V1CredentialProtocol', () => { '~please_ack': expect.any(Object), }) - expect(indyCredentialFormatService.acceptRequest).toHaveBeenCalledWith(agentContext, { + expect(legacyIndyCredentialFormatService.acceptRequest).toHaveBeenCalledWith(agentContext, { credentialRecord, requestAttachment, offerAttachment, @@ -561,7 +522,7 @@ describe('V1CredentialProtocol', () => { associatedRecordId: credentialRecord.id, }) - expect(indyCredentialFormatService.processCredential).toHaveBeenNthCalledWith(1, agentContext, { + expect(legacyIndyCredentialFormatService.processCredential).toHaveBeenNthCalledWith(1, agentContext, { attachment: credentialAttachment, credentialRecord, requestAttachment: expect.any(Attachment), @@ -840,7 +801,7 @@ describe('V1CredentialProtocol', () => { }) it('should call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is true', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + const deleteCredentialMock = mockFunction(legacyIndyCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) @@ -858,7 +819,7 @@ describe('V1CredentialProtocol', () => { }) it('should not call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is false', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + const deleteCredentialMock = mockFunction(legacyIndyCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) @@ -872,7 +833,7 @@ describe('V1CredentialProtocol', () => { }) it('deleteAssociatedCredentials should default to true', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + const deleteCredentialMock = mockFunction(legacyIndyCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) @@ -886,7 +847,7 @@ describe('V1CredentialProtocol', () => { ) }) it('deleteAssociatedDidCommMessages should default to true', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + const deleteCredentialMock = mockFunction(legacyIndyCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts similarity index 79% rename from packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts rename to packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts index d1c27861b2..f4a72ce08c 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts @@ -1,55 +1,44 @@ -import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { CreateCredentialOfferOptions, CreateCredentialProposalOptions } from '../../CredentialProtocolOptions' - +import type { CredentialProtocolOptions, CredentialStateChangedEvent } from '@aries-framework/core' + +import { + EventEmitter, + DidExchangeState, + Attachment, + AttachmentData, + CredentialState, + CredentialFormatSpec, + CredentialExchangeRecord, + CredentialEventTypes, + JsonTransformer, + InboundMessageContext, +} from '@aries-framework/core' import { Subject } from 'rxjs' -import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' -import { Dispatcher } from '../../../../../agent/Dispatcher' -import { EventEmitter } from '../../../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' -import { DidCommMessageRepository } from '../../../../../storage' -import { JsonTransformer } from '../../../../../utils' -import { DidExchangeState } from '../../../../connections' -import { ConnectionService } from '../../../../connections/services/ConnectionService' -import { IndyLedgerService } from '../../../../ledger/services' -import { RoutingService } from '../../../../routing/services/RoutingService' -import { CredentialEventTypes } from '../../../CredentialEvents' -import { schema, credDef } from '../../../__tests__/fixtures' -import { IndyCredentialFormatService } from '../../../formats' -import { CredentialFormatSpec } from '../../../models' -import { CredentialState } from '../../../models/CredentialState' -import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { CredentialRepository } from '../../../repository/CredentialRepository' +import { ConnectionService } from '../../../../../../core/src/modules/connections/services/ConnectionService' +import { CredentialRepository } from '../../../../../../core/src/modules/credentials/repository/CredentialRepository' +import { DidCommMessageRepository } from '../../../../../../core/src/storage/didcomm/DidCommMessageRepository' +import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../core/tests/helpers' +import { LegacyIndyCredentialFormatService } from '../../../../formats/LegacyIndyCredentialFormatService' import { V1CredentialProtocol } from '../V1CredentialProtocol' -import { INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, V1OfferCredentialMessage } from '../messages' -import { V1CredentialPreview } from '../messages/V1CredentialPreview' +import { V1CredentialPreview, INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, V1OfferCredentialMessage } from '../messages' // Mock classes -jest.mock('../../../repository/CredentialRepository') -jest.mock('../../../../ledger/services/IndyLedgerService') -jest.mock('../../../formats/indy/IndyCredentialFormatService') -jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') -jest.mock('../../../../routing/services/RoutingService') -jest.mock('../../../../connections/services/ConnectionService') -jest.mock('../../../../../agent/Dispatcher') +jest.mock('../../../../../../core/src/modules/credentials/repository/CredentialRepository') +jest.mock('../../../../formats/LegacyIndyCredentialFormatService') +jest.mock('../../../../../../core/src/storage/didcomm/DidCommMessageRepository') +jest.mock('../../../../../../core/src/modules/connections/services/ConnectionService') // Mock typed object const CredentialRepositoryMock = CredentialRepository as jest.Mock -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock -const DispatcherMock = Dispatcher as jest.Mock +const LegacyIndyCredentialFormatServiceMock = + LegacyIndyCredentialFormatService as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const routingService = new RoutingServiceMock() -const indyLedgerService = new IndyLedgerServiceMock() -const indyCredentialFormatService = new IndyCredentialFormatServiceMock() -const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() +const indyCredentialFormatService = new LegacyIndyCredentialFormatServiceMock() const agentConfig = getAgentConfig('V1CredentialProtocolProposeOfferTest') const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) @@ -58,8 +47,6 @@ const agentContext = getAgentContext({ registerInstances: [ [CredentialRepository, credentialRepository], [DidCommMessageRepository, didCommMessageRepository], - [RoutingService, routingService], - [Dispatcher, dispatcher], [ConnectionService, connectionService], [EventEmitter, eventEmitter], ], @@ -68,7 +55,7 @@ const agentContext = getAgentContext({ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -indyCredentialFormatService.credentialRecordType = 'indy' +indyCredentialFormatService.credentialRecordType = 'anoncreds' const connectionRecord = getMockConnection({ id: '123', @@ -108,8 +95,6 @@ describe('V1CredentialProtocolProposeOffer', () => { beforeEach(async () => { // mock function implementations mockFunction(connectionService.getById).mockResolvedValue(connectionRecord) - mockFunction(indyLedgerService.getCredentialDefinition).mockResolvedValue(credDef) - mockFunction(indyLedgerService.getSchema).mockResolvedValue(schema) credentialProtocol = new V1CredentialProtocol({ indyCredentialFormat: indyCredentialFormatService, @@ -121,7 +106,9 @@ describe('V1CredentialProtocolProposeOffer', () => { }) describe('createProposal', () => { - const proposeOptions: CreateCredentialProposalOptions<[IndyCredentialFormatService]> = { + const proposeOptions: CredentialProtocolOptions.CreateCredentialProposalOptions< + [LegacyIndyCredentialFormatService] + > = { connectionRecord: connectionRecord, credentialFormats: { indy: { @@ -234,7 +221,7 @@ describe('V1CredentialProtocolProposeOffer', () => { }) describe('createOffer', () => { - const offerOptions: CreateCredentialOfferOptions<[IndyCredentialFormatService]> = { + const offerOptions: CredentialProtocolOptions.CreateCredentialOfferOptions<[LegacyIndyCredentialFormatService]> = { comment: 'some comment', connectionRecord, credentialFormats: { diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts similarity index 66% rename from packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts rename to packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts index a3fff6612e..975db00a6e 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-connectionless-credentials.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts @@ -1,27 +1,12 @@ -import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { AcceptCredentialOfferOptions, AcceptCredentialRequestOptions } from '../../../CredentialsApiOptions' - -import { ReplaySubject, Subject } from 'rxjs' - -import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' -import { prepareForIssuance, waitForCredentialRecordSubject, getAgentOptions } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { Agent } from '../../../../../agent/Agent' -import { CredentialEventTypes } from '../../../CredentialEvents' -import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' -import { CredentialState } from '../../../models/CredentialState' -import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { V1CredentialPreview } from '../messages/V1CredentialPreview' - -const faberAgentOptions = getAgentOptions('Faber connection-less Credentials V1', { - endpoints: ['rxjs:faber'], -}) +import type { EventReplaySubject } from '../../../../../../core/tests' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' +import type { AcceptCredentialOfferOptions, AcceptCredentialRequestOptions } from '@aries-framework/core' -const aliceAgentOptions = getAgentOptions('Alice connection-less Credentials V1', { - endpoints: ['rxjs:alice'], -}) +import { AutoAcceptCredential, CredentialExchangeRecord, CredentialState } from '@aries-framework/core' + +import { waitForCredentialRecordSubject, testLogger } from '../../../../../../core/tests' +import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' +import { V1CredentialPreview } from '../messages' const credentialPreview = V1CredentialPreview.fromRecord({ name: 'John', @@ -29,42 +14,27 @@ const credentialPreview = V1CredentialPreview.fromRecord({ }) describe('V1 Connectionless Credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent - let faberReplay: ReplaySubject - let aliceReplay: ReplaySubject + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceReplay: EventReplaySubject let credentialDefinitionId: string + let schemaId: string beforeEach(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await aliceAgent.initialize() - - const { definition } = await prepareForIssuance(faberAgent, ['name', 'age']) - credentialDefinitionId = definition.id - - faberReplay = new ReplaySubject() - aliceReplay = new ReplaySubject() - - faberAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(faberReplay) - aliceAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(aliceReplay) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + schemaId, + } = await setupAnonCredsTests({ + issuerName: 'Faber connection-less Credentials V1', + holderName: 'Alice connection-less Credentials V1', + attributeNames: ['name', 'age'], + createConnections: false, + })) }) afterEach(async () => { @@ -144,14 +114,15 @@ describe('V1 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { + schemaId, credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], @@ -165,7 +136,8 @@ describe('V1 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { + schemaId, credentialDefinitionId, }, }, @@ -225,14 +197,15 @@ describe('V1 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { + schemaId, credentialDefinitionId: credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts similarity index 58% rename from packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts rename to packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts index 42da4ed4da..6c0455755c 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts @@ -1,44 +1,52 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { AcceptCredentialOfferOptions, AcceptCredentialProposalOptions } from '../../../CredentialsApiOptions' -import type { Schema } from 'indy-sdk' - -import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' -import { CredentialState } from '../../../models/CredentialState' -import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -import { V1CredentialPreview } from '../messages/V1CredentialPreview' - -describe('credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let schema: Schema - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'some x-ray', - profile_picture: 'profile picture', - }) - const newCredentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', - 'x-ray': 'another x-ray value', - profile_picture: 'another profile picture', - }) +import type { EventReplaySubject } from '../../../../../../core/tests' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' + +import { AutoAcceptCredential, CredentialState, CredentialExchangeRecord, JsonTransformer } from '@aries-framework/core' + +import { waitForCredentialRecord, waitForCredentialRecordSubject, testLogger } from '../../../../../../core/tests' +import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' +import { V1CredentialPreview } from '../messages' + +const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', +}) +const newCredentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'another x-ray value', + profile_picture: 'another profile picture', +}) - describe('Auto accept on `always`', () => { +describe('V1 Credentials Auto Accept', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let credentialDefinitionId: string + let schemaId: string + let faberConnectionId: string + let aliceConnectionId: string + + describe("Auto accept on 'always'", () => { beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: always v1', - 'alice agent: always v1', - AutoAcceptCredential.Always - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + schemaId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Credentials Auto Accept V1', + holderName: 'Alice Credentials Auto Accept V1', + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + autoAcceptCredentials: AutoAcceptCredential.Always, + })) }) afterAll(async () => { @@ -48,16 +56,16 @@ describe('credentials', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with V1 credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { + test("Alice starts with V1 credential proposal to Faber, both with autoAcceptCredential on 'always'", async () => { testLogger.test('Alice sends credential proposal to Faber') const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v1', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, comment: 'v1 propose credential test', @@ -81,9 +89,9 @@ describe('credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { - schemaId: schema.id, - credentialDefinitionId: credDefId, + '_anonCreds/anonCredsCredential': { + schemaId: schemaId, + credentialDefinitionId: credentialDefinitionId, }, }, }, @@ -91,27 +99,26 @@ describe('credentials', () => { }) }) - test('Faber starts with V1 credential offer to Alice, both with autoAcceptCredential on `always`', async () => { + test("Faber starts with V1 credential offer to Alice, both with autoAcceptCredential on 'always'", async () => { testLogger.test('Faber sends credential offer to Alice') - const schemaId = schema.id const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v1', }) testLogger.test('Alice waits for credential from Faber') - const aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + const aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, }) testLogger.test('Faber waits for credential ack from Alice') - const faberCredentialRecord: CredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + const faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.Done, }) @@ -121,16 +128,16 @@ describe('credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { + '_anonCreds/anonCredsCredentialRequest': expect.any(Object), + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], @@ -145,13 +152,23 @@ describe('credentials', () => { }) }) - describe('Auto accept on `contentApproved`', () => { + describe("Auto accept on 'contentApproved'", () => { beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: contentApproved v1', - 'alice agent: contentApproved v1', - AutoAcceptCredential.ContentApproved - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + schemaId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'faber agent: contentApproved v1', + holderName: 'alice agent: contentApproved v1', + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + })) }) afterAll(async () => { @@ -164,51 +181,45 @@ describe('credentials', () => { // ============================== // TESTS v1 BEGIN // ========================== - test('Alice starts with V1 credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { + test("Alice starts with V1 credential proposal to Faber, both with autoAcceptCredential on 'contentApproved'", async () => { testLogger.test('Alice sends credential proposal to Faber') - const schemaId = schema.id - let faberCredentialExchangeRecord: CredentialExchangeRecord - let aliceCredentialExchangeRecord: CredentialExchangeRecord - - aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + let aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, protocolVersion: 'v1', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }) testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + let faberCredentialExchangeRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: aliceCredentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) - const options: AcceptCredentialProposalOptions = { + testLogger.test('Faber sends credential offer to Alice') + faberCredentialExchangeRecord = await faberAgent.credentials.acceptProposal({ credentialRecordId: faberCredentialExchangeRecord.id, comment: 'V1 Indy Offer', credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, }, - } - testLogger.test('Faber sends credential offer to Alice') - options.credentialRecordId = faberCredentialExchangeRecord.id - faberCredentialExchangeRecord = await faberAgent.credentials.acceptProposal(options) + }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { + aliceCredentialExchangeRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, }) testLogger.test('Faber waits for credential ack from Alice') - faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + faberCredentialExchangeRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.Done, }) @@ -219,16 +230,16 @@ describe('credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { + '_anonCreds/anonCredsCredentialRequest': expect.any(Object), + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], @@ -241,9 +252,9 @@ describe('credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }, @@ -251,26 +262,22 @@ describe('credentials', () => { }) }) - test('Faber starts with V1 credential offer to Alice, both with autoAcceptCredential on `contentApproved`', async () => { + test("Faber starts with V1 credential offer to Alice, both with autoAcceptCredential on 'contentApproved'", async () => { testLogger.test('Faber sends credential offer to Alice') - const schemaId = schema.id - let aliceCredentialExchangeRecord: CredentialExchangeRecord - let faberCredentialExchangeRecord: CredentialExchangeRecord - - faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ + let faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v1', }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialExchangeRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) @@ -284,78 +291,73 @@ describe('credentials', () => { expect(aliceCredentialExchangeRecord.getTags()).toEqual({ threadId: aliceCredentialExchangeRecord.threadId, state: aliceCredentialExchangeRecord.state, - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, credentialIds: [], }) - if (aliceCredentialExchangeRecord.connectionId) { - const acceptOfferOptions: AcceptCredentialOfferOptions = { - credentialRecordId: aliceCredentialExchangeRecord.id, - } - testLogger.test('alice sends credential request to faber') - faberCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer(acceptOfferOptions) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialExchangeRecord.threadId, - state: CredentialState.Done, - }) - - expect(aliceCredentialExchangeRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - metadata: { - data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { - schemaId, - credentialDefinitionId: credDefId, - }, + testLogger.test('alice sends credential request to faber') + faberCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialExchangeRecord.id, + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialExchangeRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialExchangeRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialExchangeRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + metadata: { + data: { + '_anonCreds/anonCredsCredentialRequest': expect.any(Object), + '_anonCreds/anonCredsCredential': { + schemaId, + credentialDefinitionId: credentialDefinitionId, }, }, - credentials: [ - { - credentialRecordType: 'indy', - credentialRecordId: expect.any(String), - }, - ], - state: CredentialState.CredentialReceived, - }) - - expect(faberCredentialExchangeRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - state: CredentialState.Done, - }) - } else { - throw new AriesFrameworkError('missing alice connection id') - } + }, + credentials: [ + { + credentialRecordType: 'anoncreds', + credentialRecordId: expect.any(String), + }, + ], + state: CredentialState.CredentialReceived, + }) + + expect(faberCredentialExchangeRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + state: CredentialState.Done, + }) }) - test('Faber starts with V1 credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + test("Faber starts with V1 credential offer to Alice, both have autoAcceptCredential on 'contentApproved' and attributes did change", async () => { testLogger.test('Faber sends credential offer to Alice') let faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v1', }) testLogger.test('Alice waits for credential offer from Faber') - let aliceCredentialExchangeRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialExchangeRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) @@ -365,7 +367,7 @@ describe('credentials', () => { expect(aliceCredentialExchangeRecord.getTags()).toEqual({ threadId: aliceCredentialExchangeRecord.threadId, state: aliceCredentialExchangeRecord.state, - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, credentialIds: [], }) @@ -375,7 +377,7 @@ describe('credentials', () => { credentialFormats: { indy: { attributes: newCredentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, comment: 'v1 propose credential test', @@ -395,22 +397,22 @@ describe('credentials', () => { aliceCredentialExchangeRecord.assertState(CredentialState.ProposalSent) }) - test('Alice starts with V1 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + test("Alice starts with V1 credential proposal to Faber, both have autoAcceptCredential on 'contentApproved' and attributes did change", async () => { testLogger.test('Alice sends credential proposal to Faber') const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v1', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, comment: 'v1 propose credential test', }) testLogger.test('Faber waits for credential proposal from Alice') - let faberCredentialExchangeRecord = await waitForCredentialRecord(faberAgent, { + let faberCredentialExchangeRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: aliceCredentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) @@ -419,7 +421,7 @@ describe('credentials', () => { credentialRecordId: faberCredentialExchangeRecord.id, credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: newCredentialPreview.attributes, }, }, @@ -427,7 +429,7 @@ describe('credentials', () => { testLogger.test('Alice waits for credential offer from Faber') - const record = await waitForCredentialRecord(aliceAgent, { + const record = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) @@ -437,7 +439,7 @@ describe('credentials', () => { expect(record.getTags()).toEqual({ threadId: record.threadId, state: record.state, - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, credentialIds: [], }) diff --git a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts similarity index 86% rename from packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts rename to packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts index 1d25498a3a..78a6f2f852 100644 --- a/packages/core/src/modules/credentials/protocol/v1/__tests__/v1-credentials.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials.e2e.test.ts @@ -1,12 +1,15 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' -import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage' -import { JsonTransformer } from '../../../../../utils' -import { CredentialState } from '../../../models/CredentialState' -import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { + CredentialExchangeRecord, + CredentialState, + DidCommMessageRepository, + JsonTransformer, +} from '@aries-framework/core' + +import { waitForCredentialRecord } from '../../../../../../core/tests/helpers' +import testLogger from '../../../../../../core/tests/logger' +import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' import { V1ProposeCredentialMessage, V1RequestCredentialMessage, @@ -15,19 +18,23 @@ import { V1CredentialPreview, } from '../messages' -describe('v1 credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let aliceConnection: ConnectionRecord - let aliceCredentialRecord: CredentialExchangeRecord - let faberCredentialRecord: CredentialExchangeRecord +describe('V1 Credentials', () => { + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let credentialDefinitionId: string + let aliceConnectionId: string beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, aliceConnection } = await setupCredentialTests( - 'Faber Agent Credentials v1', - 'Alice Agent Credentials v1' - )) + ;({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + credentialDefinitionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Agent Credentials V1', + holderName: 'Alice Agent Credentials V1', + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + })) }) afterAll(async () => { @@ -48,7 +55,7 @@ describe('v1 credentials', () => { testLogger.test('Alice sends (v1) credential proposal to Faber') const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v1', credentialFormats: { indy: { @@ -65,14 +72,14 @@ describe('v1 credentials', () => { }) expect(credentialExchangeRecord).toMatchObject({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v1', state: CredentialState.ProposalSent, threadId: expect.any(String), }) testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + let faberCredentialRecord = await waitForCredentialRecord(faberAgent, { threadId: credentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) @@ -83,14 +90,14 @@ describe('v1 credentials', () => { comment: 'V1 Indy Proposal', credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, }, }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) @@ -152,7 +159,7 @@ describe('v1 credentials', () => { }) expect(offerCredentialExchangeRecord).toMatchObject({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v1', state: CredentialState.RequestSent, threadId: expect.any(String), diff --git a/packages/anoncreds/src/protocols/credentials/v1/errors/V1CredentialProblemReportError.ts b/packages/anoncreds/src/protocols/credentials/v1/errors/V1CredentialProblemReportError.ts new file mode 100644 index 0000000000..113a8ac6f2 --- /dev/null +++ b/packages/anoncreds/src/protocols/credentials/v1/errors/V1CredentialProblemReportError.ts @@ -0,0 +1,23 @@ +import type { ProblemReportErrorOptions, CredentialProblemReportReason } from '@aries-framework/core' + +import { ProblemReportError } from '@aries-framework/core' + +import { V1CredentialProblemReportMessage } from '../messages' + +export interface V1CredentialProblemReportErrorOptions extends ProblemReportErrorOptions { + problemCode: CredentialProblemReportReason +} + +export class V1CredentialProblemReportError extends ProblemReportError { + public problemReport: V1CredentialProblemReportMessage + + public constructor(message: string, { problemCode }: V1CredentialProblemReportErrorOptions) { + super(message, { problemCode }) + this.problemReport = new V1CredentialProblemReportMessage({ + description: { + en: message, + code: problemCode, + }, + }) + } +} diff --git a/packages/anoncreds/src/protocols/credentials/v1/errors/index.ts b/packages/anoncreds/src/protocols/credentials/v1/errors/index.ts new file mode 100644 index 0000000000..5d2b6fc15e --- /dev/null +++ b/packages/anoncreds/src/protocols/credentials/v1/errors/index.ts @@ -0,0 +1 @@ +export { V1CredentialProblemReportError, V1CredentialProblemReportErrorOptions } from './V1CredentialProblemReportError' diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialAckHandler.ts similarity index 94% rename from packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialAckHandler.ts index e34a95d2bb..b4d3384ed9 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialAckHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialAckHandler.ts @@ -1,5 +1,5 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1CredentialProtocol } from '../V1CredentialProtocol' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { V1CredentialAckMessage } from '../messages' diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialProblemReportHandler.ts similarity index 94% rename from packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialProblemReportHandler.ts index 06769cf1bb..b2e599577d 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1CredentialProblemReportHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1CredentialProblemReportHandler.ts @@ -1,5 +1,5 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1CredentialProtocol } from '../V1CredentialProtocol' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { V1CredentialProblemReportMessage } from '../messages' diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts similarity index 88% rename from packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts index 82e7dea44a..e828fb2258 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1IssueCredentialHandler.ts @@ -1,9 +1,8 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialProtocol } from '../V1CredentialProtocol' +import type { MessageHandler, MessageHandlerInboundMessage, CredentialExchangeRecord } from '@aries-framework/core' + +import { DidCommMessageRepository, OutboundMessageContext } from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' -import { DidCommMessageRepository } from '../../../../../storage' import { V1IssueCredentialMessage, V1RequestCredentialMessage } from '../messages' export class V1IssueCredentialHandler implements MessageHandler { diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts similarity index 82% rename from packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts index 5e7731b72f..8d2d847e96 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1OfferCredentialHandler.ts @@ -1,11 +1,14 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialProtocol } from '../V1CredentialProtocol' +import type { MessageHandler, MessageHandlerInboundMessage, CredentialExchangeRecord } from '@aries-framework/core' + +import { + OutboundMessageContext, + RoutingService, + DidCommMessageRepository, + DidCommMessageRole, + ServiceDecorator, +} from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' -import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' -import { RoutingService } from '../../../../routing/services/RoutingService' import { V1OfferCredentialMessage } from '../messages' export class V1OfferCredentialHandler implements MessageHandler { @@ -54,11 +57,6 @@ export class V1OfferCredentialHandler implements MessageHandler { const { message } = await this.credentialProtocol.acceptOffer(messageContext.agentContext, { credentialRecord, - credentialFormats: { - indy: { - holderDid: ourService.recipientKeys[0], - }, - }, }) // Set and save ~service decorator to record (to remember our verkey) diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts similarity index 86% rename from packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts index 998d3940fc..d4fdbec98f 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1ProposeCredentialHandler.ts @@ -1,8 +1,8 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialProtocol } from '../V1CredentialProtocol' +import type { CredentialExchangeRecord, MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' + +import { OutboundMessageContext } from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' import { V1ProposeCredentialMessage } from '../messages' export class V1ProposeCredentialHandler implements MessageHandler { diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts similarity index 88% rename from packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts index 2b831566b2..00154fc8a4 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/V1RequestCredentialHandler.ts @@ -1,9 +1,8 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialProtocol } from '../V1CredentialProtocol' +import type { CredentialExchangeRecord, MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' + +import { DidCommMessageRepository, DidCommMessageRole, OutboundMessageContext } from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' import { V1RequestCredentialMessage } from '../messages' export class V1RequestCredentialHandler implements MessageHandler { diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/index.ts b/packages/anoncreds/src/protocols/credentials/v1/handlers/index.ts similarity index 75% rename from packages/core/src/modules/credentials/protocol/v1/handlers/index.ts rename to packages/anoncreds/src/protocols/credentials/v1/handlers/index.ts index dc0528f7c8..8566870084 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/index.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/handlers/index.ts @@ -4,4 +4,3 @@ export * from './V1OfferCredentialHandler' export * from './V1ProposeCredentialHandler' export * from './V1RequestCredentialHandler' export * from './V1CredentialProblemReportHandler' -export * from '../../revocation-notification/handlers/V1RevocationNotificationHandler' diff --git a/packages/core/src/modules/credentials/protocol/v1/index.ts b/packages/anoncreds/src/protocols/credentials/v1/index.ts similarity index 100% rename from packages/core/src/modules/credentials/protocol/v1/index.ts rename to packages/anoncreds/src/protocols/credentials/v1/index.ts diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts similarity index 75% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts index 857ea12bc0..db9bba955d 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialAckMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts @@ -1,7 +1,6 @@ -import type { AckMessageOptions } from '../../../../common' +import type { AckMessageOptions } from '@aries-framework/core' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { AckMessage } from '../../../../common' +import { AckMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' export type V1CredentialAckMessageOptions = AckMessageOptions diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialPreview.ts similarity index 80% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialPreview.ts index da44d37618..a5e1344bb8 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialPreview.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialPreview.ts @@ -1,11 +1,14 @@ -import type { CredentialPreviewOptions } from '../../../models/CredentialPreviewAttribute' +import type { CredentialPreviewOptions } from '@aries-framework/core' +import { + CredentialPreviewAttribute, + IsValidMessageType, + parseMessageType, + JsonTransformer, + replaceLegacyDidSovPrefix, +} from '@aries-framework/core' import { Expose, Transform, Type } from 'class-transformer' -import { IsInstance, ValidateNested } from 'class-validator' - -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../../../utils/messageType' -import { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttribute' +import { ValidateNested, IsInstance } from 'class-validator' /** * Credential preview inner message class. diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts similarity index 70% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts index 11accf67b4..7b3a3c4c06 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1CredentialProblemReportMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts @@ -1,7 +1,6 @@ -import type { ProblemReportMessageOptions } from '../../../../problem-reports/messages/ProblemReportMessage' +import type { ProblemReportMessageOptions } from '@aries-framework/core' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' +import { ProblemReportMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' export type V1CredentialProblemReportMessageOptions = ProblemReportMessageOptions diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts similarity index 75% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts index 7879222141..a8727a355a 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1IssueCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts @@ -1,11 +1,8 @@ -import type { Cred } from 'indy-sdk' +import type { AnonCredsCredential } from '../../../../models' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' -import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' - -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { IsString, IsOptional, IsArray, ValidateNested, IsInstance } from 'class-validator' export const INDY_CREDENTIAL_ATTACHMENT_ID = 'libindy-cred-0' @@ -45,11 +42,11 @@ export class V1IssueCredentialMessage extends AgentMessage { @IsInstance(Attachment, { each: true }) public credentialAttachments!: Attachment[] - public get indyCredential(): Cred | null { + public get indyCredential(): AnonCredsCredential | null { const attachment = this.credentialAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_ATTACHMENT_ID) // Extract credential from attachment - const credentialJson = attachment?.getDataAsJson() ?? null + const credentialJson = attachment?.getDataAsJson() ?? null return credentialJson } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts similarity index 80% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts index 51f19b24de..abb58f9aa1 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1OfferCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts @@ -1,11 +1,8 @@ -import type { CredOffer } from 'indy-sdk' +import type { AnonCredsCredentialOffer } from '../../../../models' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' -import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' - -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { IsString, IsOptional, ValidateNested, IsInstance, IsArray } from 'class-validator' import { V1CredentialPreview } from './V1CredentialPreview' @@ -60,11 +57,11 @@ export class V1OfferCredentialMessage extends AgentMessage { @IsInstance(Attachment, { each: true }) public offerAttachments!: Attachment[] - public get indyCredentialOffer(): CredOffer | null { + public get indyCredentialOffer(): AnonCredsCredentialOffer | null { const attachment = this.offerAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) // Extract credential offer from attachment - const credentialOfferJson = attachment?.getDataAsJson() ?? null + const credentialOfferJson = attachment?.getDataAsJson() ?? null return credentialOfferJson } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts similarity index 86% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts index 2772595d33..7c50693cad 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts @@ -1,11 +1,15 @@ -import type { Attachment } from '../../../../../decorators/attachment/Attachment' +import type { Attachment } from '@aries-framework/core' +import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { indyDidRegex, schemaIdRegex, schemaVersionRegex, credDefIdRegex } from '../../../../../utils' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { + legacyIndyCredentialDefinitionIdRegex, + legacyIndyDidRegex, + legacyIndySchemaIdRegex, + legacyIndySchemaVersionRegex, +} from '../../../../utils' import { V1CredentialPreview } from './V1CredentialPreview' @@ -73,7 +77,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_issuer_did' }) @IsString() @IsOptional() - @Matches(indyDidRegex) + @Matches(legacyIndyDidRegex) public schemaIssuerDid?: string /** @@ -82,7 +86,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_id' }) @IsString() @IsOptional() - @Matches(schemaIdRegex) + @Matches(legacyIndySchemaIdRegex) public schemaId?: string /** @@ -99,7 +103,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_version' }) @IsString() @IsOptional() - @Matches(schemaVersionRegex, { + @Matches(legacyIndySchemaVersionRegex, { message: 'Version must be X.X or X.X.X', }) public schemaVersion?: string @@ -110,7 +114,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'cred_def_id' }) @IsString() @IsOptional() - @Matches(credDefIdRegex) + @Matches(legacyIndyCredentialDefinitionIdRegex) public credentialDefinitionId?: string /** @@ -119,6 +123,6 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'issuer_did' }) @IsString() @IsOptional() - @Matches(indyDidRegex) + @Matches(legacyIndyDidRegex) public issuerDid?: string } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts similarity index 80% rename from packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts index e06498a3f1..2814e6ebbf 100644 --- a/packages/core/src/modules/credentials/protocol/v1/messages/V1RequestCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts @@ -1,12 +1,9 @@ -import type { CredReq } from 'indy-sdk' +import type { LegacyIndyCredentialRequest } from '../../../../formats' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' - export const INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID = 'libindy-cred-request-0' export interface V1RequestCredentialMessageOptions { @@ -45,12 +42,12 @@ export class V1RequestCredentialMessage extends AgentMessage { @IsInstance(Attachment, { each: true }) public requestAttachments!: Attachment[] - public get indyCredentialRequest(): CredReq | null { + public get indyCredentialRequest(): LegacyIndyCredentialRequest | null { const attachment = this.requestAttachments.find( (attachment) => attachment.id === INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID ) // Extract proof request from attachment - const credentialReqJson = attachment?.getDataAsJson() ?? null + const credentialReqJson = attachment?.getDataAsJson() ?? null return credentialReqJson } diff --git a/packages/core/src/modules/credentials/protocol/v1/messages/index.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/index.ts similarity index 100% rename from packages/core/src/modules/credentials/protocol/v1/messages/index.ts rename to packages/anoncreds/src/protocols/credentials/v1/messages/index.ts diff --git a/packages/anoncreds/src/protocols/index.ts b/packages/anoncreds/src/protocols/index.ts new file mode 100644 index 0000000000..d5a3d13f6c --- /dev/null +++ b/packages/anoncreds/src/protocols/index.ts @@ -0,0 +1,2 @@ +export * from './credentials/v1' +export * from './proofs/v1' diff --git a/packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts similarity index 88% rename from packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts rename to packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts index 5461aa8ebc..5e27debbfb 100644 --- a/packages/core/src/modules/proofs/protocol/v1/V1ProofProtocol.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts @@ -1,49 +1,39 @@ -import type { AgentContext } from '../../../../agent' -import type { AgentMessage } from '../../../../agent/AgentMessage' -import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' -import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import type { DependencyManager } from '../../../../plugins' -import type { ProblemReportMessage } from '../../../problem-reports' -import type { ProofFormatService } from '../../formats' -import type { ProofFormat } from '../../formats/ProofFormat' -import type { IndyProofFormat } from '../../formats/indy/IndyProofFormat' -import type { ProofProtocol } from '../ProofProtocol' +import type { LegacyIndyProofFormatService } from '../../../formats' import type { - AcceptPresentationOptions, - AcceptProofProposalOptions, - AcceptProofRequestOptions, - CreateProofProblemReportOptions, - CreateProofProposalOptions, - CreateProofRequestOptions, - GetCredentialsForRequestOptions, - GetCredentialsForRequestReturn, + ProofProtocol, + DependencyManager, + FeatureRegistry, + AgentContext, + ProofProtocolOptions, + InboundMessageContext, + AgentMessage, + ProblemReportMessage, GetProofFormatDataReturn, - NegotiateProofProposalOptions, - NegotiateProofRequestOptions, - ProofProtocolMsgReturnType, - SelectCredentialsForRequestOptions, - SelectCredentialsForRequestReturn, -} from '../ProofProtocolOptions' - -import { Protocol } from '../../../../agent/models' -import { Attachment } from '../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' -import { JsonEncoder } from '../../../../utils' -import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { MessageValidator } from '../../../../utils/MessageValidator' -import { uuid } from '../../../../utils/uuid' -import { AckStatus } from '../../../common/messages/AckMessage' -import { ConnectionService } from '../../../connections' -import { ProofsModuleConfig } from '../../ProofsModuleConfig' -import { PresentationProblemReportReason } from '../../errors/PresentationProblemReportReason' -import { createRequestFromPreview } from '../../formats/indy/util' -import { AutoAcceptProof } from '../../models' -import { ProofState } from '../../models/ProofState' -import { ProofRepository } from '../../repository' -import { ProofExchangeRecord } from '../../repository/ProofExchangeRecord' -import { composeAutoAccept } from '../../utils/composeAutoAccept' -import { BaseProofProtocol } from '../BaseProofProtocol' + ProofFormat, +} from '@aries-framework/core' + +import { + BaseProofProtocol, + Protocol, + ProofRepository, + DidCommMessageRepository, + AriesFrameworkError, + MessageValidator, + ProofExchangeRecord, + ProofState, + DidCommMessageRole, + ConnectionService, + Attachment, + JsonTransformer, + PresentationProblemReportReason, + AckStatus, + ProofsModuleConfig, + AutoAcceptProof, + JsonEncoder, + utils, +} from '@aries-framework/core' + +import { composeProofAutoAccept, createRequestFromPreview } from '../../../utils' import { V1PresentationProblemReportError } from './errors' import { @@ -64,20 +54,17 @@ import { import { V1PresentationProblemReportMessage } from './messages/V1PresentationProblemReportMessage' import { V1PresentationPreview } from './models/V1PresentationPreview' -type IndyProofFormatServiceLike = ProofFormatService - export interface V1ProofProtocolConfig { - // indyCredentialFormat must be a service that implements the `IndyProofFormat` interface, however it doesn't - // have to be the IndyProofFormatService implementation per se. - indyProofFormat: ProofFormatService + indyProofFormat: LegacyIndyProofFormatService } -export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol<[IndyProofFormatServiceLike]> { - private indyProofFormat: ProofFormatService +export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol<[LegacyIndyProofFormatService]> { + private indyProofFormat: LegacyIndyProofFormatService public constructor({ indyProofFormat }: V1ProofProtocolConfig) { super() + // TODO: just create a new instance of LegacyIndyProofFormatService here so it makes the setup easier this.indyProofFormat = indyProofFormat } @@ -116,8 +103,8 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< comment, parentThreadId, autoAcceptProof, - }: CreateProofProposalOptions<[IndyProofFormatServiceLike]> - ): Promise> { + }: ProofProtocolOptions.CreateProofProposalOptions<[LegacyIndyProofFormatService]> + ): Promise> { this.assertOnlyIndyFormat(proofFormats) const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) @@ -243,8 +230,13 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async acceptProposal( agentContext: AgentContext, - { proofRecord, proofFormats, comment, autoAcceptProof }: AcceptProofProposalOptions<[IndyProofFormatServiceLike]> - ): Promise> { + { + proofRecord, + proofFormats, + comment, + autoAcceptProof, + }: ProofProtocolOptions.AcceptProofProposalOptions<[LegacyIndyProofFormatService]> + ): Promise> { // Assert proofRecord.assertProtocolVersion('v1') proofRecord.assertState(ProofState.ProposalReceived) @@ -307,8 +299,13 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async negotiateProposal( agentContext: AgentContext, - { proofFormats, proofRecord, comment, autoAcceptProof }: NegotiateProofProposalOptions<[IndyProofFormatServiceLike]> - ): Promise> { + { + proofFormats, + proofRecord, + comment, + autoAcceptProof, + }: ProofProtocolOptions.NegotiateProofProposalOptions<[LegacyIndyProofFormatService]> + ): Promise> { // Assert proofRecord.assertProtocolVersion('v1') proofRecord.assertState(ProofState.ProposalReceived) @@ -351,8 +348,8 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< comment, parentThreadId, autoAcceptProof, - }: CreateProofRequestOptions<[IndyProofFormatServiceLike]> - ): Promise> { + }: ProofProtocolOptions.CreateProofRequestOptions<[LegacyIndyProofFormatService]> + ): Promise> { this.assertOnlyIndyFormat(proofFormats) const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) @@ -365,7 +362,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< // Create record const proofRecord = new ProofExchangeRecord({ connectionId: connectionRecord?.id, - threadId: uuid(), + threadId: utils.uuid(), parentThreadId, state: ProofState.RequestSent, autoAcceptProof, @@ -490,8 +487,13 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async negotiateRequest( agentContext: AgentContext, - { proofFormats, proofRecord, comment, autoAcceptProof }: NegotiateProofRequestOptions<[IndyProofFormatServiceLike]> - ): Promise> { + { + proofFormats, + proofRecord, + comment, + autoAcceptProof, + }: ProofProtocolOptions.NegotiateProofRequestOptions<[LegacyIndyProofFormatService]> + ): Promise> { // Assert proofRecord.assertProtocolVersion('v1') proofRecord.assertState(ProofState.RequestReceived) @@ -538,8 +540,13 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async acceptRequest( agentContext: AgentContext, - { proofRecord, proofFormats, autoAcceptProof, comment }: AcceptProofRequestOptions<[IndyProofFormatServiceLike]> - ): Promise> { + { + proofRecord, + proofFormats, + autoAcceptProof, + comment, + }: ProofProtocolOptions.AcceptProofRequestOptions<[LegacyIndyProofFormatService]> + ): Promise> { // Assert proofRecord.assertProtocolVersion('v1') proofRecord.assertState(ProofState.RequestReceived) @@ -610,8 +617,8 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async getCredentialsForRequest( agentContext: AgentContext, - { proofRecord, proofFormats }: GetCredentialsForRequestOptions<[IndyProofFormatServiceLike]> - ): Promise> { + { proofRecord, proofFormats }: ProofProtocolOptions.GetCredentialsForRequestOptions<[LegacyIndyProofFormatService]> + ): Promise> { if (proofFormats) this.assertOnlyIndyFormat(proofFormats) const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -667,8 +674,11 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async selectCredentialsForRequest( agentContext: AgentContext, - { proofRecord, proofFormats }: SelectCredentialsForRequestOptions<[IndyProofFormatServiceLike]> - ): Promise> { + { + proofRecord, + proofFormats, + }: ProofProtocolOptions.SelectCredentialsForRequestOptions<[LegacyIndyProofFormatService]> + ): Promise> { if (proofFormats) this.assertOnlyIndyFormat(proofFormats) const didCommMessageRepository = agentContext.dependencyManager.resolve(DidCommMessageRepository) @@ -790,8 +800,8 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async acceptPresentation( agentContext: AgentContext, - { proofRecord }: AcceptPresentationOptions - ): Promise> { + { proofRecord }: ProofProtocolOptions.AcceptPresentationOptions + ): Promise> { agentContext.config.logger.debug(`Creating presentation ack for proof record with id ${proofRecord.id}`) // Assert @@ -855,8 +865,8 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< public async createProblemReport( agentContext: AgentContext, - { proofRecord, description }: CreateProofProblemReportOptions - ): Promise> { + { proofRecord, description }: ProofProtocolOptions.CreateProofProblemReportOptions + ): Promise> { const message = new V1PresentationProblemReportMessage({ description: { code: PresentationProblemReportReason.Abandoned, @@ -886,7 +896,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) - const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + const autoAccept = composeProofAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) // Handle always / never cases if (autoAccept === AutoAcceptProof.Always) return true @@ -929,7 +939,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) - const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + const autoAccept = composeProofAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) // Handle always / never cases if (autoAccept === AutoAcceptProof.Always) return true @@ -948,7 +958,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< version: '1.0', attributes: proposalMessage.presentationProposal.attributes, predicates: proposalMessage.presentationProposal.predicates, - }).toJSON() + }) return this.indyProofFormat.shouldAutoRespondToRequest(agentContext, { proofRecord, @@ -972,7 +982,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< const proofsModuleConfig = agentContext.dependencyManager.resolve(ProofsModuleConfig) - const autoAccept = composeAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) + const autoAccept = composeProofAutoAccept(proofRecord.autoAcceptProof, proofsModuleConfig.autoAcceptProofs) // Handle always / never cases if (autoAccept === AutoAcceptProof.Always) return true @@ -1082,7 +1092,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< return { proposal: proposalMessage ? { - indy: indyProposeProof?.toJSON(), + indy: indyProposeProof, } : undefined, request: requestMessage diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/V1ProofProtocol.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts similarity index 83% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/V1ProofProtocol.test.ts rename to packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts index 0b1febb680..d3c6a4f204 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/V1ProofProtocol.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts @@ -1,37 +1,38 @@ -import type { AgentContext } from '../../../../../agent' -import type { AgentConfig } from '../../../../../agent/AgentConfig' -import type { ProofStateChangedEvent } from '../../../ProofEvents' -import type { CustomProofTags } from '../../../repository/ProofExchangeRecord' +import type { CustomProofTags, AgentConfig, AgentContext, ProofStateChangedEvent } from '../../../../../../core/src' import { Subject } from 'rxjs' -import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' -import { EventEmitter } from '../../../../../agent/EventEmitter' -import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' -import { DidCommMessageRepository } from '../../../../../storage' -import { ConnectionService, DidExchangeState } from '../../../../connections' -import { ProofEventTypes } from '../../../ProofEvents' -import { PresentationProblemReportReason } from '../../../errors/PresentationProblemReportReason' -import { IndyProofFormatService } from '../../../formats/indy/IndyProofFormatService' -import { ProofState } from '../../../models/ProofState' -import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import { ProofRepository } from '../../../repository/ProofRepository' +import { + DidExchangeState, + Attachment, + AttachmentData, + ProofState, + ProofExchangeRecord, + InboundMessageContext, + ProofEventTypes, + PresentationProblemReportReason, + EventEmitter, +} from '../../../../../../core/src' +import { ConnectionService } from '../../../../../../core/src/modules/connections/services/ConnectionService' +import { ProofRepository } from '../../../../../../core/src/modules/proofs/repository/ProofRepository' +import { DidCommMessageRepository } from '../../../../../../core/src/storage/didcomm/DidCommMessageRepository' +import { getMockConnection, getAgentConfig, getAgentContext, mockFunction } from '../../../../../../core/tests' +import { LegacyIndyProofFormatService } from '../../../../formats/LegacyIndyProofFormatService' import { V1ProofProtocol } from '../V1ProofProtocol' import { INDY_PROOF_REQUEST_ATTACHMENT_ID, V1RequestPresentationMessage } from '../messages' import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' // Mock classes -jest.mock('../../../repository/ProofRepository') -jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') -jest.mock('../../../../connections/services/ConnectionService') -jest.mock('../../../../../storage/Repository') +jest.mock('../../../../../../core/src/modules/proofs/repository/ProofRepository') +jest.mock('../../../../formats/LegacyIndyProofFormatService') +jest.mock('../../../../../../core/src/storage/didcomm/DidCommMessageRepository') +jest.mock('../../../../../../core/src/modules/connections/services/ConnectionService') // Mock typed object const ProofRepositoryMock = ProofRepository as jest.Mock const connectionServiceMock = ConnectionService as jest.Mock const didCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const indyProofFormatServiceMock = IndyProofFormatService as jest.Mock +const indyProofFormatServiceMock = LegacyIndyProofFormatService as jest.Mock const proofRepository = new ProofRepositoryMock() const connectionService = new connectionServiceMock() diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts new file mode 100644 index 0000000000..df66a6217d --- /dev/null +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -0,0 +1,433 @@ +import type { SubjectMessage } from '../../../../../../../tests/transport/SubjectInboundTransport' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../../../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../../../../../tests/transport/SubjectOutboundTransport' +import { + CredentialEventTypes, + Agent, + AutoAcceptProof, + ProofState, + HandshakeProtocol, + MediatorPickupStrategy, + LinkedAttachment, + Attachment, + AttachmentData, + ProofEventTypes, +} from '../../../../../../core/src' +import { uuid } from '../../../../../../core/src/utils/uuid' +import { + testLogger, + waitForProofExchangeRecordSubject, + getAgentOptions, + makeConnection, + setupEventReplaySubjects, +} from '../../../../../../core/tests' +import { getIndySdkModules } from '../../../../../../indy-sdk/tests/setupIndySdkModule' +import { + getLegacyAnonCredsModules, + issueLegacyAnonCredsCredential, + prepareForAnonCredsIssuance, + setupAnonCredsTests, +} from '../../../../../tests/legacyAnonCredsSetup' +import { V1CredentialPreview } from '../../../credentials/v1' + +describe('V1 Proofs - Connectionless - Indy', () => { + let agents: Agent[] + + afterEach(async () => { + for (const agent of agents) { + await agent.shutdown() + await agent.wallet.delete() + } + }) + + test('Faber starts with connection-less proof requests to Alice', async () => { + const { + holderAgent: aliceAgent, + issuerAgent: faberAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber v1 connection-less Proofs - Never', + holderName: 'Alice v1 connection-less Proofs - Never', + autoAcceptProofs: AutoAcceptProof.Never, + attributeNames: ['name', 'age'], + }) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + issuerReplay: faberReplay, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'John', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) + + agents = [aliceAgent, faberAgent] + testLogger.test('Faber sends presentation request to Alice') + + // eslint-disable-next-line prefer-const + let { proofRecord: faberProofExchangeRecord, message } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofExchangeRecord.id, + message, + domain: 'https://a-domain.com', + }) + await aliceAgent.receiveMessage(requestMessage.toJSON()) + + testLogger.test('Alice waits for presentation request from Faber') + let aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.RequestReceived, + }) + + testLogger.test('Alice accepts presentation request from Faber') + const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ + proofRecordId: aliceProofExchangeRecord.id, + }) + + await aliceAgent.proofs.acceptRequest({ + proofRecordId: aliceProofExchangeRecord.id, + proofFormats: { indy: requestedCredentials.proofFormats.indy }, + }) + + testLogger.test('Faber waits for presentation from Alice') + faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + // assert presentation is valid + expect(faberProofExchangeRecord.isVerified).toBe(true) + + // Faber accepts presentation + await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) + + // Alice waits till it receives presentation ack + aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Done, + }) + }) + + test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { + const { + holderAgent: aliceAgent, + issuerAgent: faberAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber v1 connection-less Proofs - Always', + holderName: 'Alice v1 connection-less Proofs - Always', + autoAcceptProofs: AutoAcceptProof.Always, + attributeNames: ['name', 'age'], + }) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + issuerReplay: faberReplay, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'John', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) + + agents = [aliceAgent, faberAgent] + + const { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + autoAcceptProof: AutoAcceptProof.ContentApproved, + }) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofExchangeRecord.id, + message, + domain: 'https://a-domain.com', + }) + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + }) + + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + }) + }) + + test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and both agents having a mediator', async () => { + testLogger.test('Faber sends presentation request to Alice') + + const credentialPreview = V1CredentialPreview.fromRecord({ + name: 'John', + age: '99', + }) + + const unique = uuid().substring(0, 4) + + const mediatorAgentOptions = getAgentOptions( + `Connectionless proofs with mediator Mediator-${unique}`, + { + autoAcceptMediationRequests: true, + endpoints: ['rxjs:mediator'], + }, + getIndySdkModules() + ) + + const mediatorMessages = new Subject() + const subjectMap = { 'rxjs:mediator': mediatorMessages } + + // Initialize mediator + const mediatorAgent = new Agent(mediatorAgentOptions) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + await mediatorAgent.initialize() + + const faberMediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'faber invitation', + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + const aliceMediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ + label: 'alice invitation', + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + const faberAgentOptions = getAgentOptions( + `Connectionless proofs with mediator Faber-${unique}`, + { + mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, + }) + ) + + const aliceAgentOptions = getAgentOptions( + `Connectionless proofs with mediator Alice-${unique}`, + { + mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, + }) + ) + + const faberAgent = new Agent(faberAgentOptions) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await faberAgent.initialize() + + const aliceAgent = new Agent(aliceAgentOptions) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await aliceAgent.initialize() + + const [faberReplay, aliceReplay] = setupEventReplaySubjects( + [faberAgent, aliceAgent], + [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + ) + + agents = [aliceAgent, faberAgent, mediatorAgent] + + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { + attributeNames: ['name', 'age', 'image_0', 'image_1'], + issuerId: faberAgent.publicDid?.did as string, + }) + + const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) + expect(faberConnection.isReady).toBe(true) + expect(aliceConnection.isReady).toBe(true) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnection.id, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + offer: { + credentialDefinitionId: credentialDefinition.credentialDefinitionId, + attributes: credentialPreview.attributes, + linkedAttachments: [ + new LinkedAttachment({ + name: 'image_0', + attachment: new Attachment({ + filename: 'picture-of-a-cat.png', + data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + }), + }), + new LinkedAttachment({ + name: 'image_1', + attachment: new Attachment({ + filename: 'picture-of-a-dog.png', + data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + }), + }), + ], + }, + }) + + const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + }) + + const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + }) + + // eslint-disable-next-line prefer-const + let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinition.credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinition.credentialDefinitionId, + }, + ], + }, + }, + }, + }, + autoAcceptProof: AutoAcceptProof.ContentApproved, + }) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofExchangeRecord.id, + message, + domain: 'https://a-domain.com', + }) + + const mediationRecord = await faberAgent.mediationRecipient.findDefaultMediator() + if (!mediationRecord) { + throw new Error('Faber agent has no default mediator') + } + + expect(requestMessage).toMatchObject({ + service: { + recipientKeys: [expect.any(String)], + routingKeys: mediationRecord.routingKeys, + serviceEndpoint: mediationRecord.endpoint, + }, + }) + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + + await aliceProofExchangeRecordPromise + + await faberProofExchangeRecordPromise + + await aliceAgent.mediationRecipient.stopMessagePickup() + await faberAgent.mediationRecipient.stopMessagePickup() + }) +}) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-negotiation.e2e.test.ts similarity index 53% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts rename to packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-negotiation.e2e.test.ts index e7f7a6f5f1..917f5c805d 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-negotiation.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-negotiation.e2e.test.ts @@ -1,36 +1,29 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions, NegotiateProofProposalOptions } from '../../../ProofsApiOptions' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V1PresentationPreview } from '../models/V1PresentationPreview' -import type { CredDefId } from 'indy-sdk' +import type { AcceptProofProposalOptions } from '../../../../../../core/src' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' +import type { V1RequestPresentationMessage } from '../messages' -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage/didcomm' -import { AttributeFilter } from '../../../formats/indy/models/AttributeFilter' -import { PredicateType } from '../../../formats/indy/models/PredicateType' -import { ProofAttributeInfo } from '../../../formats/indy/models/ProofAttributeInfo' -import { ProofPredicateInfo } from '../../../formats/indy/models/ProofPredicateInfo' -import { ProofState } from '../../../models/ProofState' -import { V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' +import { ProofState } from '../../../../../../core/src' +import { testLogger, waitForProofExchangeRecord } from '../../../../../../core/tests' +import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: CredDefId - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' - )) + ;({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + credentialDefinitionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber - V1 Indy Proof Negotiation', + holderName: 'Alice - V1 Indy Proof Negotiation', + attributeNames: ['name', 'age'], + })) }) afterAll(async () => { @@ -48,48 +41,44 @@ describe('Present Proof', () => { state: ProofState.ProposalReceived, }) - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v1', proofFormats: { indy: { name: 'proof-request', version: '1.0', - attributes: presentationPreview.attributes.filter((attribute) => attribute.name !== 'name'), - predicates: presentationPreview.predicates, + attributes: [], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], }, }, comment: 'V1 propose proof test 1', }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - let proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1ProposePresentationMessage, - }) + let faberProofExchangeRecord = await faberProofExchangeRecordPromise + let proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', id: expect.any(String), comment: 'V1 propose proof test 1', presentationProposal: { type: 'https://didcomm.org/present-proof/1.0/presentation-preview', - attributes: [ - { - name: 'image_0', - credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, - }, - ], + attributes: [], predicates: [ { name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + credentialDefinitionId, predicate: '>=', - threshold: 50, + threshold: 18, }, ], }, @@ -101,70 +90,48 @@ describe('Present Proof', () => { protocolVersion: 'v1', }) - // Negotiate Proposal - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + }) - const requestProofAsResponseOptions: NegotiateProofProposalOptions = { + testLogger.test('Faber sends new proof request to Alice') + faberProofExchangeRecord = await faberAgent.proofs.negotiateProposal({ proofRecordId: faberProofExchangeRecord.id, proofFormats: { indy: { name: 'proof-request', - nonce: '58d223e5-fc4d-4448-b74c-5eb11c6b558f', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + something: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + somethingElse: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, - } - - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - threadId: faberProofExchangeRecord.threadId, - state: ProofState.RequestReceived, }) - testLogger.test('Faber sends new proof request to Alice') - faberProofExchangeRecord = await faberAgent.proofs.negotiateProposal(requestProofAsResponseOptions) - testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - let request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + let request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), @@ -200,8 +167,15 @@ describe('Present Proof', () => { indy: { name: 'proof-request', version: '1.0', - attributes: presentationPreview.attributes.filter((attribute) => attribute.name === 'name'), - predicates: presentationPreview.predicates, + attributes: [], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], }, }, comment: 'V1 propose proof test 2', @@ -210,33 +184,20 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1ProposePresentationMessage, - }) - + proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', id: expect.any(String), comment: 'V1 propose proof test 2', presentationProposal: { type: 'https://didcomm.org/present-proof/1.0/presentation-preview', - attributes: [ - { - name: 'name', - credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, - value: 'John', - referent: '0', - }, - ], + attributes: [], predicates: [ { name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + credentialDefinitionId, predicate: '>=', - threshold: 50, + threshold: 18, }, ], }, @@ -264,13 +225,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), @@ -301,20 +256,13 @@ describe('Present Proof', () => { comment: 'V1 propose proof test 2', presentationProposal: { type: 'https://didcomm.org/present-proof/1.0/presentation-preview', - attributes: [ - { - name: 'name', - credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, - value: 'John', - referent: '0', - }, - ], + attributes: [], predicates: [ { name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + credentialDefinitionId, predicate: '>=', - threshold: 50, + threshold: 18, }, ], }, @@ -328,23 +276,14 @@ describe('Present Proof', () => { expect(proofRequestMessage.indyProofRequest).toMatchObject({ name: 'Proof Request', version: '1.0', - requested_attributes: { - '0': { - name: 'name', - restrictions: [ - { - cred_def_id: credDefId, - }, - ], - }, - }, + requested_attributes: {}, requested_predicates: { [predicateKey]: { p_type: '>=', - p_value: 50, + p_value: 18, restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-presentation.e2e.test.ts similarity index 60% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts rename to packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-presentation.e2e.test.ts index 86095b8f01..5b4c358a2f 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-presentation.test.e2e.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-presentation.e2e.test.ts @@ -1,29 +1,55 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { V1PresentationPreview } from '../models/V1PresentationPreview' +import type { EventReplaySubject } from '../../../../../../core/tests' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage/didcomm' -import { ProofState } from '../../../models/ProofState' -import { ProofExchangeRecord } from '../../../repository' -import { V1PresentationMessage, V1ProposePresentationMessage, V1RequestPresentationMessage } from '../messages' +import { ProofState, ProofExchangeRecord } from '../../../../../../core/src' +import { testLogger, waitForProofExchangeRecord } from '../../../../../../core/tests' +import { issueLegacyAnonCredsCredential, setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let aliceConnectionId: string + let faberConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber Agent Proofs', - 'Alice Agent Proofs' - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + holderIssuerConnectionId: aliceConnectionId, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber - V1 Indy Proof', + holderName: 'Alice - V1 Indy Proof', + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'John', + }, + { + name: 'age', + value: '55', + }, + ], + }, + }) }) afterAll(async () => { @@ -37,19 +63,33 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.ProposalReceived, }) - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v1', proofFormats: { indy: { name: 'ProofRequest', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + value: 'John', + credentialDefinitionId, + referent: '0', + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], }, }, comment: 'V1 propose proof test', @@ -57,15 +97,9 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1ProposePresentationMessage, - }) + let faberProofExchangeRecord = await faberProofExchangeRecordPromise + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', id: expect.any(String), @@ -75,19 +109,15 @@ describe('Present Proof', () => { attributes: [ { name: 'name', - credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, + credentialDefinitionId, value: 'John', referent: '0', }, - { - name: 'image_0', - credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, - }, ], predicates: [ { name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + credentialDefinitionId, predicate: '>=', threshold: 50, }, @@ -100,11 +130,9 @@ describe('Present Proof', () => { state: ProofState.ProposalReceived, protocolVersion: 'v1', }) - }) - test(`Faber accepts the Proposal send by Alice`, async () => { // Accept Proposal - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, }) @@ -117,13 +145,7 @@ describe('Present Proof', () => { testLogger.test('Alice waits for proof request from Faber') aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1RequestPresentationMessage, - }) - + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/request-presentation', id: expect.any(String), @@ -146,14 +168,12 @@ describe('Present Proof', () => { state: ProofState.RequestReceived, protocolVersion: 'v1', }) - }) - test(`Alice accepts presentation request from Faber`, async () => { const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, }) - const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { threadId: aliceProofExchangeRecord.threadId, state: ProofState.PresentationReceived, }) @@ -167,11 +187,7 @@ describe('Present Proof', () => { testLogger.test('Faber waits for presentation from Alice') faberProofExchangeRecord = await faberProofExchangeRecordPromise - const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1PresentationMessage, - }) - + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) expect(presentation).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/presentation', id: expect.any(String), @@ -184,15 +200,6 @@ describe('Present Proof', () => { }, }, ], - appendedAttachments: [ - { - id: expect.any(String), - filename: expect.any(String), - data: { - base64: expect.any(String), - }, - }, - ], thread: { threadId: expect.any(String), }, @@ -204,10 +211,8 @@ describe('Present Proof', () => { state: ProofState.PresentationReceived, protocolVersion: 'v1', }) - }) - test(`Faber accepts the presentation provided by Alice`, async () => { - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-proposal.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-proposal.e2e.test.ts new file mode 100644 index 0000000000..14e9e72145 --- /dev/null +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-proposal.e2e.test.ts @@ -0,0 +1,106 @@ +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' + +import { ProofState } from '../../../../../../core/src' +import { testLogger, waitForProofExchangeRecord } from '../../../../../../core/tests' +import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' + +describe('Present Proof', () => { + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let aliceConnectionId: string + let credentialDefinitionId: string + + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + credentialDefinitionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber - V1 Indy Proof Request', + holderName: 'Alice - V1 Indy Proof Request', + attributeNames: ['name', 'age'], + })) + }) + + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test(`Alice Creates and sends Proof Proposal to Faber`, async () => { + testLogger.test('Alice sends proof proposal to Faber') + + const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { + state: ProofState.ProposalReceived, + }) + + await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'ProofRequest', + version: '1.0', + attributes: [ + { + name: 'name', + value: 'John', + credentialDefinitionId, + referent: '0', + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], + }, + }, + comment: 'V1 propose proof test', + }) + + testLogger.test('Faber waits for presentation from Alice') + const faberProofExchangeRecord = await faberProofExchangeRecordPromise + + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) + expect(proposal).toMatchObject({ + type: 'https://didcomm.org/present-proof/1.0/propose-presentation', + id: expect.any(String), + comment: 'V1 propose proof test', + presentationProposal: { + type: 'https://didcomm.org/present-proof/1.0/presentation-preview', + attributes: [ + { + name: 'name', + credentialDefinitionId, + value: 'John', + referent: '0', + }, + ], + predicates: [ + { + name: 'age', + credentialDefinitionId, + predicate: '>=', + threshold: 50, + }, + ], + }, + }) + + expect(faberProofExchangeRecord).toMatchObject({ + id: expect.anything(), + threadId: faberProofExchangeRecord.threadId, + state: ProofState.ProposalReceived, + protocolVersion: 'v1', + }) + }) +}) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proof-request.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-request.e2e.test.ts similarity index 59% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proof-request.e2e.test.ts rename to packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-request.e2e.test.ts index 8c9278b879..36e9203b0d 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proof-request.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proof-request.e2e.test.ts @@ -1,26 +1,27 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { ProofExchangeRecord } from '../../../repository' -import type { V1PresentationPreview } from '../models' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { ProofState } from '../../../models' +import { ProofState } from '../../../../../../core/src' +import { testLogger, waitForProofExchangeRecord } from '../../../../../../core/tests' +import { setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' describe('Present Proof | V1ProofProtocol', () => { - let faberAgent: Agent - let aliceAgent: Agent - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' - )) + ;({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + credentialDefinitionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber - V1 Indy Proof Request', + holderName: 'Alice - V1 Indy Proof Request', + attributeNames: ['name', 'age'], + })) }) afterAll(async () => { @@ -31,53 +32,63 @@ describe('Present Proof | V1ProofProtocol', () => { await aliceAgent.wallet.delete() }) - test(`Alice Creates and sends Proof Proposal to Faber`, async () => { + test(`Alice Creates and sends Proof Proposal to Faber and Faber accepts the proposal`, async () => { testLogger.test('Alice sends proof proposal to Faber') const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { state: ProofState.ProposalReceived, }) - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v1', proofFormats: { indy: { name: 'Proof Request', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + value: 'John', + credentialDefinitionId, + referent: '0', + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], }, }, comment: 'V1 propose proof test', }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise + let faberProofExchangeRecord = await faberProofExchangeRecordPromise const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) - expect(proposal).toMatchObject({ - type: 'https://didcomm.org/present-proof/1.0/propose-presentation', - id: expect.any(String), + expect(proposal?.toJSON()).toMatchObject({ + '@type': 'https://didcomm.org/present-proof/1.0/propose-presentation', + '@id': expect.any(String), comment: 'V1 propose proof test', - presentationProposal: { - type: 'https://didcomm.org/present-proof/1.0/presentation-preview', + presentation_proposal: { + '@type': 'https://didcomm.org/present-proof/1.0/presentation-preview', attributes: [ { name: 'name', - credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, + cred_def_id: credentialDefinitionId, value: 'John', referent: '0', }, - { - name: 'image_0', - credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, - }, ], predicates: [ { name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + cred_def_id: credentialDefinitionId, predicate: '>=', threshold: 50, }, @@ -90,9 +101,7 @@ describe('Present Proof | V1ProofProtocol', () => { state: ProofState.ProposalReceived, protocolVersion: 'v1', }) - }) - test(`Faber accepts the Proposal sent by Alice and Creates Proof Request`, async () => { const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { threadId: faberProofExchangeRecord.threadId, state: ProofState.RequestReceived, diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proofs.e2e.test.ts similarity index 72% rename from packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts rename to packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proofs.e2e.test.ts index 918673b0b3..ff71996463 100644 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-indy-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proofs.e2e.test.ts @@ -1,30 +1,50 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { V1PresentationPreview } from '../models' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { getGroupKeysFromIndyProofFormatData } from '../../../formats/indy/__tests__/groupKeys' -import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' -import { ProofState } from '../../../models' -import { ProofExchangeRecord } from '../../../repository' +import type { EventReplaySubject } from '../../../../../../core/tests' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' + +import { ProofState, ProofExchangeRecord } from '../../../../../../core/src' +import { testLogger, waitForProofExchangeRecord } from '../../../../../../core/tests' +import { issueLegacyAnonCredsCredential, setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' import { V1ProposePresentationMessage, V1RequestPresentationMessage, V1PresentationMessage } from '../messages' describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let presentationPreview: V1PresentationPreview + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let faberConnectionId: string + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('Faber agent v1', 'Alice agent v1')) - testLogger.test('Issuing second credential') + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Proofs V1 - Full', + holderName: 'Alice Proofs V1 - Full', + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { name: 'name', value: 'John' }, + { name: 'age', value: '99' }, + ], + }, + }) }) afterAll(async () => { @@ -43,20 +63,34 @@ describe('Present Proof', () => { state: ProofState.ProposalReceived, }) - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v1', proofFormats: { indy: { - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + value: 'John', + credentialDefinitionId, + referent: '0', + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], }, }, }) // Faber waits for a presentation proposal from Alice testLogger.test('Faber waits for a presentation proposal from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise + let faberProofExchangeRecord = await faberProofExchangeRecordPromise const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/1.0/propose-presentation', @@ -66,19 +100,15 @@ describe('Present Proof', () => { attributes: [ { name: 'name', - credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, + credentialDefinitionId, value: 'John', referent: '0', }, - { - name: 'image_0', - credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, - }, ], predicates: [ { name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, + credentialDefinitionId, predicate: '>=', threshold: 50, }, @@ -159,15 +189,6 @@ describe('Present Proof', () => { }, }, ], - // appendedAttachments: [ - // { - // id: expect.any(String), - // filename: expect.any(String), - // data: { - // base64: expect.any(String), - // }, - // }, - // ], thread: { threadId: expect.any(String), }, @@ -222,8 +243,8 @@ describe('Present Proof', () => { const formatData = await aliceAgent.proofs.getFormatData(aliceProofExchangeRecord.id) - // eslint-disable-next-line prefer-const - let { proposeKey1, proposeKey2, requestKey1, requestKey2 } = getGroupKeysFromIndyProofFormatData(formatData) + const proposalPredicateKey = Object.keys(formatData.proposal?.indy?.requested_predicates || {})[0] + const requestPredicateKey = Object.keys(formatData.request?.indy?.requested_predicates || {})[0] expect(formatData).toMatchObject({ proposal: { @@ -235,23 +256,15 @@ describe('Present Proof', () => { 0: { name: 'name', }, - [proposeKey1]: { - name: 'image_0', - restrictions: [ - { - cred_def_id: credDefId, - }, - ], - }, }, requested_predicates: { - [proposeKey2]: { + [proposalPredicateKey]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -267,23 +280,15 @@ describe('Present Proof', () => { 0: { name: 'name', }, - [requestKey1]: { - name: 'image_0', - restrictions: [ - { - cred_def_id: credDefId, - }, - ], - }, }, requested_predicates: { - [requestKey2]: { + [requestPredicateKey]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -301,61 +306,48 @@ describe('Present Proof', () => { }) test('Faber starts with proof request to Alice', async () => { - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Sample predicates - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') - faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + let faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v1', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + let aliceProofExchangeRecord = await aliceProofExchangeRecordPromise const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ @@ -413,15 +405,6 @@ describe('Present Proof', () => { }, }, ], - // appendedAttachments: [ - // { - // id: expect.any(String), - // filename: expect.any(String), - // data: { - // base64: expect.any(String), - // }, - // }, - // ], thread: { threadId: expect.any(String), }, @@ -468,42 +451,36 @@ describe('Present Proof', () => { }) test('an attribute group name matches with a predicate group name so an error is thrown', async () => { - // Age attribute - const attributes = { - age: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Age predicate - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - await expect( faberAgent.proofs.requestProof({ protocolVersion: 'v1', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + age: { + name: 'age', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) @@ -511,61 +488,48 @@ describe('Present Proof', () => { }) test('Faber starts with proof request to Alice but gets Problem Reported', async () => { - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Sample predicates - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') - faberProofExchangeRecord = await faberAgent.proofs.requestProof({ + let faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v1', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) // Alice waits for presentation request from Faber testLogger.test('Alice waits for presentation request from Faber') - aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + let aliceProofExchangeRecord = await aliceProofExchangeRecordPromise const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts new file mode 100644 index 0000000000..407e975271 --- /dev/null +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts @@ -0,0 +1,272 @@ +import type { EventReplaySubject } from '../../../../../../core/tests' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' + +import { AutoAcceptProof, ProofState } from '../../../../../../core/src' +import { testLogger, waitForProofExchangeRecord } from '../../../../../../core/tests' +import { issueLegacyAnonCredsCredential, setupAnonCredsTests } from '../../../../../tests/legacyAnonCredsSetup' + +describe('Auto accept present proof', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let faberConnectionId: string + let aliceConnectionId: string + let credentialDefinitionId: string + + describe("Auto accept on 'always'", () => { + beforeAll(async () => { + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Auto Accept Always Proofs', + holderName: 'Alice Auto Accept Always Proofs', + autoAcceptProofs: AutoAcceptProof.Always, + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { name: 'name', value: 'John' }, + { name: 'age', value: '99' }, + ], + }, + }) + }) + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'always'", async () => { + testLogger.test('Alice sends presentation proposal to Faber') + + await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'abc', + version: '1.0', + attributes: [ + { + name: 'name', + value: 'John', + credentialDefinitionId, + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], + }, + }, + }) + + testLogger.test('Faber waits for presentation from Alice') + testLogger.test('Alice waits till it receives presentation ack') + await Promise.all([ + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + ]) + }) + + test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'always'", async () => { + testLogger.test('Faber sends presentation request to Alice') + + await faberAgent.proofs.requestProof({ + protocolVersion: 'v1', + connectionId: faberConnectionId, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + testLogger.test('Faber waits for presentation from Alice') + await Promise.all([ + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + ]) + }) + }) + + describe("Auto accept on 'contentApproved'", () => { + beforeAll(async () => { + testLogger.test('Initializing the agents') + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Auto Accept ContentApproved Proofs', + holderName: 'Alice Auto Accept ContentApproved Proofs', + autoAcceptProofs: AutoAcceptProof.ContentApproved, + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { name: 'name', value: 'John' }, + { name: 'age', value: '99' }, + ], + }, + }) + }) + afterAll(async () => { + testLogger.test('Shutting down both agents') + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'contentApproved'", async () => { + testLogger.test('Alice sends presentation proposal to Faber') + + const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'abc', + version: '1.0', + attributes: [ + { + name: 'name', + value: 'John', + credentialDefinitionId, + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], + }, + }, + }) + + testLogger.test('Faber waits for presentation proposal from Alice') + const faberProofExchangeRecord = await waitForProofExchangeRecord(faberAgent, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.ProposalReceived, + }) + + testLogger.test('Faber accepts presentation proposal from Alice') + await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofExchangeRecord.id }) + + await Promise.all([ + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + ]) + }) + + test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'contentApproved'", async () => { + testLogger.test('Faber sends presentation request to Alice') + + await faberAgent.proofs.requestProof({ + protocolVersion: 'v1', + connectionId: faberConnectionId, + proofFormats: { + indy: { + name: 'proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + }) + + testLogger.test('Alice waits for request from Faber') + const { id: proofRecordId } = await waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + const { proofFormats } = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId }) + await aliceAgent.proofs.acceptRequest({ proofRecordId, proofFormats }) + + await Promise.all([ + waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), + waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), + ]) + }) + }) +}) diff --git a/packages/core/src/modules/proofs/protocol/v1/errors/V1PresentationProblemReportError.ts b/packages/anoncreds/src/protocols/proofs/v1/errors/V1PresentationProblemReportError.ts similarity index 62% rename from packages/core/src/modules/proofs/protocol/v1/errors/V1PresentationProblemReportError.ts rename to packages/anoncreds/src/protocols/proofs/v1/errors/V1PresentationProblemReportError.ts index 27c77c0f82..4ec7280eae 100644 --- a/packages/core/src/modules/proofs/protocol/v1/errors/V1PresentationProblemReportError.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/errors/V1PresentationProblemReportError.ts @@ -1,8 +1,8 @@ -import type { ProblemReportErrorOptions } from '../../../../problem-reports' -import type { PresentationProblemReportReason } from '../../../errors/PresentationProblemReportReason' +import type { ProblemReportErrorOptions, PresentationProblemReportReason } from '@aries-framework/core' -import { ProblemReportError } from '../../../../problem-reports' -import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' +import { ProblemReportError } from '@aries-framework/core' + +import { V1PresentationProblemReportMessage } from '../messages' interface V1PresentationProblemReportErrorOptions extends ProblemReportErrorOptions { problemCode: PresentationProblemReportReason diff --git a/packages/core/src/modules/proofs/protocol/v1/errors/index.ts b/packages/anoncreds/src/protocols/proofs/v1/errors/index.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/errors/index.ts rename to packages/anoncreds/src/protocols/proofs/v1/errors/index.ts diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationAckHandler.ts similarity index 93% rename from packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts rename to packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationAckHandler.ts index c8f331c3a8..dc087fe1af 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationAckHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationAckHandler.ts @@ -1,5 +1,5 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1ProofProtocol } from '../V1ProofProtocol' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { V1PresentationAckMessage } from '../messages' diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts similarity index 90% rename from packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts rename to packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts index e5553b1283..20732e3ecf 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts @@ -1,9 +1,8 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { ProofExchangeRecord } from '../../../repository' import type { V1ProofProtocol } from '../V1ProofProtocol' +import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@aries-framework/core' + +import { OutboundMessageContext, DidCommMessageRepository } from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' -import { DidCommMessageRepository } from '../../../../../storage' import { V1PresentationMessage, V1RequestPresentationMessage } from '../messages' export class V1PresentationHandler implements MessageHandler { diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationProblemReportHandler.ts similarity index 94% rename from packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts rename to packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationProblemReportHandler.ts index eee0266a68..4106bbf3f9 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationProblemReportHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationProblemReportHandler.ts @@ -1,5 +1,5 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' import type { V1ProofProtocol } from '../V1ProofProtocol' +import type { MessageHandler, MessageHandlerInboundMessage } from '@aries-framework/core' import { V1PresentationProblemReportMessage } from '../messages/V1PresentationProblemReportMessage' diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1ProposePresentationHandler.ts similarity index 86% rename from packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts rename to packages/anoncreds/src/protocols/proofs/v1/handlers/V1ProposePresentationHandler.ts index 113c695c98..2193f6e733 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1ProposePresentationHandler.ts @@ -1,8 +1,8 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V1ProofProtocol } from '../V1ProofProtocol' +import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@aries-framework/core' + +import { OutboundMessageContext } from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' import { V1ProposePresentationMessage } from '../messages' export class V1ProposePresentationHandler implements MessageHandler { diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts similarity index 86% rename from packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts rename to packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts index 340307e50a..f0309ddee3 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts @@ -1,11 +1,14 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V1ProofProtocol } from '../V1ProofProtocol' +import type { MessageHandler, MessageHandlerInboundMessage, ProofExchangeRecord } from '@aries-framework/core' + +import { + OutboundMessageContext, + RoutingService, + ServiceDecorator, + DidCommMessageRepository, + DidCommMessageRole, +} from '@aries-framework/core' -import { OutboundMessageContext } from '../../../../../agent/models' -import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../../storage' -import { RoutingService } from '../../../../routing' import { V1RequestPresentationMessage } from '../messages' export class V1RequestPresentationHandler implements MessageHandler { diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/index.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/index.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/handlers/index.ts rename to packages/anoncreds/src/protocols/proofs/v1/handlers/index.ts diff --git a/packages/core/src/modules/proofs/protocol/v1/index.ts b/packages/anoncreds/src/protocols/proofs/v1/index.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/index.ts rename to packages/anoncreds/src/protocols/proofs/v1/index.ts diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationAckMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts similarity index 64% rename from packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationAckMessage.ts rename to packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts index 29f12a8dd9..743fdba4fa 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationAckMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts @@ -1,7 +1,6 @@ -import type { AckMessageOptions } from '../../../../common' +import type { AckMessageOptions } from '@aries-framework/core' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { AckMessage } from '../../../../common' +import { AckMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' export class V1PresentationAckMessage extends AckMessage { public constructor(options: AckMessageOptions) { diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts similarity index 84% rename from packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts rename to packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts index 12d2978ab0..e27309111c 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts @@ -1,12 +1,9 @@ -import type { IndyProof } from 'indy-sdk' +import type { AnonCredsProof } from '../../../../models' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' - export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0' export interface V1PresentationMessageOptions { @@ -57,11 +54,11 @@ export class V1PresentationMessage extends AgentMessage { @IsInstance(Attachment, { each: true }) public presentationAttachments!: Attachment[] - public get indyProof(): IndyProof | null { + public get indyProof(): AnonCredsProof | null { const attachment = this.presentationAttachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) ?? null - const proofJson = attachment?.getDataAsJson() ?? null + const proofJson = attachment?.getDataAsJson() ?? null return proofJson } diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationProblemReportMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts similarity index 72% rename from packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationProblemReportMessage.ts rename to packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts index 87901ce6a8..baa7d1935e 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1PresentationProblemReportMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts @@ -1,7 +1,6 @@ -import type { ProblemReportMessageOptions } from '../../../../problem-reports/messages/ProblemReportMessage' +import type { ProblemReportMessageOptions } from '@aries-framework/core' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { ProblemReportMessage } from '../../../../problem-reports/messages/ProblemReportMessage' +import { ProblemReportMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' export type V1PresentationProblemReportMessageOptions = ProblemReportMessageOptions diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts similarity index 91% rename from packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts rename to packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts index bef0a44c7a..12d94f73fa 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1ProposePresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts @@ -1,8 +1,7 @@ +import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { V1PresentationPreview } from '../models/V1PresentationPreview' export interface V1ProposePresentationMessageOptions { diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts similarity index 82% rename from packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts rename to packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts index 5ac5fd6798..e49dfd9aaa 100644 --- a/packages/core/src/modules/proofs/protocol/v1/messages/V1RequestPresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts @@ -1,12 +1,9 @@ -import type { IndyProofRequest } from 'indy-sdk' +import type { LegacyIndyProofRequest } from '../../../../formats' +import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' - export interface V1RequestPresentationMessageOptions { id?: string comment?: string @@ -54,10 +51,10 @@ export class V1RequestPresentationMessage extends AgentMessage { @IsInstance(Attachment, { each: true }) public requestAttachments!: Attachment[] - public get indyProofRequest(): IndyProofRequest | null { + public get indyProofRequest(): LegacyIndyProofRequest | null { const attachment = this.requestAttachments.find((attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID) // Extract proof request from attachment - return attachment?.getDataAsJson() ?? null + return attachment?.getDataAsJson() ?? null } public getRequestAttachmentById(id: string): Attachment | undefined { diff --git a/packages/core/src/modules/proofs/protocol/v1/messages/index.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/index.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/messages/index.ts rename to packages/anoncreds/src/protocols/proofs/v1/messages/index.ts diff --git a/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts b/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts similarity index 87% rename from packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts rename to packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts index 0de67b3a00..7e651dea57 100644 --- a/packages/core/src/modules/proofs/protocol/v1/models/V1PresentationPreview.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts @@ -1,6 +1,7 @@ +import { JsonTransformer, IsValidMessageType, replaceLegacyDidSovPrefix, parseMessageType } from '@aries-framework/core' import { Expose, Transform, Type } from 'class-transformer' import { - IsEnum, + IsIn, IsInstance, IsInt, IsMimeType, @@ -11,10 +12,8 @@ import { ValidateNested, } from 'class-validator' -import { credDefIdRegex } from '../../../../../utils' -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { IsValidMessageType, parseMessageType, replaceLegacyDidSovPrefix } from '../../../../../utils/messageType' -import { PredicateType } from '../../../formats/indy/models/PredicateType' +import { anonCredsPredicateType, AnonCredsPredicateType } from '../../../../models' +import { legacyIndyCredentialDefinitionIdRegex } from '../../../../utils' export interface V1PresentationPreviewAttributeOptions { name: string @@ -40,7 +39,7 @@ export class V1PresentationPreviewAttribute { @Expose({ name: 'cred_def_id' }) @IsString() @ValidateIf((o: V1PresentationPreviewAttribute) => o.referent !== undefined) - @Matches(credDefIdRegex) + @Matches(legacyIndyCredentialDefinitionIdRegex) public credentialDefinitionId?: string @Expose({ name: 'mime-type' }) @@ -64,7 +63,7 @@ export class V1PresentationPreviewAttribute { export interface V1PresentationPreviewPredicateOptions { name: string credentialDefinitionId: string - predicate: PredicateType + predicate: AnonCredsPredicateType threshold: number } @@ -83,11 +82,11 @@ export class V1PresentationPreviewPredicate { @Expose({ name: 'cred_def_id' }) @IsString() - @Matches(credDefIdRegex) + @Matches(legacyIndyCredentialDefinitionIdRegex) public credentialDefinitionId!: string - @IsEnum(PredicateType) - public predicate!: PredicateType + @IsIn(anonCredsPredicateType) + public predicate!: AnonCredsPredicateType @IsInt() public threshold!: number diff --git a/packages/core/src/modules/proofs/protocol/v1/models/index.ts b/packages/anoncreds/src/protocols/proofs/v1/models/index.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v1/models/index.ts rename to packages/anoncreds/src/protocols/proofs/v1/models/index.ts diff --git a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts index 1bf5614720..815150c6c1 100644 --- a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts @@ -26,7 +26,7 @@ export interface RegisterCredentialDefinitionReturnStateFailed extends AnonCreds export interface RegisterCredentialDefinitionReturnStateFinished extends AnonCredsOperationStateFinished { credentialDefinition: AnonCredsCredentialDefinition - credentialDefinitionId?: string + credentialDefinitionId: string } export interface RegisterCredentialDefinitionReturnState extends AnonCredsOperationState { diff --git a/packages/anoncreds/src/utils/__tests__/credentialPreviewAttributes.test.ts b/packages/anoncreds/src/utils/__tests__/credentialPreviewAttributes.test.ts new file mode 100644 index 0000000000..419f9af0b7 --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/credentialPreviewAttributes.test.ts @@ -0,0 +1,143 @@ +import { areCredentialPreviewAttributesEqual } from '../credentialPreviewAttributes' + +describe('areCredentialPreviewAttributesEqual', () => { + test('returns true if the attributes are equal', () => { + const firstAttributes = [ + { + name: 'firstName', + value: 'firstValue', + mimeType: 'text/grass', + }, + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + const secondAttribute = [ + { + name: 'firstName', + value: 'firstValue', + mimeType: 'text/grass', + }, + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + expect(areCredentialPreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(true) + }) + + test('returns false if the attribute name and value are equal but the mime type is different', () => { + const firstAttributes = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + const secondAttribute = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/notGrass', + }, + ] + + expect(areCredentialPreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) + + test('returns false if the attribute name and mime type are equal but the value is different', () => { + const firstAttributes = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + const secondAttribute = [ + { + name: 'secondName', + value: 'thirdValue', + mimeType: 'text/grass', + }, + ] + + expect(areCredentialPreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) + + test('returns false if the value and mime type are equal but the name is different', () => { + const firstAttributes = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + const secondAttribute = [ + { + name: 'thirdName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + expect(areCredentialPreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) + + test('returns false if the length of the attributes does not match', () => { + const firstAttributes = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + const secondAttribute = [ + { + name: 'thirdName', + value: 'secondValue', + mimeType: 'text/grass', + }, + { + name: 'fourthName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + expect(areCredentialPreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) + + test('returns false if duplicate key names exist', () => { + const firstAttributes = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + const secondAttribute = [ + { + name: 'secondName', + value: 'secondValue', + mimeType: 'text/grass', + }, + ] + + expect(areCredentialPreviewAttributesEqual(firstAttributes, secondAttribute)).toBe(false) + }) +}) diff --git a/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts b/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts index 4e7bab2ddd..c4deb02be7 100644 --- a/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts +++ b/packages/anoncreds/src/utils/__tests__/hasDuplicateGroupNames.test.ts @@ -1,10 +1,10 @@ import type { AnonCredsProofRequest } from '../../models' -import { hasDuplicateGroupsNamesInProofRequest } from '../hasDuplicateGroupNames' +import { assertNoDuplicateGroupsNamesInProofRequest } from '../hasDuplicateGroupNames' const credentialDefinitionId = '9vPXgSpQJPkJEALbLXueBp:3:CL:57753:tag1' -describe('util | hasDuplicateGroupsNamesInProofRequest', () => { +describe('util | assertNoDuplicateGroupsNamesInProofRequest', () => { describe('assertNoDuplicateGroupsNamesInProofRequest', () => { test('attribute names match', () => { const proofRequest = { @@ -32,7 +32,7 @@ describe('util | hasDuplicateGroupsNamesInProofRequest', () => { requested_predicates: {}, } satisfies AnonCredsProofRequest - expect(hasDuplicateGroupsNamesInProofRequest(proofRequest)).toBe(false) + expect(() => assertNoDuplicateGroupsNamesInProofRequest(proofRequest)).not.toThrow() }) test('attribute names match with predicates name', () => { @@ -64,7 +64,9 @@ describe('util | hasDuplicateGroupsNamesInProofRequest', () => { }, } satisfies AnonCredsProofRequest - expect(hasDuplicateGroupsNamesInProofRequest(proofRequest)).toBe(true) + expect(() => assertNoDuplicateGroupsNamesInProofRequest(proofRequest)).toThrowError( + 'The proof request contains duplicate predicates and attributes: age' + ) }) }) }) diff --git a/packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts b/packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts new file mode 100644 index 0000000000..2d38390ae9 --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts @@ -0,0 +1,34 @@ +import { + legacyIndyCredentialDefinitionIdRegex, + legacyIndyDidRegex, + legacyIndySchemaIdRegex, + legacyIndySchemaVersionRegex, +} from '../legacyIndyIdentifiers' + +describe('Legacy Indy Identifier Regex', () => { + const invalidTest = 'test' + + test('test for legacyIndyCredentialDefinitionIdRegex', async () => { + const test = 'q7ATwTYbQDgiigVijUAej:3:CL:160971:1.0.0' + expect(test).toMatch(legacyIndyCredentialDefinitionIdRegex) + expect(legacyIndyCredentialDefinitionIdRegex.test(invalidTest)).toBeFalsy() + }) + + test('test for legacyIndyDidRegex', async () => { + const test = 'did:sov:q7ATwTYbQDgiigVijUAej' + expect(test).toMatch(legacyIndyDidRegex) + expect(legacyIndyDidRegex.test(invalidTest)).toBeFalsy + }) + + test('test for legacyIndySchemaIdRegex', async () => { + const test = 'q7ATwTYbQDgiigVijUAej:2:test:1.0' + expect(test).toMatch(legacyIndySchemaIdRegex) + expect(legacyIndySchemaIdRegex.test(invalidTest)).toBeFalsy + }) + + test('test for legacyIndySchemaVersionRegex', async () => { + const test = '1.0.0' + expect(test).toMatch(legacyIndySchemaVersionRegex) + expect(legacyIndySchemaVersionRegex.test(invalidTest)).toBeFalsy + }) +}) diff --git a/packages/anoncreds/src/utils/composeAutoAccept.ts b/packages/anoncreds/src/utils/composeAutoAccept.ts new file mode 100644 index 0000000000..0d874154d2 --- /dev/null +++ b/packages/anoncreds/src/utils/composeAutoAccept.ts @@ -0,0 +1,21 @@ +import { AutoAcceptCredential, AutoAcceptProof } from '@aries-framework/core' + +/** + * Returns the credential auto accept config based on priority: + * - The record config takes first priority + * - Otherwise the agent config + * - Otherwise {@link AutoAcceptCredential.Never} is returned + */ +export function composeCredentialAutoAccept(recordConfig?: AutoAcceptCredential, agentConfig?: AutoAcceptCredential) { + return recordConfig ?? agentConfig ?? AutoAcceptCredential.Never +} + +/** + * Returns the proof auto accept config based on priority: + * - The record config takes first priority + * - Otherwise the agent config + * - Otherwise {@link AutoAcceptProof.Never} is returned + */ +export function composeProofAutoAccept(recordConfig?: AutoAcceptProof, agentConfig?: AutoAcceptProof) { + return recordConfig ?? agentConfig ?? AutoAcceptProof.Never +} diff --git a/packages/anoncreds/src/utils/credentialPreviewAttributes.ts b/packages/anoncreds/src/utils/credentialPreviewAttributes.ts new file mode 100644 index 0000000000..686e07ac80 --- /dev/null +++ b/packages/anoncreds/src/utils/credentialPreviewAttributes.ts @@ -0,0 +1,27 @@ +import type { CredentialPreviewAttributeOptions } from '@aries-framework/core' + +export function areCredentialPreviewAttributesEqual( + firstAttributes: CredentialPreviewAttributeOptions[], + secondAttributes: CredentialPreviewAttributeOptions[] +) { + if (firstAttributes.length !== secondAttributes.length) return false + + const secondAttributeMap = secondAttributes.reduce>( + (attributeMap, attribute) => ({ ...attributeMap, [attribute.name]: attribute }), + {} + ) + + // check if no duplicate keys exist + if (new Set(firstAttributes.map((attribute) => attribute.name)).size !== firstAttributes.length) return false + if (new Set(secondAttributes.map((attribute) => attribute.name)).size !== secondAttributes.length) return false + + for (const firstAttribute of firstAttributes) { + const secondAttribute = secondAttributeMap[firstAttribute.name] + + if (!secondAttribute) return false + if (firstAttribute.value !== secondAttribute.value) return false + if (firstAttribute.mimeType !== secondAttribute.mimeType) return false + } + + return true +} diff --git a/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts b/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts index f4915fe6fc..7a16743eb9 100644 --- a/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts +++ b/packages/anoncreds/src/utils/hasDuplicateGroupNames.ts @@ -1,5 +1,7 @@ import type { AnonCredsProofRequest } from '../models' +import { AriesFrameworkError } from '@aries-framework/core' + function attributeNamesToArray(proofRequest: AnonCredsProofRequest) { // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array // containing all attribute names from the requested attributes. @@ -14,10 +16,14 @@ function predicateNamesToArray(proofRequest: AnonCredsProofRequest) { } // TODO: This is still not ideal. The requested groups can specify different credentials using restrictions. -export function hasDuplicateGroupsNamesInProofRequest(proofRequest: AnonCredsProofRequest) { +export function assertNoDuplicateGroupsNamesInProofRequest(proofRequest: AnonCredsProofRequest) { const attributes = attributeNamesToArray(proofRequest) const predicates = predicateNamesToArray(proofRequest) - const duplicates = predicates.find((item) => attributes.indexOf(item) !== -1) - return duplicates !== undefined + const duplicates = predicates.filter((item) => attributes.indexOf(item) !== -1) + if (duplicates.length > 0) { + throw new AriesFrameworkError( + `The proof request contains duplicate predicates and attributes: ${duplicates.toString()}` + ) + } } diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts index a140e13cfb..2de326adf2 100644 --- a/packages/anoncreds/src/utils/index.ts +++ b/packages/anoncreds/src/utils/index.ts @@ -1,8 +1,16 @@ export { createRequestFromPreview } from './createRequestFromPreview' export { sortRequestedCredentialsMatches } from './sortRequestedCredentialsMatches' -export { hasDuplicateGroupsNamesInProofRequest } from './hasDuplicateGroupNames' +export { assertNoDuplicateGroupsNamesInProofRequest } from './hasDuplicateGroupNames' export { areAnonCredsProofRequestsEqual } from './areRequestsEqual' export { downloadTailsFile } from './tails' export { assertRevocationInterval } from './revocationInterval' export { encodeCredentialValue, checkValidCredentialValueEncoding } from './credential' export { IsMap } from './isMap' +export { composeCredentialAutoAccept, composeProofAutoAccept } from './composeAutoAccept' +export { areCredentialPreviewAttributesEqual } from './credentialPreviewAttributes' +export { + legacyIndyCredentialDefinitionIdRegex, + legacyIndyDidRegex, + legacyIndySchemaIdRegex, + legacyIndySchemaVersionRegex, +} from './legacyIndyIdentifiers' diff --git a/packages/anoncreds/src/utils/legacyIndyIdentifiers.ts b/packages/anoncreds/src/utils/legacyIndyIdentifiers.ts new file mode 100644 index 0000000000..29cc3f45d6 --- /dev/null +++ b/packages/anoncreds/src/utils/legacyIndyIdentifiers.ts @@ -0,0 +1,5 @@ +export const legacyIndySchemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ +export const legacyIndySchemaVersionRegex = /^(\d+\.)?(\d+\.)?(\*|\d+)$/ +export const legacyIndyCredentialDefinitionIdRegex = + /^([a-zA-Z0-9]{21,22}):3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ +export const legacyIndyDidRegex = /^(did:sov:)?[a-zA-Z0-9]{21,22}$/ diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index f905c92db9..5d0561d8f0 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -1,5 +1,6 @@ import { Agent, KeyDerivationMethod } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' +import indySdk from 'indy-sdk' import { IndySdkModule } from '../../indy-sdk/src/IndySdkModule' import { AnonCredsCredentialDefinitionRepository, AnonCredsModule, AnonCredsSchemaRepository } from '../src' @@ -81,7 +82,7 @@ const agent = new Agent({ }, modules: { indySdk: new IndySdkModule({ - indySdk: agentDependencies.indy, + indySdk, }), anoncreds: new AnonCredsModule({ registries: [ diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts new file mode 100644 index 0000000000..e5a1082b64 --- /dev/null +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -0,0 +1,438 @@ +import type { EventReplaySubject } from '../../core/tests' +import type { + AnonCredsRegisterCredentialDefinitionOptions, + AnonCredsRequestedAttribute, + AnonCredsRequestedPredicate, + AnonCredsOfferCredentialFormat, + AnonCredsSchema, + RegisterCredentialDefinitionReturnStateFinished, + RegisterSchemaReturnStateFinished, +} from '../src' +import type { AutoAcceptProof, ConnectionRecord } from '@aries-framework/core' + +import { + CacheModule, + InMemoryLruCache, + Agent, + AriesFrameworkError, + AutoAcceptCredential, + CredentialEventTypes, + CredentialsModule, + CredentialState, + ProofEventTypes, + ProofsModule, + ProofState, + V2CredentialProtocol, + V2ProofProtocol, + DidsModule, +} from '@aries-framework/core' +import { randomUUID } from 'crypto' + +import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests' +import { + getAgentOptions, + makeConnection, + waitForCredentialRecordSubject, + waitForProofExchangeRecordSubject, +} from '../../core/tests/helpers' +import testLogger from '../../core/tests/logger' +import { + IndySdkAnonCredsRegistry, + IndySdkModule, + IndySdkSovDidRegistrar, + IndySdkSovDidResolver, +} from '../../indy-sdk/src' +import { getIndySdkModuleConfig } from '../../indy-sdk/tests/setupIndySdkModule' +import { + V1CredentialProtocol, + V1ProofProtocol, + AnonCredsModule, + LegacyIndyCredentialFormatService, + LegacyIndyProofFormatService, +} from '../src' + +// Helper type to get the type of the agents (with the custom modules) for the credential tests +export type AnonCredsTestsAgent = Agent> + +export const getLegacyAnonCredsModules = ({ + autoAcceptCredentials, + autoAcceptProofs, +}: { autoAcceptCredentials?: AutoAcceptCredential; autoAcceptProofs?: AutoAcceptProof } = {}) => { + const indyCredentialFormat = new LegacyIndyCredentialFormatService() + const indyProofFormat = new LegacyIndyProofFormatService() + + // Register the credential and proof protocols + const modules = { + credentials: new CredentialsModule({ + autoAcceptCredentials, + credentialProtocols: [ + new V1CredentialProtocol({ indyCredentialFormat }), + new V2CredentialProtocol({ + credentialFormats: [indyCredentialFormat], + }), + ], + }), + proofs: new ProofsModule({ + autoAcceptProofs, + proofProtocols: [ + new V1ProofProtocol({ indyProofFormat }), + new V2ProofProtocol({ + proofFormats: [indyProofFormat], + }), + ], + }), + anoncreds: new AnonCredsModule({ + registries: [new IndySdkAnonCredsRegistry()], + }), + dids: new DidsModule({ + resolvers: [new IndySdkSovDidResolver()], + registrars: [new IndySdkSovDidRegistrar()], + }), + indySdk: new IndySdkModule(getIndySdkModuleConfig()), + cache: new CacheModule({ + cache: new InMemoryLruCache({ limit: 100 }), + }), + } as const + + return modules +} + +export async function presentLegacyAnonCredsProof({ + verifierAgent, + verifierReplay, + + holderAgent, + holderReplay, + + verifierHolderConnectionId, + + request: { attributes, predicates }, +}: { + holderAgent: AnonCredsTestsAgent + holderReplay: EventReplaySubject + + verifierAgent: AnonCredsTestsAgent + verifierReplay: EventReplaySubject + + verifierHolderConnectionId: string + request: { + attributes?: Record + predicates?: Record + } +}) { + let holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { + state: ProofState.RequestReceived, + }) + + let verifierProofExchangeRecord = await verifierAgent.proofs.requestProof({ + connectionId: verifierHolderConnectionId, + proofFormats: { + indy: { + name: 'Test Proof Request', + requested_attributes: attributes, + requested_predicates: predicates, + version: '1.0', + }, + }, + protocolVersion: 'v2', + }) + + let holderProofExchangeRecord = await holderProofExchangeRecordPromise + + const selectedCredentials = await holderAgent.proofs.selectCredentialsForRequest({ + proofRecordId: holderProofExchangeRecord.id, + }) + + const verifierProofExchangeRecordPromise = waitForProofExchangeRecordSubject(verifierReplay, { + threadId: holderProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + + await holderAgent.proofs.acceptRequest({ + proofRecordId: holderProofExchangeRecord.id, + proofFormats: { indy: selectedCredentials.proofFormats.indy }, + }) + + verifierProofExchangeRecord = await verifierProofExchangeRecordPromise + + // assert presentation is valid + expect(verifierProofExchangeRecord.isVerified).toBe(true) + + holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { + threadId: holderProofExchangeRecord.threadId, + state: ProofState.Done, + }) + + verifierProofExchangeRecord = await verifierAgent.proofs.acceptPresentation({ + proofRecordId: verifierProofExchangeRecord.id, + }) + holderProofExchangeRecord = await holderProofExchangeRecordPromise + + return { + verifierProofExchangeRecord, + holderProofExchangeRecord, + } +} + +export async function issueLegacyAnonCredsCredential({ + issuerAgent, + issuerReplay, + + holderAgent, + holderReplay, + + issuerHolderConnectionId, + offer, +}: { + issuerAgent: AnonCredsTestsAgent + issuerReplay: EventReplaySubject + + holderAgent: AnonCredsTestsAgent + holderReplay: EventReplaySubject + + issuerHolderConnectionId: string + offer: AnonCredsOfferCredentialFormat +}) { + let issuerCredentialExchangeRecord = await issuerAgent.credentials.offerCredential({ + comment: 'some comment about credential', + connectionId: issuerHolderConnectionId, + protocolVersion: 'v1', + credentialFormats: { + indy: offer, + }, + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }) + + let holderCredentialExchangeRecord = await waitForCredentialRecordSubject(holderReplay, { + threadId: issuerCredentialExchangeRecord.threadId, + state: CredentialState.OfferReceived, + }) + + await holderAgent.credentials.acceptOffer({ + credentialRecordId: holderCredentialExchangeRecord.id, + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }) + + // Because we use auto-accept it can take a while to have the whole credential flow finished + // Both parties need to interact with the ledger and sign/verify the credential + holderCredentialExchangeRecord = await waitForCredentialRecordSubject(holderReplay, { + threadId: issuerCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + issuerCredentialExchangeRecord = await waitForCredentialRecordSubject(issuerReplay, { + threadId: issuerCredentialExchangeRecord.threadId, + state: CredentialState.Done, + }) + + return { + issuerCredentialExchangeRecord, + holderCredentialExchangeRecord, + } +} + +interface SetupAnonCredsTestsReturn { + issuerAgent: AnonCredsTestsAgent + issuerReplay: EventReplaySubject + + holderAgent: AnonCredsTestsAgent + holderReplay: EventReplaySubject + + issuerHolderConnectionId: CreateConnections extends true ? string : undefined + holderIssuerConnectionId: CreateConnections extends true ? string : undefined + + verifierHolderConnectionId: CreateConnections extends true + ? VerifierName extends string + ? string + : undefined + : undefined + holderVerifierConnectionId: CreateConnections extends true + ? VerifierName extends string + ? string + : undefined + : undefined + + verifierAgent: VerifierName extends string ? AnonCredsTestsAgent : undefined + verifierReplay: VerifierName extends string ? EventReplaySubject : undefined + + schemaId: string + credentialDefinitionId: string +} + +export async function setupAnonCredsTests< + VerifierName extends string | undefined = undefined, + CreateConnections extends boolean = true +>({ + issuerName, + holderName, + verifierName, + autoAcceptCredentials, + autoAcceptProofs, + attributeNames, + createConnections, +}: { + issuerName: string + holderName: string + verifierName?: VerifierName + autoAcceptCredentials?: AutoAcceptCredential + autoAcceptProofs?: AutoAcceptProof + attributeNames: string[] + createConnections?: CreateConnections +}): Promise> { + const issuerAgent = new Agent( + getAgentOptions( + issuerName, + { + endpoints: ['rxjs:issuer'], + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials, + autoAcceptProofs, + }) + ) + ) + + const holderAgent = new Agent( + getAgentOptions( + holderName, + { + endpoints: ['rxjs:holder'], + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials, + autoAcceptProofs, + }) + ) + ) + + const verifierAgent = verifierName + ? new Agent( + getAgentOptions( + verifierName, + { + endpoints: ['rxjs:verifier'], + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials, + autoAcceptProofs, + }) + ) + ) + : undefined + + setupSubjectTransports(verifierAgent ? [issuerAgent, holderAgent, verifierAgent] : [issuerAgent, holderAgent]) + const [issuerReplay, holderReplay, verifierReplay] = setupEventReplaySubjects( + verifierAgent ? [issuerAgent, holderAgent, verifierAgent] : [issuerAgent, holderAgent], + [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + ) + + await issuerAgent.initialize() + await holderAgent.initialize() + if (verifierAgent) await verifierAgent.initialize() + + const { credentialDefinition, schema } = await prepareForAnonCredsIssuance(issuerAgent, { + attributeNames, + // TODO: replace with more dynamic / generic value We should create a did using the dids module + // and use that probably + issuerId: issuerAgent.publicDid?.did as string, + }) + + let issuerHolderConnection: ConnectionRecord | undefined + let holderIssuerConnection: ConnectionRecord | undefined + let verifierHolderConnection: ConnectionRecord | undefined + let holderVerifierConnection: ConnectionRecord | undefined + + if (createConnections ?? true) { + ;[issuerHolderConnection, holderIssuerConnection] = await makeConnection(issuerAgent, holderAgent) + + if (verifierAgent) { + ;[holderVerifierConnection, verifierHolderConnection] = await makeConnection(holderAgent, verifierAgent) + } + } + + return { + issuerAgent, + issuerReplay, + + holderAgent, + holderReplay, + + verifierAgent: verifierName ? verifierAgent : undefined, + verifierReplay: verifierName ? verifierReplay : undefined, + + credentialDefinitionId: credentialDefinition.credentialDefinitionId, + schemaId: schema.schemaId, + + issuerHolderConnectionId: issuerHolderConnection?.id, + holderIssuerConnectionId: holderIssuerConnection?.id, + holderVerifierConnectionId: holderVerifierConnection?.id, + verifierHolderConnectionId: verifierHolderConnection?.id, + } as unknown as SetupAnonCredsTestsReturn +} + +export async function prepareForAnonCredsIssuance( + agent: Agent, + { attributeNames, issuerId }: { attributeNames: string[]; issuerId: string } +) { + const schema = await registerSchema(agent, { + // TODO: update attrNames to attributeNames + attrNames: attributeNames, + name: `Schema ${randomUUID()}`, + version: '1.0', + issuerId, + }) + + const credentialDefinition = await registerCredentialDefinition(agent, { + schemaId: schema.schemaId, + issuerId, + tag: 'default', + }) + + return { + schema, + credentialDefinition, + } +} + +async function registerSchema( + agent: AnonCredsTestsAgent, + schema: AnonCredsSchema +): Promise { + const { schemaState } = await agent.modules.anoncreds.registerSchema({ + schema, + options: { + didIndyNamespace: 'pool:localtest', + }, + }) + + testLogger.test(`created schema with id ${schemaState.schemaId}`, schema) + + if (schemaState.state !== 'finished') { + throw new AriesFrameworkError( + `Schema not created: ${schemaState.state === 'failed' ? schemaState.reason : 'Not finished'}` + ) + } + + return schemaState +} + +async function registerCredentialDefinition( + agent: AnonCredsTestsAgent, + credentialDefinition: AnonCredsRegisterCredentialDefinitionOptions +): Promise { + const { credentialDefinitionState } = await agent.modules.anoncreds.registerCredentialDefinition({ + credentialDefinition, + options: { + didIndyNamespace: 'pool:localtest', + }, + }) + + if (credentialDefinitionState.state !== 'finished') { + throw new AriesFrameworkError( + `Credential definition not created: ${ + credentialDefinitionState.state === 'failed' ? credentialDefinitionState.reason : 'Not finished' + }` + ) + } + + return credentialDefinitionState +} diff --git a/packages/askar/package.json b/packages/askar/package.json index af83ba53ea..9e4024caea 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.1", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.3", "bn.js": "^5.2.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", @@ -34,7 +34,7 @@ }, "devDependencies": { "@types/bn.js": "^5.1.0", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.1", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.3", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index baa95e5324..02564c4d0a 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -2,13 +2,11 @@ import type { EncryptedMessage, WalletConfig, WalletCreateKeyOptions, - DidConfig, DidInfo, WalletSignOptions, UnpackedMessageContext, WalletVerifyOptions, Wallet, - WalletExportImportConfig, WalletConfigRekey, KeyPair, KeyDerivationMethod, @@ -302,12 +300,12 @@ export class AskarWallet implements Wallet { } } - public async export(exportConfig: WalletExportImportConfig) { + public async export() { // TODO throw new WalletError('AskarWallet Export not yet implemented') } - public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig) { + public async import() { // TODO throw new WalletError('AskarWallet Import not yet implemented') } @@ -338,7 +336,7 @@ export class AskarWallet implements Wallet { } } - public async initPublicDid(didConfig: DidConfig) { + public async initPublicDid() { // Not implemented, as it does not work with legacy Ledger module } diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index 17a521a1af..d6e9ba0727 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -26,18 +26,9 @@ export function getPostgresAgentOptions( key: `Key${name}`, storage: storageConfig, }, - connectToIndyLedgersOnStartup: false, publicDidSeed, autoAcceptConnections: true, autoUpdateStorageOnStartup: false, - indyLedgers: [ - { - id: `pool-${name}`, - indyNamespace: `pool:localtest`, - isProduction: false, - genesisPath, - }, - ], logger: new TestLogger(LogLevel.off, name), ...extraConfig, } diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index efbc08027a..0ee5b85036 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -1,5 +1,5 @@ import type { W3cCredentialRepository } from '../../core/src/modules/vc/repository' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext, Wallet } from '@aries-framework/core' import { VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, @@ -17,7 +17,6 @@ import { LinkedDataProof, W3cPresentation, W3cVerifiablePresentation, - IndyWallet, Ed25519Signature2018, TypedArrayEncoder, } from '@aries-framework/core' @@ -26,6 +25,8 @@ import { SignatureSuiteRegistry } from '../../core/src/modules/vc/SignatureSuite import { W3cVcModuleConfig } from '../../core/src/modules/vc/W3cVcModuleConfig' import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader' import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { IndySdkWallet } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { BbsBlsSignature2020, BbsBlsSignatureProof2020, Bls12381g2SigningProvider } from '../src' import { BbsBlsSignature2020Fixtures } from './fixtures' @@ -60,16 +61,15 @@ const signingProviderRegistry = new SigningProviderRegistry([new Bls12381g2Signi const agentConfig = getAgentConfig('BbsSignaturesE2eTest') describeSkipNode17And18('BBS W3cCredentialService', () => { - let wallet: IndyWallet + let wallet: Wallet let agentContext: AgentContext let w3cCredentialService: W3cCredentialService const seed = TypedArrayEncoder.fromString('testseed000000000000000000000001') const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, signingProviderRegistry) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) + wallet = new IndySdkWallet(indySdk, agentConfig.logger, signingProviderRegistry) + await wallet.createAndOpen(agentConfig.walletConfig) agentContext = getAgentContext({ agentConfig, wallet, diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index d428dd65be..e956c633d7 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -1,4 +1,4 @@ -import type { WalletConfig } from '@aries-framework/core' +import type { Wallet, WalletConfig } from '@aries-framework/core' import { KeyDerivationMethod, @@ -6,12 +6,12 @@ import { WalletError, TypedArrayEncoder, SigningProviderRegistry, - IndyWallet, } from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' import { BBS_SIGNATURE_LENGTH } from '@mattrglobal/bbs-signatures' import testLogger from '../../core/tests/logger' +import { IndySdkWallet } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { Bls12381g2SigningProvider } from '../src' import { describeSkipNode17And18 } from './util' @@ -25,25 +25,21 @@ const walletConfig: WalletConfig = { } describeSkipNode17And18('BBS Signing Provider', () => { - let indyWallet: IndyWallet + let wallet: Wallet const seed = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { - indyWallet = new IndyWallet( - agentDependencies, - testLogger, - new SigningProviderRegistry([new Bls12381g2SigningProvider()]) - ) - await indyWallet.createAndOpen(walletConfig) + wallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([new Bls12381g2SigningProvider()])) + await wallet.createAndOpen(walletConfig) }) afterEach(async () => { - await indyWallet.delete() + await wallet.delete() }) test('Create bls12381g2 keypair', async () => { - await expect(indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 })).resolves.toMatchObject({ + await expect(wallet.createKey({ seed, keyType: KeyType.Bls12381g2 })).resolves.toMatchObject({ publicKeyBase58: 't54oLBmhhRcDLUyWTvfYRWw8VRXRy1p43pVm62hrpShrYPuHe9WNAgS33DPfeTK6xK7iPrtJDwCHZjYgbFYDVTJHxXex9xt2XEGF8D356jBT1HtqNeucv3YsPLfTWcLcpFA', keyType: KeyType.Bls12381g2, @@ -51,12 +47,12 @@ describeSkipNode17And18('BBS Signing Provider', () => { }) test('Fail to create bls12381g1g2 keypair', async () => { - await expect(indyWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) + await expect(wallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) }) test('Create a signature with a bls12381g2 keypair', async () => { - const bls12381g2Key = await indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) - const signature = await indyWallet.sign({ + const bls12381g2Key = await wallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) + const signature = await wallet.sign({ data: message, key: bls12381g2Key, }) @@ -64,11 +60,11 @@ describeSkipNode17And18('BBS Signing Provider', () => { }) test('Verify a signed message with a bls12381g2 publicKey', async () => { - const bls12381g2Key = await indyWallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) - const signature = await indyWallet.sign({ + const bls12381g2Key = await wallet.createKey({ seed, keyType: KeyType.Bls12381g2 }) + const signature = await wallet.sign({ data: message, key: bls12381g2Key, }) - await expect(indyWallet.verify({ key: bls12381g2Key, data: message, signature })).resolves.toStrictEqual(true) + await expect(wallet.verify({ key: bls12381g2Key, data: message, signature })).resolves.toStrictEqual(true) }) }) diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index b445f0f24b..2205fde9b0 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -1,48 +1,80 @@ -import type { ConnectionRecord } from '../../core/src/modules/connections' -import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../core/src/modules/credentials/formats/jsonld' -import type { Wallet } from '../../core/src/wallet' -import type { CredentialTestsAgent } from '../../core/tests/helpers' +import type { V2IssueCredentialMessage } from '../../core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage' +import type { EventReplaySubject, JsonLdTestsAgent } from '../../core/tests' -import { TypedArrayEncoder } from '@aries-framework/core' - -import { InjectionSymbols } from '../../core/src/constants' +import { TypedArrayEncoder } from '../../core/src' import { KeyType } from '../../core/src/crypto' import { CredentialState } from '../../core/src/modules/credentials/models' -import { V2IssueCredentialMessage } from '../../core/src/modules/credentials/protocol/v2/messages/V2IssueCredentialMessage' -import { V2OfferCredentialMessage } from '../../core/src/modules/credentials/protocol/v2/messages/V2OfferCredentialMessage' import { CredentialExchangeRecord } from '../../core/src/modules/credentials/repository/CredentialExchangeRecord' -import { DidKey } from '../../core/src/modules/dids' import { CREDENTIALS_CONTEXT_V1_URL, SECURITY_CONTEXT_BBS_URL } from '../../core/src/modules/vc' -import { DidCommMessageRepository } from '../../core/src/storage' import { JsonTransformer } from '../../core/src/utils/JsonTransformer' -import { setupCredentialTests, waitForCredentialRecord } from '../../core/tests/helpers' -import testLogger from '../../core/tests/logger' +import { waitForCredentialRecordSubject, setupJsonLdTests, testLogger } from '../../core/tests' import { describeSkipNode17And18 } from './util' -let faberAgent: CredentialTestsAgent -let aliceAgent: CredentialTestsAgent -let aliceConnection: ConnectionRecord +let faberAgent: JsonLdTestsAgent +let faberReplay: EventReplaySubject +let aliceAgent: JsonLdTestsAgent +let aliceReplay: EventReplaySubject +let aliceConnectionId: string let aliceCredentialRecord: CredentialExchangeRecord let faberCredentialRecord: CredentialExchangeRecord +const signCredentialOptions = { + credential: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: + 'did:key:zUC72Q7XD4PE4CrMiDVXuvZng3sBvMmaGgNeTUJuzavH2BS7ThbHL9FhsZM9QYY5fqAQ4MB8M9oudz3tfuaX36Ajr97QRW7LBt6WWmrtESe6Bs5NYzFtLWEmeVtvRYVAgjFcJSa', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + identifier: '83627465', + name: 'Permanent Resident Card', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + description: 'Government of Example Permanent Resident Card.', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + }, + options: { + proofType: 'BbsBlsSignature2020', + proofPurpose: 'assertionMethod', + }, +} + describeSkipNode17And18('credentials, BBS+ signature', () => { - let wallet - let issuerDidKey: DidKey - let didCommMessageRepository: DidCommMessageRepository - let signCredentialOptions: JsonLdCredentialDetailFormat - const seed = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { - ;({ faberAgent, aliceAgent, aliceConnection } = await setupCredentialTests( - 'Faber Agent Credentials LD BBS+', - 'Alice Agent Credentials LD BBS+' - )) - wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ keyType: KeyType.Ed25519, privateKey: seed }) - const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, seed }) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + holderIssuerConnectionId: aliceConnectionId, + } = await setupJsonLdTests({ + issuerName: 'Faber Agent Credentials LD BBS+', + holderName: 'Alice Agent Credentials LD BBS+', + })) - issuerDidKey = new DidKey(key) + await faberAgent.context.wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + }) + await faberAgent.context.wallet.createKey({ + keyType: KeyType.Bls12381g2, + seed: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + }) }) + afterAll(async () => { await faberAgent.shutdown() await faberAgent.wallet.delete() @@ -52,45 +84,8 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { test('Alice starts with V2 (ld format, BbsBlsSignature2020 signature) credential proposal to Faber', async () => { testLogger.test('Alice sends (v2 jsonld) credential proposal to Faber') - // set the propose options - - const TEST_LD_DOCUMENT: JsonCredential = { - '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://w3id.org/citizenship/v1', SECURITY_CONTEXT_BBS_URL], - id: 'https://issuer.oidp.uscis.gov/credentials/83627465', - type: ['VerifiableCredential', 'PermanentResidentCard'], - issuer: issuerDidKey.did, - issuanceDate: '2019-12-03T12:19:52Z', - expirationDate: '2029-12-03T12:19:52Z', - identifier: '83627465', - name: 'Permanent Resident Card', - credentialSubject: { - id: 'did:example:b34ca6cd37bbf23', - type: ['PermanentResident', 'Person'], - givenName: 'JOHN', - familyName: 'SMITH', - gender: 'Male', - image: 'data:image/png;base64,iVBORw0KGgokJggg==', - residentSince: '2015-01-01', - description: 'Government of Example Permanent Resident Card.', - lprCategory: 'C09', - lprNumber: '999-999-999', - commuterClassification: 'C1', - birthCountry: 'Bahamas', - birthDate: '1958-07-17', - }, - } - signCredentialOptions = { - credential: TEST_LD_DOCUMENT, - options: { - proofType: 'BbsBlsSignature2020', - proofPurpose: 'assertionMethod', - }, - } - - testLogger.test('Alice sends (v2, Indy) credential proposal to Faber') - const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { jsonld: signCredentialOptions, @@ -98,16 +93,17 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { comment: 'v2 propose credential test for W3C Credentials', }) - expect(credentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(credentialExchangeRecord.connectionId).toEqual(aliceConnectionId) expect(credentialExchangeRecord.protocolVersion).toEqual('v2') expect(credentialExchangeRecord.state).toEqual(CredentialState.ProposalSent) expect(credentialExchangeRecord.threadId).not.toBeNull() testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: credentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) + testLogger.test('Faber sends credential offer to Alice') await faberAgent.credentials.acceptProposal({ credentialRecordId: faberCredentialRecord.id, @@ -115,18 +111,12 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const offerMessage = await didCommMessageRepository.findAgentMessage(aliceAgent.context, { - associatedRecordId: aliceCredentialRecord.id, - messageClass: V2OfferCredentialMessage, - }) - + const offerMessage = await faberAgent.credentials.findOfferMessage(faberCredentialRecord.id) expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', '@id': expect.any(String), @@ -164,37 +154,32 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { expect(aliceCredentialRecord.id).not.toBeNull() expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) - if (!aliceCredentialRecord.connectionId) { - throw new Error('Missing Connection Id') - } - - const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ credentialRecordId: aliceCredentialRecord.id, credentialFormats: { jsonld: undefined, }, }) - expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnectionId) expect(offerCredentialExchangeRecord.protocolVersion).toEqual('v2') expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) expect(offerCredentialExchangeRecord.threadId).not.toBeNull() testLogger.test('Faber waits for credential request from Alice') - await waitForCredentialRecord(faberAgent, { + await waitForCredentialRecordSubject(faberReplay, { threadId: aliceCredentialRecord.threadId, state: CredentialState.RequestReceived, }) testLogger.test('Faber sends credential to Alice') - await faberAgent.credentials.acceptRequest({ credentialRecordId: faberCredentialRecord.id, comment: 'V2 W3C Offer', }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.CredentialReceived, }) @@ -203,7 +188,7 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.Done, }) @@ -216,15 +201,11 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { state: CredentialState.CredentialReceived, }) - const credentialMessage = await didCommMessageRepository.getAgentMessage(faberAgent.context, { - associatedRecordId: faberCredentialRecord.id, - messageClass: V2IssueCredentialMessage, - }) - - const w3cCredential = credentialMessage.credentialAttachments[0].getDataAsJson() + const credentialMessage = await faberAgent.credentials.findCredentialMessage(faberCredentialRecord.id) + const w3cCredential = (credentialMessage as V2IssueCredentialMessage).credentialAttachments[0].getDataAsJson() expect(w3cCredential).toMatchObject({ - context: [ + '@context': [ 'https://www.w3.org/2018/credentials/v1', 'https://w3id.org/citizenship/v1', 'https://w3id.org/security/bbs/v1', diff --git a/packages/core/package.json b/packages/core/package.json index e4295e0d2a..7b3dd8da9b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,11 +30,9 @@ "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", - "@types/indy-sdk": "1.16.26", "@types/node-fetch": "^2.5.10", "@types/ws": "^7.4.6", "abort-controller": "^3.0.0", - "bn.js": "^5.2.0", "borc": "^3.0.0", "buffer": "^6.0.3", "class-transformer": "0.5.1", @@ -59,6 +57,7 @@ "@types/object-inspect": "^1.8.0", "@types/uuid": "^8.3.0", "@types/varint": "^6.0.0", + "nock": "^13.3.0", "node-fetch": "^2.0", "rimraf": "^4.0.7", "tslog": "^3.2.0", diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 3785d00f4a..88ff3597fb 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -1,6 +1,7 @@ import type { AgentDependencies } from './AgentDependencies' import type { AgentModulesInput } from './AgentModules' import type { AgentMessageReceivedEvent } from './Events' +import type { Module } from '../plugins' import type { InboundTransport } from '../transport/InboundTransport' import type { OutboundTransport } from '../transport/OutboundTransport' import type { InitConfig } from '../types' @@ -16,8 +17,6 @@ import { AriesFrameworkError } from '../error' import { DependencyManager } from '../plugins' import { DidCommMessageRepository, StorageUpdateService, StorageVersionRepository } from '../storage' import { InMemoryMessageRepository } from '../storage/InMemoryMessageRepository' -import { IndyStorageService } from '../storage/IndyStorageService' -import { IndyWallet } from '../wallet/IndyWallet' import { AgentConfig } from './AgentConfig' import { extendModulesWithDefaultModules } from './AgentModules' @@ -79,13 +78,17 @@ export class Agent extends BaseAge // Register possibly already defined services if (!dependencyManager.isRegistered(InjectionSymbols.Wallet)) { - dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndyWallet) + throw new AriesFrameworkError( + "Missing required dependency: 'Wallet'. You can register it using one of the provided modules such as the AskarModule or the IndySdkModule, or implement your own." + ) } if (!dependencyManager.isRegistered(InjectionSymbols.Logger)) { dependencyManager.registerInstance(InjectionSymbols.Logger, agentConfig.logger) } if (!dependencyManager.isRegistered(InjectionSymbols.StorageService)) { - dependencyManager.registerSingleton(InjectionSymbols.StorageService, IndyStorageService) + throw new AriesFrameworkError( + "Missing required dependency: 'StorageService'. You can register it using one of the provided modules such as the AskarModule or the IndySdkModule, or implement your own." + ) } if (!dependencyManager.isRegistered(InjectionSymbols.MessageRepository)) { dependencyManager.registerSingleton(InjectionSymbols.MessageRepository, InMemoryMessageRepository) @@ -159,13 +162,10 @@ export class Agent extends BaseAge public async initialize() { await super.initialize() - // set the pools on the ledger. - this.ledger.setPools(this.ledger.config.indyLedgers) - // As long as value isn't false we will async connect to all genesis pools on startup - if (this.ledger.config.connectToIndyLedgersOnStartup) { - this.ledger.connectToPools().catch((error) => { - this.logger.warn('Error connecting to ledger, will try to reconnect when needed.', { error }) - }) + for (const [, module] of Object.entries(this.dependencyManager.registeredModules) as [string, Module][]) { + if (module.initialize) { + await module.initialize(this.agentContext) + } } for (const transport of this.inboundTransports) { diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index a2df97a94c..09f348652d 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -33,13 +33,6 @@ export class AgentConfig { } } - /** - * @deprecated use connectToIndyLedgersOnStartup from the `LedgerModuleConfig` class - */ - public get connectToIndyLedgersOnStartup() { - return this.initConfig.connectToIndyLedgersOnStartup ?? true - } - /** * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but @@ -50,13 +43,6 @@ export class AgentConfig { return this.initConfig.publicDidSeed } - /** - * @deprecated use indyLedgers from the `LedgerModuleConfig` class - */ - public get indyLedgers() { - return this.initConfig.indyLedgers ?? [] - } - /** * @todo move to context configuration */ diff --git a/packages/core/src/agent/AgentDependencies.ts b/packages/core/src/agent/AgentDependencies.ts index 1aa681645d..be1146e818 100644 --- a/packages/core/src/agent/AgentDependencies.ts +++ b/packages/core/src/agent/AgentDependencies.ts @@ -1,6 +1,5 @@ import type { FileSystem } from '../storage/FileSystem' import type { EventEmitter } from 'events' -import type * as Indy from 'indy-sdk' import type fetch from 'node-fetch' import type WebSocket from 'ws' @@ -8,7 +7,6 @@ export interface AgentDependencies { FileSystem: { new (): FileSystem } - indy: typeof Indy EventEmitterClass: typeof EventEmitter fetch: typeof fetch WebSocketClass: typeof WebSocket diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 20f0d6cbb7..aa3ab239c6 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -10,8 +10,6 @@ import { CredentialsModule } from '../modules/credentials' import { DidsModule } from '../modules/dids' import { DiscoverFeaturesModule } from '../modules/discover-features' import { GenericRecordsModule } from '../modules/generic-records' -import { IndyModule } from '../modules/indy' -import { LedgerModule } from '../modules/ledger' import { OutOfBandModule } from '../modules/oob' import { ProofsModule } from '../modules/proofs' import { MediatorModule, RecipientModule } from '../modules/routing' @@ -149,16 +147,10 @@ function getDefaultAgentModules(agentConfig: AgentConfig) { }), basicMessages: () => new BasicMessagesModule(), genericRecords: () => new GenericRecordsModule(), - ledger: () => - new LedgerModule({ - connectToIndyLedgersOnStartup: agentConfig.connectToIndyLedgersOnStartup, - indyLedgers: agentConfig.indyLedgers, - }), discovery: () => new DiscoverFeaturesModule(), dids: () => new DidsModule(), wallet: () => new WalletModule(), oob: () => new OutOfBandModule(), - indy: () => new IndyModule(), w3cVc: () => new W3cVcModule(), cache: () => new CacheModule(), } as const diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 704b2b5d98..e5e59bd7f4 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -13,7 +13,6 @@ import { CredentialsApi } from '../modules/credentials' import { DidsApi } from '../modules/dids' import { DiscoverFeaturesApi } from '../modules/discover-features' import { GenericRecordsApi } from '../modules/generic-records' -import { LedgerApi } from '../modules/ledger' import { OutOfBandApi } from '../modules/oob' import { ProofsApi } from '../modules/proofs' import { MediatorApi, RecipientApi } from '../modules/routing' @@ -50,7 +49,6 @@ export abstract class BaseAgent { ...agentOptions, modules: { myModule: new MyModule(), + ...getIndySdkModules(), }, }) @@ -79,6 +78,7 @@ describe('Agent', () => { mediationRecipient: new RecipientModule({ maximumMessagePickup: 42, }), + ...getIndySdkModules(), }, }) @@ -175,13 +175,9 @@ describe('Agent', () => { expect(container.resolve(MediatorService)).toBeInstanceOf(MediatorService) expect(container.resolve(MediationRecipientService)).toBeInstanceOf(MediationRecipientService) - expect(container.resolve(LedgerApi)).toBeInstanceOf(LedgerApi) - expect(container.resolve(IndyLedgerService)).toBeInstanceOf(IndyLedgerService) - // Symbols, interface based expect(container.resolve(InjectionSymbols.Logger)).toBe(agentOptions.config.logger) expect(container.resolve(InjectionSymbols.MessageRepository)).toBeInstanceOf(InMemoryMessageRepository) - expect(container.resolve(InjectionSymbols.StorageService)).toBeInstanceOf(IndyStorageService) // Agent expect(container.resolve(MessageSender)).toBeInstanceOf(MessageSender) @@ -216,9 +212,6 @@ describe('Agent', () => { expect(container.resolve(MediatorService)).toBe(container.resolve(MediatorService)) expect(container.resolve(MediationRecipientService)).toBe(container.resolve(MediationRecipientService)) - expect(container.resolve(LedgerApi)).toBe(container.resolve(LedgerApi)) - expect(container.resolve(IndyLedgerService)).toBe(container.resolve(IndyLedgerService)) - // Symbols, interface based expect(container.resolve(InjectionSymbols.Logger)).toBe(container.resolve(InjectionSymbols.Logger)) expect(container.resolve(InjectionSymbols.MessageRepository)).toBe( @@ -248,20 +241,18 @@ describe('Agent', () => { 'https://didcomm.org/basicmessage/1.0', 'https://didcomm.org/connections/1.0', 'https://didcomm.org/coordinate-mediation/1.0', + 'https://didcomm.org/issue-credential/2.0', + 'https://didcomm.org/present-proof/2.0', 'https://didcomm.org/didexchange/1.0', 'https://didcomm.org/discover-features/1.0', 'https://didcomm.org/discover-features/2.0', - 'https://didcomm.org/issue-credential/1.0', - 'https://didcomm.org/issue-credential/2.0', 'https://didcomm.org/messagepickup/1.0', 'https://didcomm.org/messagepickup/2.0', 'https://didcomm.org/out-of-band/1.1', - 'https://didcomm.org/present-proof/1.0', - 'https://didcomm.org/present-proof/2.0', 'https://didcomm.org/revocation_notification/1.0', 'https://didcomm.org/revocation_notification/2.0', ]) ) - expect(protocols.length).toEqual(15) + expect(protocols.length).toEqual(13) }) }) diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts index 60755c487e..7ee76dfe6f 100644 --- a/packages/core/src/agent/__tests__/AgentModules.test.ts +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -8,8 +8,6 @@ import { CredentialsModule } from '../../modules/credentials' import { DidsModule } from '../../modules/dids' import { DiscoverFeaturesModule } from '../../modules/discover-features' import { GenericRecordsModule } from '../../modules/generic-records' -import { IndyModule } from '../../modules/indy' -import { LedgerModule } from '../../modules/ledger' import { OutOfBandModule } from '../../modules/oob' import { ProofsModule } from '../../modules/proofs' import { MediatorModule, RecipientModule } from '../../modules/routing' @@ -66,12 +64,10 @@ describe('AgentModules', () => { mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), - ledger: expect.any(LedgerModule), discovery: expect.any(DiscoverFeaturesModule), dids: expect.any(DidsModule), wallet: expect.any(WalletModule), oob: expect.any(OutOfBandModule), - indy: expect.any(IndyModule), w3cVc: expect.any(W3cVcModule), cache: expect.any(CacheModule), }) @@ -91,12 +87,10 @@ describe('AgentModules', () => { mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), - ledger: expect.any(LedgerModule), discovery: expect.any(DiscoverFeaturesModule), dids: expect.any(DidsModule), wallet: expect.any(WalletModule), oob: expect.any(OutOfBandModule), - indy: expect.any(IndyModule), w3cVc: expect.any(W3cVcModule), cache: expect.any(CacheModule), myModule, @@ -119,12 +113,10 @@ describe('AgentModules', () => { mediationRecipient: expect.any(RecipientModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), - ledger: expect.any(LedgerModule), discovery: expect.any(DiscoverFeaturesModule), dids: expect.any(DidsModule), wallet: expect.any(WalletModule), oob: expect.any(OutOfBandModule), - indy: expect.any(IndyModule), w3cVc: expect.any(W3cVcModule), cache: expect.any(CacheModule), myModule, diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index 4080ab2f24..15dc242f9b 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -1,10 +1,11 @@ import type { AgentContext } from '../../agent' import type { Key, Wallet } from '@aries-framework/core' +import { IndySdkWallet } from '../../../../indy-sdk/src' +import { indySdk } from '../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentConfig, getAgentContext } from '../../../tests/helpers' import { DidKey } from '../../modules/dids' import { Buffer, JsonEncoder, TypedArrayEncoder } from '../../utils' -import { IndyWallet } from '../../wallet/IndyWallet' import { JwsService } from '../JwsService' import { KeyType } from '../KeyType' import { SigningProviderRegistry } from '../signing-provider' @@ -20,12 +21,12 @@ describe('JwsService', () => { let didJwsz6MkvKey: Key beforeAll(async () => { const config = getAgentConfig('JwsService') - wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) + // TODO: update to InMemoryWallet + wallet = new IndySdkWallet(indySdk, config.logger, new SigningProviderRegistry([])) agentContext = getAgentContext({ wallet, }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) + await wallet.createAndOpen(config.walletConfig) jwsService = new JwsService() didJwsz6MkfKey = await wallet.createKey({ diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index 5336162c59..93d3610f29 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -1,8 +1,11 @@ +import type { Wallet } from '../../wallet' + +import { IndySdkWallet } from '../../../../indy-sdk/src' +import { indySdk } from '../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentConfig } from '../../../tests/helpers' import { KeyType } from '../../crypto' import { SigningProviderRegistry } from '../../crypto/signing-provider' import { TypedArrayEncoder } from '../../utils' -import { IndyWallet } from '../../wallet/IndyWallet' import { SignatureDecorator } from './SignatureDecorator' import { signData, unpackAndVerifySignatureDecorator } from './SignatureDecoratorUtils' @@ -40,11 +43,11 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { signer: 'GjZWsBLgZCR18aL468JAT7w9CZRiBnpxUPPgyQxh4voa', }) - let wallet: IndyWallet + let wallet: Wallet beforeAll(async () => { const config = getAgentConfig('SignatureDecoratorUtilsTest') - wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) + wallet = new IndySdkWallet(indySdk, config.logger, new SigningProviderRegistry([])) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 38f4bc2fa8..3ffd7de1f4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -31,6 +31,7 @@ export type { export { DidCommMimeType, KeyDerivationMethod } from './types' export type { FileSystem, DownloadToFileOptions } from './storage/FileSystem' export * from './storage/BaseRecord' +export { DidCommMessageRecord, DidCommMessageRole, DidCommMessageRepository } from './storage/didcomm' export { InMemoryMessageRepository } from './storage/InMemoryMessageRepository' export { Repository } from './storage/Repository' export * from './storage/RepositoryEvents' @@ -42,6 +43,7 @@ export * from './wallet' export type { TransportSession } from './agent/TransportService' export { TransportService } from './agent/TransportService' export { Attachment, AttachmentData } from './decorators/attachment/Attachment' +export { ServiceDecorator, ServiceDecoratorOptions } from './decorators/service/ServiceDecorator' export { ReturnRouteTypes } from './decorators/transport/TransportDecorator' export * from './plugins' @@ -53,7 +55,6 @@ export * from './modules/discover-features' export * from './modules/problem-reports' export * from './modules/proofs' export * from './modules/connections' -export * from './modules/ledger' export * from './modules/routing' export * from './modules/oob' export * from './modules/dids' @@ -63,12 +64,13 @@ export { JsonEncoder, JsonTransformer, isJsonObject, isValidJweStructure, TypedA export * from './logger' export * from './error' export * from './wallet/error' -export { parseMessageType, IsValidMessageType } from './utils/messageType' +export { parseMessageType, IsValidMessageType, replaceLegacyDidSovPrefix } from './utils/messageType' export type { Constructor } from './utils/mixins' export * from './agent/Events' export * from './crypto/' -export { encodeAttachment } from './utils/attachment' +// TODO: clean up util exports +export { encodeAttachment, isLinkedAttachment } from './utils/attachment' export { Hasher } from './utils/Hasher' export { MessageValidator } from './utils/MessageValidator' export { LinkedAttachment, LinkedAttachmentOptions } from './utils/LinkedAttachment' diff --git a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts index e4e2d0dd17..1ed5be6fb7 100644 --- a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts @@ -6,6 +6,7 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions, makeConnection, waitForBasicMessage } from '../../../../tests/helpers' import testLogger from '../../../../tests/logger' import { Agent } from '../../../agent/Agent' @@ -13,13 +14,21 @@ import { MessageSendingError, RecordNotFoundError } from '../../../error' import { BasicMessage } from '../messages' import { BasicMessageRecord } from '../repository' -const faberConfig = getAgentOptions('Faber Basic Messages', { - endpoints: ['rxjs:faber'], -}) - -const aliceConfig = getAgentOptions('Alice Basic Messages', { - endpoints: ['rxjs:alice'], -}) +const faberConfig = getAgentOptions( + 'Faber Basic Messages', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() +) + +const aliceConfig = getAgentOptions( + 'Alice Basic Messages', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) describe('Basic Messages E2E', () => { let faberAgent: Agent diff --git a/packages/core/src/modules/cache/InMemoryLruCache.ts b/packages/core/src/modules/cache/InMemoryLruCache.ts index 4a56cb97c5..4f6ba0733e 100644 --- a/packages/core/src/modules/cache/InMemoryLruCache.ts +++ b/packages/core/src/modules/cache/InMemoryLruCache.ts @@ -51,6 +51,10 @@ export class InMemoryLruCache implements Cache { }) } + public clear() { + this.cache.clear() + } + public async remove(agentContext: AgentContext, key: string): Promise { this.removeExpiredItems() this.cache.delete(key) diff --git a/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts b/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts index 0016ed3d8e..257b6b6080 100644 --- a/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts +++ b/packages/core/src/modules/cache/singleContextLruCache/SingleContextLruCacheRecord.ts @@ -1,5 +1,7 @@ import type { TagsBase } from '../../../storage/BaseRecord' +import { Type } from 'class-transformer' + import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' @@ -17,6 +19,7 @@ export interface SingleContextLruCacheProps { } export class SingleContextLruCacheRecord extends BaseRecord { + @Type(() => Object) public entries!: Map public static readonly type = 'SingleContextLruCacheRecord' diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index 9cc403ecba..c07c94a893 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -5,6 +5,8 @@ import type { Routing } from '../services/ConnectionService' import { Subject } from 'rxjs' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentConfig, getAgentContext, @@ -21,7 +23,6 @@ import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators import { JsonTransformer } from '../../../utils/JsonTransformer' import { indyDidFromPublicKeyBase58 } from '../../../utils/did' import { uuid } from '../../../utils/uuid' -import { IndyWallet } from '../../../wallet/IndyWallet' import { AckMessage, AckStatus } from '../../common' import { DidKey, IndyAgentService } from '../../dids' import { DidDocumentRole } from '../../dids/domain/DidDocumentRole' @@ -81,10 +82,9 @@ describe('ConnectionService', () => { let agentContext: AgentContext beforeAll(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) + wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) agentContext = getAgentContext({ wallet, agentConfig }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) + await wallet.createAndOpen(agentConfig.walletConfig) }) afterAll(async () => { diff --git a/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts b/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts index dc07e9639f..457e5f7b7e 100644 --- a/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts +++ b/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts @@ -4,8 +4,8 @@ import type { ConnectionStateChangedEvent } from '../ConnectionEvents' import { firstValueFrom } from 'rxjs' import { filter, first, map, timeout } from 'rxjs/operators' -import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' +import { setupSubjectTransports } from '../../../../tests' import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { ConnectionEventTypes } from '../ConnectionEvents' @@ -45,42 +45,38 @@ describe('Manual Connection Flow', () => { // This test was added to reproduce a bug where all connections based on a reusable invitation would use the same keys // This was only present in the manual flow, which is almost never used. it('can connect multiple times using the same reusable invitation without manually using the connections api', async () => { - const aliceInboundTransport = new SubjectInboundTransport() - const bobInboundTransport = new SubjectInboundTransport() - const faberInboundTransport = new SubjectInboundTransport() - - const subjectMap = { - 'rxjs:faber': faberInboundTransport.ourSubject, - 'rxjs:alice': aliceInboundTransport.ourSubject, - 'rxjs:bob': bobInboundTransport.ourSubject, - } - const aliceAgentOptions = getAgentOptions('Manual Connection Flow Alice', { - label: 'alice', - autoAcceptConnections: false, - endpoints: ['rxjs:alice'], - }) - const bobAgentOptions = getAgentOptions('Manual Connection Flow Bob', { - label: 'bob', - autoAcceptConnections: false, - endpoints: ['rxjs:bob'], - }) - const faberAgentOptions = getAgentOptions('Manual Connection Flow Faber', { - autoAcceptConnections: false, - endpoints: ['rxjs:faber'], - }) + const aliceAgentOptions = getAgentOptions( + 'Manual Connection Flow Alice', + { + label: 'alice', + autoAcceptConnections: false, + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() + ) + const bobAgentOptions = getAgentOptions( + 'Manual Connection Flow Bob', + { + label: 'bob', + autoAcceptConnections: false, + endpoints: ['rxjs:bob'], + }, + getIndySdkModules() + ) + const faberAgentOptions = getAgentOptions( + 'Manual Connection Flow Faber', + { + autoAcceptConnections: false, + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() + ) const aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(aliceInboundTransport) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - const bobAgent = new Agent(bobAgentOptions) - bobAgent.registerInboundTransport(bobInboundTransport) - bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - const faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(faberInboundTransport) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + setupSubjectTransports([aliceAgent, bobAgent, faberAgent]) await aliceAgent.initialize() await bobAgent.initialize() await faberAgent.initialize() diff --git a/packages/core/src/modules/credentials/CredentialsModule.ts b/packages/core/src/modules/credentials/CredentialsModule.ts index a7d762e248..9cae75eb28 100644 --- a/packages/core/src/modules/credentials/CredentialsModule.ts +++ b/packages/core/src/modules/credentials/CredentialsModule.ts @@ -9,16 +9,14 @@ import { Protocol } from '../../agent/models' import { CredentialsApi } from './CredentialsApi' import { CredentialsModuleConfig } from './CredentialsModuleConfig' -import { IndyCredentialFormatService } from './formats/indy' import { RevocationNotificationService } from './protocol/revocation-notification/services' -import { V1CredentialProtocol } from './protocol/v1' import { V2CredentialProtocol } from './protocol/v2' import { CredentialRepository } from './repository' /** * Default credentialProtocols that will be registered if the `credentialProtocols` property is not configured. */ -export type DefaultCredentialProtocols = [V1CredentialProtocol, V2CredentialProtocol] +export type DefaultCredentialProtocols = [] // CredentialsModuleOptions makes the credentialProtocols property optional from the config, as it will set it when not provided. export type CredentialsModuleOptions = Optional< @@ -39,26 +37,10 @@ export class CredentialsModule } - /** - * Get the default credential protocols that will be registered if the `credentialProtocols` property is not configured. - */ - private getDefaultCredentialProtocols(): DefaultCredentialProtocols { - // Instantiate credential formats - const indyCredentialFormat = new IndyCredentialFormatService() - - // Instantiate credential protocols - const v1CredentialProtocol = new V1CredentialProtocol({ indyCredentialFormat }) - const v2CredentialProtocol = new V2CredentialProtocol({ - credentialFormats: [indyCredentialFormat], - }) - - return [v1CredentialProtocol, v2CredentialProtocol] - } - /** * Registers the dependencies of the credentials module on the dependency manager. */ diff --git a/packages/core/src/modules/credentials/CredentialsModuleConfig.ts b/packages/core/src/modules/credentials/CredentialsModuleConfig.ts index 34c20f50e7..e6d23909ed 100644 --- a/packages/core/src/modules/credentials/CredentialsModuleConfig.ts +++ b/packages/core/src/modules/credentials/CredentialsModuleConfig.ts @@ -18,11 +18,11 @@ export interface CredentialsModuleConfigOptions { ) }) - test('registers V1CredentialProtocol and V2CredentialProtocol if no credentialProtocols are configured', () => { + test('registers V2CredentialProtocol if no credentialProtocols are configured', () => { const credentialsModule = new CredentialsModule() - expect(credentialsModule.config.credentialProtocols).toEqual([ - expect.any(V1CredentialProtocol), - expect.any(V2CredentialProtocol), - ]) + expect(credentialsModule.config.credentialProtocols).toEqual([expect.any(V2CredentialProtocol)]) }) test('calls register on the provided CredentialProtocols', () => { diff --git a/packages/core/src/modules/credentials/__tests__/fixtures.ts b/packages/core/src/modules/credentials/__tests__/fixtures.ts index b8e5e7451c..057ae8d4e1 100644 --- a/packages/core/src/modules/credentials/__tests__/fixtures.ts +++ b/packages/core/src/modules/credentials/__tests__/fixtures.ts @@ -1,35 +1,3 @@ -import type { Schema } from 'indy-sdk' - -export const credDef = { - ver: '1.0', - id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:16:TAG', - schemaId: '16', - type: 'CL', - tag: 'TAG', - value: { - primary: { - n: '92498022445845202032348897620554299694896009176315493627722439892023558526259875239808280186111059586069456394012963552956574651629517633396592827947162983189649269173220440607665417484696688946624963596710652063849006738050417440697782608643095591808084344059908523401576738321329706597491345875134180790935098782801918369980296355919072827164363500681884641551147645504164254206270541724042784184712124576190438261715948768681331862924634233043594086219221089373455065715714369325926959533971768008691000560918594972006312159600845441063618991760512232714992293187779673708252226326233136573974603552763615191259713', - s: '10526250116244590830801226936689232818708299684432892622156345407187391699799320507237066062806731083222465421809988887959680863378202697458984451550048737847231343182195679453915452156726746705017249911605739136361885518044604626564286545453132948801604882107628140153824106426249153436206037648809856342458324897885659120708767794055147846459394129610878181859361616754832462886951623882371283575513182530118220334228417923423365966593298195040550255217053655606887026300020680355874881473255854564974899509540795154002250551880061649183753819902391970912501350100175974791776321455551753882483918632271326727061054', - r: [Object], - rctxt: - '46370806529776888197599056685386177334629311939451963919411093310852010284763705864375085256873240323432329015015526097014834809926159013231804170844321552080493355339505872140068998254185756917091385820365193200970156007391350745837300010513687490459142965515562285631984769068796922482977754955668569724352923519618227464510753980134744424528043503232724934196990461197793822566137436901258663918660818511283047475389958180983391173176526879694302021471636017119966755980327241734084462963412467297412455580500138233383229217300797768907396564522366006433982511590491966618857814545264741708965590546773466047139517', - z: '84153935869396527029518633753040092509512111365149323230260584738724940130382637900926220255597132853379358675015222072417404334537543844616589463419189203852221375511010886284448841979468767444910003114007224993233448170299654815710399828255375084265247114471334540928216537567325499206413940771681156686116516158907421215752364889506967984343660576422672840921988126699885304325384925457260272972771547695861942114712679509318179363715259460727275178310181122162544785290813713205047589943947592273130618286905125194410421355167030389500160371886870735704712739886223342214864760968555566496288314800410716250791012', - }, - }, -} - -export const credOffer = { - schema_id: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', - cred_def_id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:49:TAG', - key_correctness_proof: { - c: '50047550092211803100898435599448498249230644214602846259465380105187911562981', - xz_cap: - '903377919969858361861015636539761203188657065139923565169527138921408162179186528356880386741834936511828233627399006489728775544195659624738894378139967421189010372215352983118513580084886680005590351907106638703178655817619548698392274394080197104513101326422946899502782963819178061725651195158952405559244837834363357514238035344644245428381747318500206935512140018411279271654056625228252895211750431161165113594675112781707690650346028518711572046490157895995321932792559036799731075010805676081761818738662133557673397343395090042309895292970880031625026873886199268438633391631171327618951514526941153292890331525143330509967786605076984412387036942171388655140446222693051734534012842', - xr_cap: [[], [], []], - }, - nonce: '947121108704767252195123', -} - export const credReq = { prover_did: 'did:sov:Y8iyDrCHfUpBY2jkd7Utfx', cred_def_id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:51:TAG', @@ -51,12 +19,3 @@ export const credReq = { }, nonce: '784158051402761459123237', } - -export const schema: Schema = { - name: 'schema', - attrNames: ['name', 'age'], - id: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', - seqNo: 989798923653, - ver: '1.0', - version: '1.0', -} diff --git a/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts b/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts deleted file mode 100644 index 41a5ed808b..0000000000 --- a/packages/core/src/modules/credentials/errors/CredentialProblemReportError.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { CredentialProblemReportReason } from './CredentialProblemReportReason' -import type { ProblemReportErrorOptions } from '../../problem-reports' - -import { V1CredentialProblemReportMessage } from '../protocol/v1/messages' - -import { ProblemReportError } from './../../problem-reports/errors/ProblemReportError' - -interface CredentialProblemReportErrorOptions extends ProblemReportErrorOptions { - problemCode: CredentialProblemReportReason -} -export class CredentialProblemReportError extends ProblemReportError { - public problemReport: V1CredentialProblemReportMessage - - public constructor(message: string, { problemCode }: CredentialProblemReportErrorOptions) { - super(message, { problemCode }) - this.problemReport = new V1CredentialProblemReportMessage({ - description: { - en: message, - code: problemCode, - }, - }) - } -} diff --git a/packages/core/src/modules/credentials/errors/index.ts b/packages/core/src/modules/credentials/errors/index.ts deleted file mode 100644 index 3d5c266524..0000000000 --- a/packages/core/src/modules/credentials/errors/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './CredentialProblemReportError' -export * from './CredentialProblemReportReason' diff --git a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts deleted file mode 100644 index bbe6f379f8..0000000000 --- a/packages/core/src/modules/credentials/formats/__tests__/IndyCredentialFormatService.test.ts +++ /dev/null @@ -1,443 +0,0 @@ -import type { AgentContext } from '../../../../agent' -import type { AgentConfig } from '../../../../agent/AgentConfig' -import type { ParseRevocationRegistryDefinitionTemplate } from '../../../ledger/services/IndyLedgerService' -import type { CredentialFormatService } from '../../formats' -import type { IndyCredentialFormat } from '../../formats/indy/IndyCredentialFormat' -import type { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' -import type { V2OfferCredentialMessageOptions } from '../../protocol/v2/messages/V2OfferCredentialMessage' -import type { CustomCredentialTags } from '../../repository/CredentialExchangeRecord' -import type { RevocRegDef } from 'indy-sdk' - -import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' -import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' -import { JsonEncoder } from '../../../../utils/JsonEncoder' -import { ConnectionService } from '../../../connections/services/ConnectionService' -import { DidResolverService } from '../../../dids/services/DidResolverService' -import { IndyHolderService } from '../../../indy/services/IndyHolderService' -import { IndyIssuerService } from '../../../indy/services/IndyIssuerService' -import { IndyLedgerService } from '../../../ledger/services/IndyLedgerService' -import { credDef, credReq, schema } from '../../__tests__/fixtures' -import { IndyCredentialFormatService } from '../../formats' -import { IndyCredentialUtils } from '../../formats/indy/IndyCredentialUtils' -import { CredentialState } from '../../models' -import { - INDY_CREDENTIAL_ATTACHMENT_ID, - INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, -} from '../../protocol/v1/messages' -import { V2CredentialPreview } from '../../protocol/v2/messages' -import { V2OfferCredentialMessage } from '../../protocol/v2/messages/V2OfferCredentialMessage' -import { CredentialMetadataKeys } from '../../repository' -import { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' - -jest.mock('../../../../modules/ledger/services/IndyLedgerService') -jest.mock('../../../indy/services/IndyHolderService') -jest.mock('../../../indy/services/IndyIssuerService') -jest.mock('../../../dids/services/DidResolverService') -jest.mock('../../../connections/services/ConnectionService') - -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const IndyHolderServiceMock = IndyHolderService as jest.Mock -const IndyIssuerServiceMock = IndyIssuerService as jest.Mock -const ConnectionServiceMock = ConnectionService as jest.Mock -const DidResolverServiceMock = DidResolverService as jest.Mock - -const values = { - x: { - raw: 'x', - encoded: 'y', - }, -} -const cred = { - schema_id: 'xsxs', - cred_def_id: 'xdxd', - rev_reg_id: 'x', - values: values, - signature: undefined, - signature_correctness_proof: undefined, -} - -const revDef: RevocRegDef = { - id: 'x', - revocDefType: 'CL_ACCUM', - tag: 'x', - credDefId: 'x', - value: { - issuanceType: 'ISSUANCE_BY_DEFAULT', - maxCredNum: 33, - tailsHash: 'd', - tailsLocation: 'x', - publicKeys: { - accumKey: { - z: 'x', - }, - }, - }, - ver: 't', -} - -const revocationTemplate: ParseRevocationRegistryDefinitionTemplate = { - revocationRegistryDefinition: revDef, - revocationRegistryDefinitionTxnTime: 42, -} - -const credentialPreview = V2CredentialPreview.fromRecord({ - name: 'John', - age: '99', -}) - -const offerAttachment = new Attachment({ - id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: - 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', - }), -}) - -const requestAttachment = new Attachment({ - id: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(credReq), - }), -}) - -const credentialAttachment = new Attachment({ - id: INDY_CREDENTIAL_ATTACHMENT_ID, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64({ - values: IndyCredentialUtils.convertAttributesToValues(credentialPreview.attributes), - }), - }), -}) - -// A record is deserialized to JSON when it's stored into the storage. We want to simulate this behaviour for `offer` -// object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. -const mockCredentialRecord = ({ - state, - metadata, - threadId, - connectionId, - tags, - id, - credentialAttributes, -}: { - state?: CredentialState - metadata?: { indyRequest: Record } - tags?: CustomCredentialTags - threadId?: string - connectionId?: string - id?: string - credentialAttributes?: CredentialPreviewAttribute[] -} = {}) => { - const offerOptions: V2OfferCredentialMessageOptions = { - id: '', - formats: [ - { - attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - format: 'hlindy/cred-abstract@v2.0', - }, - ], - comment: 'some comment', - credentialPreview: credentialPreview, - offerAttachments: [offerAttachment], - replacementId: undefined, - } - const offerMessage = new V2OfferCredentialMessage(offerOptions) - - const credentialRecord = new CredentialExchangeRecord({ - id, - credentialAttributes: credentialAttributes || credentialPreview.attributes, - state: state || CredentialState.OfferSent, - threadId: threadId ?? offerMessage.id, - connectionId: connectionId ?? '123', - tags, - protocolVersion: 'v2', - }) - - if (metadata?.indyRequest) { - credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, { ...metadata.indyRequest }) - } - - return credentialRecord -} -let indyFormatService: CredentialFormatService -let indyLedgerService: IndyLedgerService -let indyIssuerService: IndyIssuerService -let indyHolderService: IndyHolderService -let didResolverService: DidResolverService -let connectionService: ConnectionService -let agentConfig: AgentConfig -let credentialRecord: CredentialExchangeRecord - -describe('Indy CredentialFormatService', () => { - let agentContext: AgentContext - beforeEach(async () => { - indyIssuerService = new IndyIssuerServiceMock() - indyHolderService = new IndyHolderServiceMock() - indyLedgerService = new IndyLedgerServiceMock() - didResolverService = new DidResolverServiceMock() - connectionService = new ConnectionServiceMock() - - agentConfig = getAgentConfig('IndyCredentialFormatServiceTest') - agentContext = getAgentContext({ - registerInstances: [ - [IndyIssuerService, indyIssuerService], - [IndyHolderService, indyHolderService], - [IndyLedgerService, indyLedgerService], - [DidResolverService, didResolverService], - [ConnectionService, connectionService], - ], - agentConfig, - }) - - indyFormatService = new IndyCredentialFormatService() - - mockFunction(indyLedgerService.getSchema).mockReturnValue(Promise.resolve(schema)) - }) - - describe('Create Credential Proposal / Offer', () => { - test(`Creates Credential Proposal`, async () => { - // when - const { attachment, previewAttributes, format } = await indyFormatService.createProposal(agentContext, { - credentialRecord: mockCredentialRecord(), - credentialFormats: { - indy: { - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - schemaName: 'ahoy', - schemaVersion: '1.0', - schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - attributes: credentialPreview.attributes, - }, - }, - }) - - // then - expect(attachment).toMatchObject({ - id: expect.any(String), - description: undefined, - filename: undefined, - mimeType: 'application/json', - lastmodTime: undefined, - byteCount: undefined, - data: { - base64: - 'eyJzY2hlbWFfaXNzdWVyX2RpZCI6IkdNbTR2TXc4TExyTEpqcDgxa1JSTHAiLCJzY2hlbWFfaWQiOiJxN0FUd1RZYlFEZ2lpZ1ZpalVBZWo6Mjp0ZXN0OjEuMCIsInNjaGVtYV9uYW1lIjoiYWhveSIsInNjaGVtYV92ZXJzaW9uIjoiMS4wIiwiY3JlZF9kZWZfaWQiOiJUaDdNcFRhUlpWUlluUGlhYmRzODFZOjM6Q0w6MTc6VEFHIiwiaXNzdWVyX2RpZCI6IkdNbTR2TXc4TExyTEpqcDgxa1JSTHAifQ==', - json: undefined, - links: undefined, - jws: undefined, - sha256: undefined, - }, - }) - - expect(previewAttributes).toMatchObject([ - { - mimeType: 'text/plain', - name: 'name', - value: 'John', - }, - { - mimeType: 'text/plain', - name: 'age', - value: '99', - }, - ]) - - expect(format).toMatchObject({ - attachmentId: expect.any(String), - format: 'hlindy/cred-filter@v2.0', - }) - }) - - test(`Creates Credential Offer`, async () => { - // when - const { attachment, previewAttributes, format } = await indyFormatService.createOffer(agentContext, { - credentialRecord: mockCredentialRecord(), - credentialFormats: { - indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - }, - }, - }) - - // then - expect(indyIssuerService.createCredentialOffer).toHaveBeenCalledTimes(1) - - expect(attachment).toMatchObject({ - id: expect.any(String), - description: undefined, - filename: undefined, - mimeType: 'application/json', - lastmodTime: undefined, - byteCount: undefined, - data: { - base64: - 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0=', - json: undefined, - links: undefined, - jws: undefined, - sha256: undefined, - }, - }) - - expect(previewAttributes).toMatchObject([ - { - mimeType: 'text/plain', - name: 'name', - value: 'John', - }, - { - mimeType: 'text/plain', - name: 'age', - value: '99', - }, - ]) - - expect(format).toMatchObject({ - attachmentId: expect.any(String), - format: 'hlindy/cred-abstract@v2.0', - }) - }) - }) - describe('Process Credential Offer', () => { - test(`processes credential offer - returns modified credential record (adds metadata)`, async () => { - // given - const credentialRecord = mockCredentialRecord({ - state: CredentialState.OfferReceived, - threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', - connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', - }) - - // when - await indyFormatService.processOffer(agentContext, { attachment: offerAttachment, credentialRecord }) - }) - }) - - describe('Create Credential Request', () => { - test('returns credential request message base on existing credential offer message', async () => { - // given - const credentialRecord = mockCredentialRecord({ - state: CredentialState.OfferReceived, - threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', - connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', - }) - - mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) - - // when - const { format, attachment } = await indyFormatService.acceptOffer(agentContext, { - credentialRecord, - credentialFormats: { - indy: { - holderDid: 'holderDid', - }, - }, - offerAttachment, - }) - - // then - expect(indyHolderService.createCredentialRequest).toHaveBeenCalledTimes(1) - - expect(attachment).toMatchObject({ - id: expect.any(String), - description: undefined, - filename: undefined, - mimeType: 'application/json', - lastmodTime: undefined, - byteCount: undefined, - data: { - base64: - 'eyJwcm92ZXJfZGlkIjoiaG9sZGVyRGlkIiwiY3JlZF9kZWZfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjM6Q0w6MTY6VEFHIiwiYmxpbmRlZF9tcyI6e30sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnt9LCJub25jZSI6Im5vbmNlIn0=', - json: undefined, - links: undefined, - jws: undefined, - sha256: undefined, - }, - }) - expect(format).toMatchObject({ - attachmentId: expect.any(String), - format: 'hlindy/cred-req@v2.0', - }) - - const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyCredential) - - expect(credentialRequestMetadata?.schemaId).toBe('aaa') - expect(credentialRequestMetadata?.credentialDefinitionId).toBe('Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG') - }) - }) - - describe('Accept request', () => { - test('Creates a credentials', async () => { - // given - const credentialRecord = mockCredentialRecord({ - state: CredentialState.RequestReceived, - threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', - connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', - }) - - mockFunction(indyIssuerService.createCredential).mockReturnValue(Promise.resolve([cred, 'x'])) - - // when - const { format, attachment } = await indyFormatService.acceptRequest(agentContext, { - credentialRecord, - requestAttachment, - offerAttachment, - attachmentId: INDY_CREDENTIAL_ATTACHMENT_ID, - }) - - expect(attachment).toMatchObject({ - id: 'libindy-cred-0', - description: undefined, - filename: undefined, - mimeType: 'application/json', - lastmodTime: undefined, - byteCount: undefined, - data: { - base64: - 'eyJzY2hlbWFfaWQiOiJ4c3hzIiwiY3JlZF9kZWZfaWQiOiJ4ZHhkIiwicmV2X3JlZ19pZCI6IngiLCJ2YWx1ZXMiOnsieCI6eyJyYXciOiJ4IiwiZW5jb2RlZCI6InkifX19', - json: undefined, - links: undefined, - jws: undefined, - sha256: undefined, - }, - }) - expect(format).toMatchObject({ - attachmentId: expect.any(String), - format: 'hlindy/cred@v2.0', - }) - }) - }) - - describe('Process Credential', () => { - test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { - // given - credentialRecord = mockCredentialRecord({ - state: CredentialState.RequestSent, - metadata: { indyRequest: { cred_req: 'meta-data' } }, - }) - mockFunction(indyLedgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) - mockFunction(indyLedgerService.getRevocationRegistryDefinition).mockReturnValue( - Promise.resolve(revocationTemplate) - ) - mockFunction(indyHolderService.storeCredential).mockReturnValue(Promise.resolve('100')) - - // when - await indyFormatService.processCredential(agentContext, { - attachment: credentialAttachment, - requestAttachment: requestAttachment, - credentialRecord, - }) - - // then - expect(indyHolderService.storeCredential).toHaveBeenCalledTimes(1) - expect(credentialRecord.credentials.length).toBe(1) - expect(credentialRecord.credentials[0].credentialRecordType).toBe('indy') - expect(credentialRecord.credentials[0].credentialRecordId).toBe('100') - }) - }) -}) diff --git a/packages/core/src/modules/credentials/formats/index.ts b/packages/core/src/modules/credentials/formats/index.ts index 614b3559a3..fb2b300b5e 100644 --- a/packages/core/src/modules/credentials/formats/index.ts +++ b/packages/core/src/modules/credentials/formats/index.ts @@ -1,5 +1,4 @@ export * from './CredentialFormatService' export * from './CredentialFormatServiceOptions' export * from './CredentialFormat' -export * from './indy' export * from './jsonld' diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts deleted file mode 100644 index 73c8082372..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormat.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { IndyCredProposeOptions } from './models/IndyCredPropose' -import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' -import type { CredentialPreviewAttributeOptions } from '../../models' -import type { CredentialFormat } from '../CredentialFormat' -import type { Cred, CredOffer, CredReq } from 'indy-sdk' - -/** - * This defines the module payload for calling CredentialsApi.createProposal - * or CredentialsApi.negotiateOffer - */ -export interface IndyProposeCredentialFormat extends IndyCredProposeOptions { - attributes?: CredentialPreviewAttributeOptions[] - linkedAttachments?: LinkedAttachment[] -} - -/** - * This defines the module payload for calling CredentialsApi.acceptProposal - */ -export interface IndyAcceptProposalFormat { - credentialDefinitionId?: string - attributes?: CredentialPreviewAttributeOptions[] - linkedAttachments?: LinkedAttachment[] -} - -export interface IndyAcceptOfferFormat { - holderDid?: string -} - -/** - * This defines the module payload for calling CredentialsApi.offerCredential - * or CredentialsApi.negotiateProposal - */ -export interface IndyOfferCredentialFormat { - credentialDefinitionId: string - attributes: CredentialPreviewAttributeOptions[] - linkedAttachments?: LinkedAttachment[] -} - -export interface IndyCredentialFormat extends CredentialFormat { - formatKey: 'indy' - credentialRecordType: 'indy' - credentialFormats: { - createProposal: IndyProposeCredentialFormat - acceptProposal: IndyAcceptProposalFormat - createOffer: IndyOfferCredentialFormat - acceptOffer: IndyAcceptOfferFormat - createRequest: never // cannot start from createRequest - acceptRequest: Record // empty object - } - // Format data is based on RFC 0592 - // https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments - formatData: { - proposal: { - schema_issuer_did?: string - schema_name?: string - schema_version?: string - schema_id?: string - issuer_did?: string - cred_def_id?: string - } - offer: CredOffer - request: CredReq - credential: Cred - } -} diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts deleted file mode 100644 index e62124e6f2..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialFormatService.ts +++ /dev/null @@ -1,594 +0,0 @@ -import type { IndyCredentialFormat } from './IndyCredentialFormat' -import type { AgentContext } from '../../../../agent' -import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' -import type { CredentialPreviewAttributeOptions } from '../../models/CredentialPreviewAttribute' -import type { CredentialExchangeRecord } from '../../repository/CredentialExchangeRecord' -import type { CredentialFormatService } from '../CredentialFormatService' -import type { - CredentialFormatAcceptOfferOptions, - CredentialFormatAcceptProposalOptions, - CredentialFormatAcceptRequestOptions, - CredentialFormatAutoRespondCredentialOptions, - CredentialFormatAutoRespondOfferOptions, - CredentialFormatAutoRespondProposalOptions, - CredentialFormatAutoRespondRequestOptions, - CredentialFormatCreateOfferOptions, - CredentialFormatCreateOfferReturn, - CredentialFormatCreateProposalOptions, - CredentialFormatCreateProposalReturn, - CredentialFormatCreateReturn, - CredentialFormatProcessOptions, - CredentialFormatProcessCredentialOptions, -} from '../CredentialFormatServiceOptions' -import type * as Indy from 'indy-sdk' - -import { KeyType } from '../../../../crypto' -import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../error' -import { JsonEncoder } from '../../../../utils/JsonEncoder' -import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { MessageValidator } from '../../../../utils/MessageValidator' -import { TypedArrayEncoder } from '../../../../utils/TypedArrayEncoder' -import { getIndyDidFromVerificationMethod } from '../../../../utils/did' -import { uuid } from '../../../../utils/uuid' -import { ConnectionService } from '../../../connections' -import { DidResolverService, findVerificationMethodByKeyType } from '../../../dids' -import { IndyHolderService } from '../../../indy/services/IndyHolderService' -import { IndyIssuerService } from '../../../indy/services/IndyIssuerService' -import { IndyLedgerService } from '../../../ledger' -import { CredentialProblemReportError, CredentialProblemReportReason } from '../../errors' -import { CredentialFormatSpec } from '../../models/CredentialFormatSpec' -import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' -import { CredentialMetadataKeys } from '../../repository/CredentialMetadataTypes' - -import { IndyCredentialUtils } from './IndyCredentialUtils' -import { IndyCredPropose } from './models/IndyCredPropose' - -const INDY_CRED_ABSTRACT = 'hlindy/cred-abstract@v2.0' -const INDY_CRED_REQUEST = 'hlindy/cred-req@v2.0' -const INDY_CRED_FILTER = 'hlindy/cred-filter@v2.0' -const INDY_CRED = 'hlindy/cred@v2.0' - -export class IndyCredentialFormatService implements CredentialFormatService { - public readonly formatKey = 'indy' as const - public readonly credentialRecordType = 'indy' as const - - /** - * Create a {@link AttachmentFormats} object dependent on the message type. - * - * @param options The object containing all the options for the proposed credential - * @returns object containing associated attachment, format and optionally the credential preview - * - */ - public async createProposal( - agentContext: AgentContext, - { credentialFormats, credentialRecord, attachmentId }: CredentialFormatCreateProposalOptions - ): Promise { - const format = new CredentialFormatSpec({ - format: INDY_CRED_FILTER, - attachmentId, - }) - - const indyFormat = credentialFormats.indy - - if (!indyFormat) { - throw new AriesFrameworkError('Missing indy payload in createProposal') - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { attributes, linkedAttachments, ...indyCredentialProposal } = indyFormat - - const proposal = new IndyCredPropose(indyCredentialProposal) - - try { - MessageValidator.validateSync(proposal) - } catch (error) { - throw new AriesFrameworkError(`Invalid proposal supplied: ${indyCredentialProposal} in Indy Format Service`) - } - - const proposalJson = JsonTransformer.toJSON(proposal) - const attachment = this.getFormatData(proposalJson, format.attachmentId) - - const { previewAttributes } = this.getCredentialLinkedAttachments( - indyFormat.attributes, - indyFormat.linkedAttachments - ) - - // Set the metadata - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: proposal.schemaId, - credentialDefinitionId: proposal.credentialDefinitionId, - }) - - return { format, attachment, previewAttributes } - } - - public async processProposal( - agentContext: AgentContext, - { attachment }: CredentialFormatProcessOptions - ): Promise { - const proposalJson = attachment.getDataAsJson() - - // fromJSON also validates - JsonTransformer.fromJSON(proposalJson, IndyCredPropose) - } - - public async acceptProposal( - agentContext: AgentContext, - { - attachmentId, - credentialFormats, - credentialRecord, - proposalAttachment, - }: CredentialFormatAcceptProposalOptions - ): Promise { - const indyFormat = credentialFormats?.indy - - const credentialProposal = JsonTransformer.fromJSON(proposalAttachment.getDataAsJson(), IndyCredPropose) - - const credentialDefinitionId = indyFormat?.credentialDefinitionId ?? credentialProposal.credentialDefinitionId - const attributes = indyFormat?.attributes ?? credentialRecord.credentialAttributes - - if (!credentialDefinitionId) { - throw new AriesFrameworkError( - 'No credentialDefinitionId in proposal or provided as input to accept proposal method.' - ) - } - - if (!attributes) { - throw new AriesFrameworkError('No attributes in proposal or provided as input to accept proposal method.') - } - - const { format, attachment, previewAttributes } = await this.createIndyOffer(agentContext, { - credentialRecord, - attachmentId, - attributes, - credentialDefinitionId: credentialDefinitionId, - linkedAttachments: indyFormat?.linkedAttachments, - }) - - return { format, attachment, previewAttributes } - } - - /** - * Create a credential attachment format for a credential request. - * - * @param options The object containing all the options for the credential offer - * @returns object containing associated attachment, formats and offersAttach elements - * - */ - public async createOffer( - agentContext: AgentContext, - { credentialFormats, credentialRecord, attachmentId }: CredentialFormatCreateOfferOptions - ): Promise { - const indyFormat = credentialFormats.indy - - if (!indyFormat) { - throw new AriesFrameworkError('Missing indy credentialFormat data') - } - - const { format, attachment, previewAttributes } = await this.createIndyOffer(agentContext, { - credentialRecord, - attachmentId, - attributes: indyFormat.attributes, - credentialDefinitionId: indyFormat.credentialDefinitionId, - linkedAttachments: indyFormat.linkedAttachments, - }) - - return { format, attachment, previewAttributes } - } - - public async processOffer( - agentContext: AgentContext, - { attachment, credentialRecord }: CredentialFormatProcessOptions - ) { - agentContext.config.logger.debug(`Processing indy credential offer for credential record ${credentialRecord.id}`) - - const credOffer = attachment.getDataAsJson() - - if (!credOffer.schema_id || !credOffer.cred_def_id) { - throw new CredentialProblemReportError('Invalid credential offer', { - problemCode: CredentialProblemReportReason.IssuanceAbandoned, - }) - } - } - - public async acceptOffer( - agentContext: AgentContext, - { - credentialFormats, - credentialRecord, - attachmentId, - offerAttachment, - }: CredentialFormatAcceptOfferOptions - ): Promise { - const indyFormat = credentialFormats?.indy - - const indyLedgerService = agentContext.dependencyManager.resolve(IndyLedgerService) - const indyHolderService = agentContext.dependencyManager.resolve(IndyHolderService) - - const holderDid = indyFormat?.holderDid ?? (await this.getIndyHolderDid(agentContext, credentialRecord)) - - const credentialOffer = offerAttachment.getDataAsJson() - const credentialDefinition = await indyLedgerService.getCredentialDefinition( - agentContext, - credentialOffer.cred_def_id - ) - - const [credentialRequest, credentialRequestMetadata] = await indyHolderService.createCredentialRequest( - agentContext, - { - holderDid, - credentialOffer, - credentialDefinition, - } - ) - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, credentialRequestMetadata) - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - credentialDefinitionId: credentialOffer.cred_def_id, - schemaId: credentialOffer.schema_id, - }) - - const format = new CredentialFormatSpec({ - attachmentId, - format: INDY_CRED_REQUEST, - }) - - const attachment = this.getFormatData(credentialRequest, format.attachmentId) - return { format, attachment } - } - - /** - * Starting from a request is not supported for indy credentials, this method only throws an error. - */ - public async createRequest(): Promise { - throw new AriesFrameworkError('Starting from a request is not supported for indy credentials') - } - - /** - * We don't have any models to validate an indy request object, for now this method does nothing - */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async processRequest(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise { - // not needed for Indy - } - - public async acceptRequest( - agentContext: AgentContext, - { - credentialRecord, - attachmentId, - offerAttachment, - requestAttachment, - }: CredentialFormatAcceptRequestOptions - ): Promise { - // Assert credential attributes - const credentialAttributes = credentialRecord.credentialAttributes - if (!credentialAttributes) { - throw new CredentialProblemReportError( - `Missing required credential attribute values on credential record with id ${credentialRecord.id}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } - - const indyIssuerService = agentContext.dependencyManager.resolve(IndyIssuerService) - - const credentialOffer = offerAttachment?.getDataAsJson() - const credentialRequest = requestAttachment.getDataAsJson() - - if (!credentialOffer || !credentialRequest) { - throw new AriesFrameworkError('Missing indy credential offer or credential request in createCredential') - } - - const [credential, credentialRevocationId] = await indyIssuerService.createCredential(agentContext, { - credentialOffer, - credentialRequest, - credentialValues: IndyCredentialUtils.convertAttributesToValues(credentialAttributes), - }) - - if (credential.rev_reg_id) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - indyCredentialRevocationId: credentialRevocationId, - indyRevocationRegistryId: credential.rev_reg_id, - }) - } - - const format = new CredentialFormatSpec({ - attachmentId, - format: INDY_CRED, - }) - - const attachment = this.getFormatData(credential, format.attachmentId) - return { format, attachment } - } - - /** - * Processes an incoming credential - retrieve metadata, retrieve payload and store it in the Indy wallet - * @param options the issue credential message wrapped inside this object - * @param credentialRecord the credential exchange record for this credential - */ - public async processCredential( - agentContext: AgentContext, - { credentialRecord, attachment }: CredentialFormatProcessCredentialOptions - ): Promise { - const credentialRequestMetadata = credentialRecord.metadata.get(CredentialMetadataKeys.IndyRequest) - - const indyLedgerService = agentContext.dependencyManager.resolve(IndyLedgerService) - const indyHolderService = agentContext.dependencyManager.resolve(IndyHolderService) - - if (!credentialRequestMetadata) { - throw new CredentialProblemReportError( - `Missing required request metadata for credential with id ${credentialRecord.id}`, - { problemCode: CredentialProblemReportReason.IssuanceAbandoned } - ) - } - - const indyCredential = attachment.getDataAsJson() - const credentialDefinition = await indyLedgerService.getCredentialDefinition( - agentContext, - indyCredential.cred_def_id - ) - const revocationRegistry = indyCredential.rev_reg_id - ? await indyLedgerService.getRevocationRegistryDefinition(agentContext, indyCredential.rev_reg_id) - : null - - if (!credentialRecord.credentialAttributes) { - throw new AriesFrameworkError( - 'Missing credential attributes on credential record. Unable to check credential attributes' - ) - } - - // assert the credential values match the offer values - const recordCredentialValues = IndyCredentialUtils.convertAttributesToValues(credentialRecord.credentialAttributes) - IndyCredentialUtils.assertValuesMatch(indyCredential.values, recordCredentialValues) - - const credentialId = await indyHolderService.storeCredential(agentContext, { - credentialId: uuid(), - credentialRequestMetadata, - credential: indyCredential, - credentialDefinition, - revocationRegistryDefinition: revocationRegistry?.revocationRegistryDefinition, - }) - - // If the credential is revocable, store the revocation identifiers in the credential record - if (indyCredential.rev_reg_id) { - const credential = await indyHolderService.getCredential(agentContext, credentialId) - - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - indyCredentialRevocationId: credential.cred_rev_id, - indyRevocationRegistryId: indyCredential.rev_reg_id, - }) - } - - credentialRecord.credentials.push({ - credentialRecordType: this.credentialRecordType, - credentialRecordId: credentialId, - }) - } - - public supportsFormat(format: string): boolean { - const supportedFormats = [INDY_CRED_ABSTRACT, INDY_CRED_REQUEST, INDY_CRED_FILTER, INDY_CRED] - - return supportedFormats.includes(format) - } - - /** - * Gets the attachment object for a given attachmentId. We need to get out the correct attachmentId for - * indy and then find the corresponding attachment (if there is one) - * @param formats the formats object containing the attachmentId - * @param messageAttachments the attachments containing the payload - * @returns The Attachment if found or undefined - * - */ - public getAttachment(formats: CredentialFormatSpec[], messageAttachments: Attachment[]): Attachment | undefined { - const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachmentId) - const supportedAttachments = messageAttachments.filter((attachment) => - supportedAttachmentIds.includes(attachment.id) - ) - - return supportedAttachments[0] - } - - public async deleteCredentialById(agentContext: AgentContext, credentialRecordId: string): Promise { - const indyHolderService = agentContext.dependencyManager.resolve(IndyHolderService) - - await indyHolderService.deleteCredential(agentContext, credentialRecordId) - } - - public async shouldAutoRespondToProposal( - agentContext: AgentContext, - { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondProposalOptions - ) { - const credentialProposalJson = proposalAttachment.getDataAsJson() - const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) - - const credentialOfferJson = offerAttachment.getDataAsJson() - - // We want to make sure the credential definition matches. - // TODO: If no credential definition is present on the proposal, we could check whether the other fields - // of the proposal match with the credential definition id. - return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id - } - - public async shouldAutoRespondToOffer( - agentContext: AgentContext, - { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondOfferOptions - ) { - const credentialProposalJson = proposalAttachment.getDataAsJson() - const credentialProposal = JsonTransformer.fromJSON(credentialProposalJson, IndyCredPropose) - - const credentialOfferJson = offerAttachment.getDataAsJson() - - // We want to make sure the credential definition matches. - // TODO: If no credential definition is present on the proposal, we could check whether the other fields - // of the proposal match with the credential definition id. - return credentialProposal.credentialDefinitionId === credentialOfferJson.cred_def_id - } - - public async shouldAutoRespondToRequest( - agentContext: AgentContext, - { offerAttachment, requestAttachment }: CredentialFormatAutoRespondRequestOptions - ) { - const credentialOfferJson = offerAttachment.getDataAsJson() - const credentialRequestJson = requestAttachment.getDataAsJson() - - return credentialOfferJson.cred_def_id === credentialRequestJson.cred_def_id - } - - public async shouldAutoRespondToCredential( - agentContext: AgentContext, - { credentialRecord, requestAttachment, credentialAttachment }: CredentialFormatAutoRespondCredentialOptions - ) { - const credentialJson = credentialAttachment.getDataAsJson() - const credentialRequestJson = requestAttachment.getDataAsJson() - - // make sure the credential definition matches - if (credentialJson.cred_def_id !== credentialRequestJson.cred_def_id) return false - - // If we don't have any attributes stored we can't compare so always return false. - if (!credentialRecord.credentialAttributes) return false - const attributeValues = IndyCredentialUtils.convertAttributesToValues(credentialRecord.credentialAttributes) - - // check whether the values match the values in the record - return IndyCredentialUtils.checkValuesMatch(attributeValues, credentialJson.values) - } - - private async createIndyOffer( - agentContext: AgentContext, - { - credentialRecord, - attachmentId, - credentialDefinitionId, - attributes, - linkedAttachments, - }: { - credentialDefinitionId: string - credentialRecord: CredentialExchangeRecord - attachmentId?: string - attributes: CredentialPreviewAttributeOptions[] - linkedAttachments?: LinkedAttachment[] - } - ): Promise { - const indyIssuerService = agentContext.dependencyManager.resolve(IndyIssuerService) - - // if the proposal has an attachment Id use that, otherwise the generated id of the formats object - const format = new CredentialFormatSpec({ - attachmentId, - format: INDY_CRED_ABSTRACT, - }) - - const offer = await indyIssuerService.createCredentialOffer(agentContext, credentialDefinitionId) - - const { previewAttributes } = this.getCredentialLinkedAttachments(attributes, linkedAttachments) - if (!previewAttributes) { - throw new AriesFrameworkError('Missing required preview attributes for indy offer') - } - - await this.assertPreviewAttributesMatchSchemaAttributes(agentContext, offer, previewAttributes) - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - schemaId: offer.schema_id, - credentialDefinitionId: offer.cred_def_id, - }) - - const attachment = this.getFormatData(offer, format.attachmentId) - - return { format, attachment, previewAttributes } - } - - private async assertPreviewAttributesMatchSchemaAttributes( - agentContext: AgentContext, - offer: Indy.CredOffer, - attributes: CredentialPreviewAttribute[] - ): Promise { - const indyLedgerService = agentContext.dependencyManager.resolve(IndyLedgerService) - - const schema = await indyLedgerService.getSchema(agentContext, offer.schema_id) - - IndyCredentialUtils.checkAttributesMatch(schema, attributes) - } - - private async getIndyHolderDid(agentContext: AgentContext, credentialRecord: CredentialExchangeRecord) { - const connectionService = agentContext.dependencyManager.resolve(ConnectionService) - const didResolver = agentContext.dependencyManager.resolve(DidResolverService) - - // If we have a connection id we try to extract the did from the connection did document. - if (credentialRecord.connectionId) { - const connection = await connectionService.getById(agentContext, credentialRecord.connectionId) - if (!connection.did) { - throw new AriesFrameworkError(`Connection record ${connection.id} has no 'did'`) - } - const resolved = await didResolver.resolve(agentContext, connection.did) - - if (resolved.didDocument) { - const verificationMethod = await findVerificationMethodByKeyType( - 'Ed25519VerificationKey2018', - resolved.didDocument - ) - - if (verificationMethod) { - return getIndyDidFromVerificationMethod(verificationMethod) - } - } - } - - // If it wasn't successful to extract the did from the connection, we'll create a new key (e.g. if using connection-less) - // FIXME: we already create a did for the exchange when using connection-less, but this is on a higher level. We should look at - // a way to reuse this key, but for now this is easier. - const key = await agentContext.wallet.createKey({ keyType: KeyType.Ed25519 }) - const did = TypedArrayEncoder.toBase58(key.publicKey.slice(0, 16)) - - return did - } - - /** - * Get linked attachments for indy format from a proposal message. This allows attachments - * to be copied across to old style credential records - * - * @param options ProposeCredentialOptions object containing (optionally) the linked attachments - * @return array of linked attachments or undefined if none present - */ - private getCredentialLinkedAttachments( - attributes?: CredentialPreviewAttributeOptions[], - linkedAttachments?: LinkedAttachment[] - ): { - attachments?: Attachment[] - previewAttributes?: CredentialPreviewAttribute[] - } { - if (!linkedAttachments && !attributes) { - return {} - } - - let previewAttributes = attributes?.map((attribute) => new CredentialPreviewAttribute(attribute)) ?? [] - let attachments: Attachment[] | undefined - - if (linkedAttachments) { - // there are linked attachments so transform into the attribute field of the CredentialPreview object for - // this proposal - previewAttributes = IndyCredentialUtils.createAndLinkAttachmentsToPreview(linkedAttachments, previewAttributes) - attachments = linkedAttachments.map((linkedAttachment) => linkedAttachment.attachment) - } - - return { attachments, previewAttributes } - } - - /** - * Returns an object of type {@link Attachment} for use in credential exchange messages. - * It looks up the correct format identifier and encodes the data as a base64 attachment. - * - * @param data The data to include in the attach object - * @param id the attach id from the formats component of the message - */ - private getFormatData(data: unknown, id: string): Attachment { - const attachment = new Attachment({ - id, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(data), - }), - }) - - return attachment - } -} diff --git a/packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts b/packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts deleted file mode 100644 index 34042333e8..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/IndyCredentialUtils.ts +++ /dev/null @@ -1,207 +0,0 @@ -import type { LinkedAttachment } from '../../../../utils/LinkedAttachment' -import type { CredValues, Schema } from 'indy-sdk' - -import BigNumber from 'bn.js' - -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { Hasher } from '../../../../utils' -import { encodeAttachment } from '../../../../utils/attachment' -import { Buffer } from '../../../../utils/buffer' -import { isBoolean, isNumber, isString } from '../../../../utils/type' -import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' - -export class IndyCredentialUtils { - /** - * Adds attribute(s) to the credential preview that is linked to the given attachment(s) - * - * @param attachments a list of the attachments that need to be linked to a credential - * @param preview the credential previews where the new linked credential has to be appended to - * - * @returns a modified version of the credential preview with the linked credentials - * */ - public static createAndLinkAttachmentsToPreview( - attachments: LinkedAttachment[], - previewAttributes: CredentialPreviewAttribute[] - ) { - const credentialPreviewAttributeNames = previewAttributes.map((attribute) => attribute.name) - const newPreviewAttributes = [...previewAttributes] - - attachments.forEach((linkedAttachment) => { - if (credentialPreviewAttributeNames.includes(linkedAttachment.attributeName)) { - throw new AriesFrameworkError( - `linkedAttachment ${linkedAttachment.attributeName} already exists in the preview` - ) - } else { - const credentialPreviewAttribute = new CredentialPreviewAttribute({ - name: linkedAttachment.attributeName, - mimeType: linkedAttachment.attachment.mimeType, - value: encodeAttachment(linkedAttachment.attachment), - }) - newPreviewAttributes.push(credentialPreviewAttribute) - } - }) - - return newPreviewAttributes - } - - /** - * Converts int value to string - * Converts string value: - * - hash with sha256, - * - convert to byte array and reverse it - * - convert it to BigInteger and return as a string - * @param attributes - * - * @returns CredValues - */ - public static convertAttributesToValues(attributes: CredentialPreviewAttribute[]): CredValues { - return attributes.reduce((credentialValues, attribute) => { - return { - [attribute.name]: { - raw: attribute.value, - encoded: IndyCredentialUtils.encode(attribute.value), - }, - ...credentialValues, - } - }, {}) - } - - /** - * Check whether the values of two credentials match (using {@link assertValuesMatch}) - * - * @returns a boolean whether the values are equal - * - */ - public static checkValuesMatch(firstValues: CredValues, secondValues: CredValues): boolean { - try { - this.assertValuesMatch(firstValues, secondValues) - return true - } catch { - return false - } - } - - /** - * Assert two credential values objects match. - * - * @param firstValues The first values object - * @param secondValues The second values object - * - * @throws If not all values match - */ - public static assertValuesMatch(firstValues: CredValues, secondValues: CredValues) { - const firstValuesKeys = Object.keys(firstValues) - const secondValuesKeys = Object.keys(secondValues) - - if (firstValuesKeys.length !== secondValuesKeys.length) { - throw new Error( - `Number of values in first entry (${firstValuesKeys.length}) does not match number of values in second entry (${secondValuesKeys.length})` - ) - } - - for (const key of firstValuesKeys) { - const firstValue = firstValues[key] - const secondValue = secondValues[key] - - if (!secondValue) { - throw new Error(`Second cred values object has no value for key '${key}'`) - } - - if (firstValue.encoded !== secondValue.encoded) { - throw new Error(`Encoded credential values for key '${key}' do not match`) - } - - if (firstValue.raw !== secondValue.raw) { - throw new Error(`Raw credential values for key '${key}' do not match`) - } - } - } - - /** - * Check whether the raw value matches the encoded version according to the encoding format described in Aries RFC 0037 - * Use this method to ensure the received proof (over the encoded) value is the same as the raw value of the data. - * - * @param raw - * @param encoded - * @returns Whether raw and encoded value match - * - * @see https://github.com/hyperledger/aries-framework-dotnet/blob/a18bef91e5b9e4a1892818df7408e2383c642dfa/src/Hyperledger.Aries/Utils/CredentialUtils.cs#L78-L89 - * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials - */ - public static checkValidEncoding(raw: unknown, encoded: string) { - return encoded === IndyCredentialUtils.encode(raw) - } - - /** - * Encode value according to the encoding format described in Aries RFC 0036/0037 - * - * @param value - * @returns Encoded version of value - * - * @see https://github.com/hyperledger/aries-cloudagent-python/blob/0000f924a50b6ac5e6342bff90e64864672ee935/aries_cloudagent/messaging/util.py#L106-L136 - * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0037-present-proof/README.md#verifying-claims-of-indy-based-verifiable-credentials - * @see https://github.com/hyperledger/aries-rfcs/blob/be4ad0a6fb2823bb1fc109364c96f077d5d8dffa/features/0036-issue-credential/README.md#encoding-claims-for-indy-based-verifiable-credentials - */ - public static encode(value: unknown) { - const isEmpty = (value: unknown) => isString(value) && value === '' - - // If bool return bool as number string - if (isBoolean(value)) { - return Number(value).toString() - } - - // If value is int32 return as number string - if (isNumber(value) && this.isInt32(value)) { - return value.toString() - } - - // If value is an int32 number string return as number string - if ( - isString(value) && - !isEmpty(value) && - !isNaN(Number(value)) && - this.isNumeric(value) && - this.isInt32(Number(value)) - ) { - return Number(value).toString() - } - - if (isNumber(value)) { - value = value.toString() - } - - // If value is null we must use the string value 'None' - if (value === null || value === undefined) { - value = 'None' - } - - return new BigNumber(Hasher.hash(Buffer.from(value as string), 'sha2-256')).toString() - } - - public static checkAttributesMatch(schema: Schema, attributes: CredentialPreviewAttribute[]) { - const schemaAttributes = schema.attrNames - const credAttributes = attributes.map((a) => a.name) - - const difference = credAttributes - .filter((x) => !schemaAttributes.includes(x)) - .concat(schemaAttributes.filter((x) => !credAttributes.includes(x))) - - if (difference.length > 0) { - throw new AriesFrameworkError( - `The credential preview attributes do not match the schema attributes (difference is: ${difference}, needs: ${schemaAttributes})` - ) - } - } - - private static isInt32(number: number) { - const minI32 = -2147483648 - const maxI32 = 2147483647 - - // Check if number is integer and in range of int32 - return Number.isInteger(number) && number >= minI32 && number <= maxI32 - } - - private static isNumeric(value: string) { - return /^-?\d+$/.test(value) - } -} diff --git a/packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts b/packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts deleted file mode 100644 index e89a849001..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/__tests__/IndyCredentialUtils.test.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { CredentialPreviewAttribute } from '../../../models/CredentialPreviewAttribute' -import { IndyCredentialUtils } from '../IndyCredentialUtils' - -/** - * Sample test cases for encoding/decoding of verifiable credential claims - Aries RFCs 0036 and 0037 - * @see https://gist.github.com/swcurran/78e5a9e8d11236f003f6a6263c6619a6 - */ -const testEncodings: { [key: string]: { raw: string | number | boolean | null; encoded: string } } = { - address2: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - zip: { - raw: '87121', - encoded: '87121', - }, - city: { - raw: 'SLC', - encoded: '101327353979588246869873249766058188995681113722618593621043638294296500696424', - }, - address1: { - raw: '101 Tela Lane', - encoded: '63690509275174663089934667471948380740244018358024875547775652380902762701972', - }, - state: { - raw: 'UT', - encoded: '93856629670657830351991220989031130499313559332549427637940645777813964461231', - }, - Empty: { - raw: '', - encoded: '102987336249554097029535212322581322789799900648198034993379397001115665086549', - }, - Null: { - raw: null, - encoded: '99769404535520360775991420569103450442789945655240760487761322098828903685777', - }, - 'bool True': { - raw: true, - encoded: '1', - }, - 'bool False': { - raw: false, - encoded: '0', - }, - 'str True': { - raw: 'True', - encoded: '27471875274925838976481193902417661171675582237244292940724984695988062543640', - }, - 'str False': { - raw: 'False', - encoded: '43710460381310391454089928988014746602980337898724813422905404670995938820350', - }, - 'max i32': { - raw: 2147483647, - encoded: '2147483647', - }, - 'max i32 + 1': { - raw: 2147483648, - encoded: '26221484005389514539852548961319751347124425277437769688639924217837557266135', - }, - 'min i32': { - raw: -2147483648, - encoded: '-2147483648', - }, - 'min i32 - 1': { - raw: -2147483649, - encoded: '68956915425095939579909400566452872085353864667122112803508671228696852865689', - }, - 'float 0.1': { - raw: 0.1, - encoded: '9382477430624249591204401974786823110077201914483282671737639310288175260432', - }, - 'str 0.1': { - raw: '0.1', - encoded: '9382477430624249591204401974786823110077201914483282671737639310288175260432', - }, - 'str 1.0': { - raw: '1.0', - encoded: '94532235908853478633102631881008651863941875830027892478278578250784387892726', - }, - 'str 1': { - raw: '1', - encoded: '1', - }, - 'leading zero number string': { - raw: '012345', - encoded: '12345', - }, - 'chr 0': { - raw: String.fromCharCode(0), - encoded: '49846369543417741186729467304575255505141344055555831574636310663216789168157', - }, - 'chr 1': { - raw: String.fromCharCode(1), - encoded: '34356466678672179216206944866734405838331831190171667647615530531663699592602', - }, - 'chr 2': { - raw: String.fromCharCode(2), - encoded: '99398763056634537812744552006896172984671876672520535998211840060697129507206', - }, -} - -describe('IndyCredentialUtils', () => { - describe('convertAttributesToValues', () => { - test('returns object with raw and encoded attributes', () => { - const attributes = [ - new CredentialPreviewAttribute({ - name: 'name', - mimeType: 'text/plain', - value: '101 Wilson Lane', - }), - new CredentialPreviewAttribute({ - name: 'age', - mimeType: 'text/plain', - value: '1234', - }), - ] - - expect(IndyCredentialUtils.convertAttributesToValues(attributes)).toEqual({ - name: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - age: { raw: '1234', encoded: '1234' }, - }) - }) - }) - - describe('assertValuesMatch', () => { - test('does not throw if attributes match', () => { - const firstValues = { - name: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - age: { raw: '1234', encoded: '1234' }, - } - const secondValues = { - name: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - age: { raw: '1234', encoded: '1234' }, - } - - expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).not.toThrow() - }) - - test('throws if number of values in the entries do not match', () => { - const firstValues = { - age: { raw: '1234', encoded: '1234' }, - } - const secondValues = { - name: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - age: { raw: '1234', encoded: '1234' }, - } - - expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( - 'Number of values in first entry (1) does not match number of values in second entry (2)' - ) - }) - - test('throws if second value does not contain key from first value', () => { - const firstValues = { - name: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - age: { raw: '1234', encoded: '1234' }, - } - const secondValues = { - anotherName: { - raw: '101 Wilson Lane', - encoded: '68086943237164982734333428280784300550565381723532936263016368251445461241953', - }, - age: { raw: '1234', encoded: '1234' }, - } - - expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( - "Second cred values object has no value for key 'name'" - ) - }) - - test('throws if encoded values do not match', () => { - const firstValues = { - age: { raw: '1234', encoded: '1234' }, - } - const secondValues = { - age: { raw: '1234', encoded: '12345' }, - } - - expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( - "Encoded credential values for key 'age' do not match" - ) - }) - - test('throws if raw values do not match', () => { - const firstValues = { - age: { raw: '1234', encoded: '1234' }, - } - const secondValues = { - age: { raw: '12345', encoded: '1234' }, - } - - expect(() => IndyCredentialUtils.assertValuesMatch(firstValues, secondValues)).toThrow( - "Raw credential values for key 'age' do not match" - ) - }) - }) - - describe('checkValidEncoding', () => { - // Formatted for test.each - const testEntries = Object.entries(testEncodings).map( - ([name, { raw, encoded }]) => [name, raw, encoded] as [string, string | number | boolean | null, string] - ) - - test.each(testEntries)('returns true for valid encoding %s', (_, raw, encoded) => { - expect(IndyCredentialUtils.checkValidEncoding(raw, encoded)).toEqual(true) - }) - }) -}) diff --git a/packages/core/src/modules/credentials/formats/indy/index.ts b/packages/core/src/modules/credentials/formats/indy/index.ts deleted file mode 100644 index f79b7ee9c2..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './models' -export * from './IndyCredentialFormatService' -export * from './IndyCredentialFormat' diff --git a/packages/core/src/modules/credentials/formats/indy/models/IndyCredPropose.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyCredPropose.ts deleted file mode 100644 index 3ddfff7542..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/IndyCredPropose.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Expose } from 'class-transformer' -import { IsOptional, IsString } from 'class-validator' - -export interface IndyCredProposeOptions { - schemaIssuerDid?: string - schemaId?: string - schemaName?: string - schemaVersion?: string - credentialDefinitionId?: string - issuerDid?: string -} - -/** - * Class providing validation for the V2 credential proposal payload. - * - * The v1 message contains the properties directly in the message, which means they are - * validated using the class validator decorators. In v2 the attachments content is not transformed - * when transforming the message to a class instance so the content is not verified anymore, hence this - * class. - * - */ -export class IndyCredPropose { - public constructor(options: IndyCredProposeOptions) { - if (options) { - this.schemaIssuerDid = options.schemaIssuerDid - this.schemaId = options.schemaId - this.schemaName = options.schemaName - this.schemaVersion = options.schemaVersion - this.credentialDefinitionId = options.credentialDefinitionId - this.issuerDid = options.issuerDid - } - } - - /** - * Filter to request credential based on a particular Schema issuer DID. - */ - @Expose({ name: 'schema_issuer_did' }) - @IsString() - @IsOptional() - public schemaIssuerDid?: string - - /** - * Filter to request credential based on a particular Schema. - */ - @Expose({ name: 'schema_id' }) - @IsString() - @IsOptional() - public schemaId?: string - - /** - * Filter to request credential based on a schema name. - */ - @Expose({ name: 'schema_name' }) - @IsString() - @IsOptional() - public schemaName?: string - - /** - * Filter to request credential based on a schema version. - */ - @Expose({ name: 'schema_version' }) - @IsString() - @IsOptional() - public schemaVersion?: string - - /** - * Filter to request credential based on a particular Credential Definition. - */ - @Expose({ name: 'cred_def_id' }) - @IsString() - @IsOptional() - public credentialDefinitionId?: string - - /** - * Filter to request a credential issued by the owner of a particular DID. - */ - @Expose({ name: 'issuer_did' }) - @IsString() - @IsOptional() - public issuerDid?: string -} diff --git a/packages/core/src/modules/credentials/formats/indy/models/IndyCredential.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyCredential.ts deleted file mode 100644 index 7c1addefb4..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/IndyCredential.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type * as Indy from 'indy-sdk' - -import { Expose, Type } from 'class-transformer' -import { IsInstance, IsOptional, ValidateNested } from 'class-validator' - -import { JsonTransformer } from '../../../../../utils/JsonTransformer' - -import { IndyCredentialInfo } from './IndyCredentialInfo' -import { IndyRevocationInterval } from './IndyRevocationInterval' - -export class IndyCredential { - public constructor(options: IndyCredential) { - if (options) { - this.credentialInfo = options.credentialInfo - this.interval = options.interval - } - } - - @Expose({ name: 'cred_info' }) - @Type(() => IndyCredentialInfo) - @ValidateNested() - @IsInstance(IndyCredentialInfo) - public credentialInfo!: IndyCredentialInfo - - @IsOptional() - @Type(() => IndyRevocationInterval) - @ValidateNested() - @IsInstance(IndyRevocationInterval) - public interval?: IndyRevocationInterval - - public toJSON(): Indy.IndyCredential { - return JsonTransformer.toJSON(this) as unknown as Indy.IndyCredential - } -} diff --git a/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialInfo.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialInfo.ts deleted file mode 100644 index 9edd269157..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialInfo.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { IndyCredentialInfo as IndySDKCredentialInfo } from 'indy-sdk' - -import { Expose } from 'class-transformer' -import { IsOptional, IsString } from 'class-validator' - -import { JsonTransformer } from '../../../../../utils' - -export interface IndyCredentialInfoOptions { - referent: string - attributes: Record - schemaId: string - credentialDefinitionId: string - revocationRegistryId?: string - credentialRevocationId?: string -} - -export class IndyCredentialInfo { - public constructor(options: IndyCredentialInfoOptions) { - if (options) { - this.referent = options.referent - this.attributes = options.attributes - this.schemaId = options.schemaId - this.credentialDefinitionId = options.credentialDefinitionId - this.revocationRegistryId = options.revocationRegistryId - this.credentialRevocationId = options.credentialRevocationId - } - } - - /** - * Credential ID in the wallet - */ - @IsString() - public referent!: string - - @Expose({ name: 'attrs' }) - @IsString({ each: true }) - public attributes!: Record - - @Expose({ name: 'schema_id' }) - @IsString() - public schemaId!: string - - @Expose({ name: 'cred_def_id' }) - @IsString() - public credentialDefinitionId!: string - - @Expose({ name: 'rev_reg_id' }) - @IsString() - @IsOptional() - public revocationRegistryId?: string - - @Expose({ name: 'cred_rev_id' }) - @IsString() - @IsOptional() - public credentialRevocationId?: string - - public toJSON(): IndySDKCredentialInfo { - return JsonTransformer.toJSON(this) as unknown as IndySDKCredentialInfo - } -} diff --git a/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialView.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialView.ts deleted file mode 100644 index 05f18c6e9c..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/IndyCredentialView.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { Attachment } from '../../../../../decorators/attachment/Attachment' - -export interface IndyCredentialViewModel { - metadata?: IndyCredentialViewMetadata | null - claims: Record - attachments?: Attachment[] -} - -export interface IndyCredentialViewMetadata { - credentialDefinitionId?: string - schemaId?: string -} - -export class IndyCredentialView { - public constructor(options: IndyCredentialViewModel) { - this.metadata = options.metadata ?? {} - this.claims = options.claims - this.attachments = options.attachments - } - - public metadata: IndyCredentialViewMetadata - public claims: Record - public attachments?: Attachment[] -} diff --git a/packages/core/src/modules/credentials/formats/indy/models/IndyRevocationInterval.ts b/packages/core/src/modules/credentials/formats/indy/models/IndyRevocationInterval.ts deleted file mode 100644 index 6057153aaa..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/IndyRevocationInterval.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IsInt, IsOptional } from 'class-validator' - -export class IndyRevocationInterval { - public constructor(options: { from?: number; to?: number }) { - if (options) { - this.from = options.from - this.to = options.to - } - } - - @IsInt() - @IsOptional() - public from?: number - - @IsInt() - @IsOptional() - public to?: number -} diff --git a/packages/core/src/modules/credentials/formats/indy/models/__tests__/IndyCredentialView.test.ts b/packages/core/src/modules/credentials/formats/indy/models/__tests__/IndyCredentialView.test.ts deleted file mode 100644 index 840099d41d..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/__tests__/IndyCredentialView.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { IndyCredentialView } from '../IndyCredentialView' - -describe('CredentialInfo', () => { - it('should return the correct property values', () => { - const claims = { - name: 'Timo', - date_of_birth: '1998-07-29', - 'country-of-residence': 'The Netherlands', - 'street name': 'Test street', - age: '22', - } - const metadata = { - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', - } - const credentialInfo = new IndyCredentialView({ - claims, - metadata, - }) - - expect(credentialInfo.claims).toEqual(claims) - expect(credentialInfo.metadata).toEqual(metadata) - }) -}) diff --git a/packages/core/src/modules/credentials/formats/indy/models/index.ts b/packages/core/src/modules/credentials/formats/indy/models/index.ts deleted file mode 100644 index 8f63220f10..0000000000 --- a/packages/core/src/modules/credentials/formats/indy/models/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './IndyCredential' -export * from './IndyCredentialInfo' -export * from './IndyRevocationInterval' -export * from './IndyCredentialView' -export * from './IndyCredPropose' diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index 52be8f6493..0b49134c0f 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -252,7 +252,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService const DidResolverServiceMock = DidResolverService as jest.Mock @@ -113,26 +106,11 @@ const mockCredentialRecord = ({ id?: string credentialAttributes?: CredentialPreviewAttribute[] } = {}) => { - const offerOptions: V2OfferCredentialMessageOptions = { - id: '', - formats: [ - { - attachmentId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, - format: 'hlindy/cred-abstract@v2.0', - }, - ], - comment: 'some comment', - credentialPreview: credentialPreview, - offerAttachments: [offerAttachment], - replacementId: undefined, - } - const offerMessage = new V2OfferCredentialMessage(offerOptions) - const credentialRecord = new CredentialExchangeRecord({ id, credentialAttributes: credentialAttributes || credentialPreview.attributes, state: state || CredentialState.OfferSent, - threadId: threadId ?? offerMessage.id, + threadId: threadId ?? 'add7e1a0-109e-4f37-9caa-cfd0fcdfe540', connectionId: connectionId ?? '123', tags, protocolVersion: 'v2', diff --git a/packages/core/src/modules/credentials/index.ts b/packages/core/src/modules/credentials/index.ts index 286f34276d..d34680afe1 100644 --- a/packages/core/src/modules/credentials/index.ts +++ b/packages/core/src/modules/credentials/index.ts @@ -7,4 +7,3 @@ export * from './formats' export * from './protocol' export * from './CredentialsModule' export * from './CredentialsModuleConfig' -export { CredentialProblemReportError, CredentialProblemReportReason } from './errors' diff --git a/packages/core/src/modules/credentials/errors/CredentialProblemReportReason.ts b/packages/core/src/modules/credentials/models/CredentialProblemReportReason.ts similarity index 100% rename from packages/core/src/modules/credentials/errors/CredentialProblemReportReason.ts rename to packages/core/src/modules/credentials/models/CredentialProblemReportReason.ts diff --git a/packages/core/src/modules/credentials/models/index.ts b/packages/core/src/modules/credentials/models/index.ts index 0db2bca14c..bec3b0ce2f 100644 --- a/packages/core/src/modules/credentials/models/index.ts +++ b/packages/core/src/modules/credentials/models/index.ts @@ -3,3 +3,4 @@ export * from './CredentialPreviewAttribute' export * from './CredentialAutoAcceptType' export * from './CredentialFormatSpec' export * from './CredentialState' +export * from './CredentialProblemReportReason' diff --git a/packages/core/src/modules/credentials/protocol/index.ts b/packages/core/src/modules/credentials/protocol/index.ts index 6433655022..cb3d5c3b51 100644 --- a/packages/core/src/modules/credentials/protocol/index.ts +++ b/packages/core/src/modules/credentials/protocol/index.ts @@ -1,3 +1,11 @@ -export * from './v1' export * from './v2' export * from './revocation-notification' +import * as CredentialProtocolOptions from './CredentialProtocolOptions' + +export { CredentialProtocol } from './CredentialProtocol' +// NOTE: ideally we don't export the BaseCredentialProtocol, but as the V1CredentialProtocol is defined in the +// anoncreds package, we need to export it. We should at some point look at creating a core package which can be used for +// sharing internal types, and when you want to build you own modules, and an agent package, which is the one you use when +// consuming the framework +export { BaseCredentialProtocol } from './BaseCredentialProtocol' +export { CredentialProtocolOptions } diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts index f3636c689a..e2b9d6e1f9 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts @@ -41,12 +41,13 @@ export class RevocationNotificationService { private async processRevocationNotification( agentContext: AgentContext, - indyRevocationRegistryId: string, - indyCredentialRevocationId: string, + anonCredsRevocationRegistryId: string, + anonCredsCredentialRevocationId: string, connection: ConnectionRecord, comment?: string ) { - const query = { indyRevocationRegistryId, indyCredentialRevocationId, connectionId: connection.id } + // TODO: can we extract support for this revocation notification handler to the anoncreds module? + const query = { anonCredsRevocationRegistryId, anonCredsCredentialRevocationId, connectionId: connection.id } this.logger.trace(`Getting record by query for revocation notification:`, query) const credentialRecord = await this.credentialRepository.getSingleByQuery(agentContext, query) @@ -88,14 +89,14 @@ export class RevocationNotificationService { ) } - const [, , indyRevocationRegistryId, indyCredentialRevocationId] = threadIdGroups + const [, , anonCredsRevocationRegistryId, anonCredsCredentialRevocationId] = threadIdGroups const comment = messageContext.message.comment const connection = messageContext.assertReadyConnection() await this.processRevocationNotification( messageContext.agentContext, - indyRevocationRegistryId, - indyCredentialRevocationId, + anonCredsRevocationRegistryId, + anonCredsCredentialRevocationId, connection, comment ) @@ -131,13 +132,13 @@ export class RevocationNotificationService { ) } - const [, indyRevocationRegistryId, indyCredentialRevocationId] = credentialIdGroups + const [, anonCredsRevocationRegistryId, anonCredsCredentialRevocationId] = credentialIdGroups const comment = messageContext.message.comment const connection = messageContext.assertReadyConnection() await this.processRevocationNotification( messageContext.agentContext, - indyRevocationRegistryId, - indyCredentialRevocationId, + anonCredsRevocationRegistryId, + anonCredsCredentialRevocationId, connection, comment ) diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts index c0b3e57904..e834ca5585 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts @@ -1,3 +1,4 @@ +import type { AnonCredsCredentialMetadata } from '../../../../../../../../anoncreds/src/utils/metadata' import type { AgentContext } from '../../../../../../agent' import type { RevocationNotificationReceivedEvent } from '../../../../CredentialEvents' @@ -9,7 +10,6 @@ import { Dispatcher } from '../../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../../agent/EventEmitter' import { DidExchangeState } from '../../../../../connections' import { CredentialEventTypes } from '../../../../CredentialEvents' -import { CredentialMetadataKeys } from '../../../../repository' import { CredentialRepository } from '../../../../repository/CredentialRepository' import { V1RevocationNotificationMessage, V2RevocationNotificationMessage } from '../../messages' import { RevocationNotificationService } from '../RevocationNotificationService' @@ -32,9 +32,7 @@ describe('RevocationNotificationService', () => { let eventEmitter: EventEmitter beforeEach(() => { - const agentConfig = getAgentConfig('RevocationNotificationService', { - indyLedgers: [], - }) + const agentConfig = getAgentConfig('RevocationNotificationService') agentContext = getAgentContext() @@ -72,16 +70,19 @@ describe('RevocationNotificationService', () => { state: CredentialState.Done, }) - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - indyRevocationRegistryId: + const metadata = { + revocationRegistryId: 'AsB27X6KRrJFsqZ3unNAH6:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9', - indyCredentialRevocationId: '1', - }) + credentialRevocationId: '1', + } satisfies AnonCredsCredentialMetadata + + // Set required tags + credentialRecord.setTag('anonCredsRevocationRegistryId', metadata.revocationRegistryId) + credentialRecord.setTag('anonCredsCredentialRevocationId', metadata.credentialRevocationId) mockFunction(credentialRepository.getSingleByQuery).mockResolvedValueOnce(credentialRecord) - const { indyRevocationRegistryId, indyCredentialRevocationId } = credentialRecord.getTags() - const revocationNotificationThreadId = `indy::${indyRevocationRegistryId}::${indyCredentialRevocationId}` + const revocationNotificationThreadId = `indy::${metadata.revocationRegistryId}::${metadata.credentialRevocationId}` const revocationNotificationMessage = new V1RevocationNotificationMessage({ issueThread: revocationNotificationThreadId, comment: 'Credential has been revoked', @@ -182,15 +183,14 @@ describe('RevocationNotificationService', () => { state: CredentialState.Done, }) - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - indyRevocationRegistryId: + const metadata = { + revocationRegistryId: 'AsB27X6KRrJFsqZ3unNAH6:4:AsB27X6KRrJFsqZ3unNAH6:3:cl:48187:default:CL_ACCUM:3b24a9b0-a979-41e0-9964-2292f2b1b7e9', - indyCredentialRevocationId: '1', - }) + credentialRevocationId: '1', + } satisfies AnonCredsCredentialMetadata mockFunction(credentialRepository.getSingleByQuery).mockResolvedValueOnce(credentialRecord) - const { indyRevocationRegistryId, indyCredentialRevocationId } = credentialRecord.getTags() - const revocationNotificationCredentialId = `${indyRevocationRegistryId}::${indyCredentialRevocationId}` + const revocationNotificationCredentialId = `${metadata.revocationRegistryId}::${metadata.credentialRevocationId}` const revocationNotificationMessage = new V2RevocationNotificationMessage({ credentialId: revocationNotificationCredentialId, diff --git a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts index df39187210..901834a106 100644 --- a/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/v2/V2CredentialProtocol.ts @@ -36,8 +36,7 @@ import { uuid } from '../../../../utils/uuid' import { AckStatus } from '../../../common' import { ConnectionService } from '../../../connections' import { CredentialsModuleConfig } from '../../CredentialsModuleConfig' -import { CredentialProblemReportReason } from '../../errors' -import { AutoAcceptCredential, CredentialState } from '../../models' +import { AutoAcceptCredential, CredentialProblemReportReason, CredentialState } from '../../models' import { CredentialExchangeRecord, CredentialRepository } from '../../repository' import { composeAutoAccept } from '../../util/composeAutoAccept' import { arePreviewAttributesEqual } from '../../util/previewAttributes' @@ -699,7 +698,7 @@ export class V2CredentialProtocol -const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock -const JsonLdCredentialFormatServiceMock = JsonLdCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() -const indyCredentialFormatService = new IndyCredentialFormatServiceMock() -const jsonLdCredentialFormatService = new JsonLdCredentialFormatServiceMock() const connectionService = new ConnectionServiceMock() -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -indyCredentialFormatService.formatKey = 'indy' -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -indyCredentialFormatService.credentialRecordType = 'indy' - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -jsonLdCredentialFormatService.formatKey = 'jsonld' - const agentConfig = getAgentConfig('V2CredentialProtocolCredTest') const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) @@ -88,11 +72,6 @@ const connection = getMockConnection({ state: DidExchangeState.Completed, }) -const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', -}) - const offerAttachment = new Attachment({ id: 'offer-attachment-id', mimeType: 'application/json', @@ -115,7 +94,7 @@ const credentialAttachment = new Attachment({ mimeType: 'application/json', data: new AttachmentData({ base64: JsonEncoder.toBase64({ - values: IndyCredentialUtils.convertAttributesToValues(credentialPreview.attributes), + values: {}, }), }), }) @@ -162,7 +141,9 @@ credentialRequestMessage.setThread({ threadId: 'somethreadid' }) const credentialOfferMessage = new V2OfferCredentialMessage({ formats: [offerFormat], comment: 'some comment', - credentialPreview: credentialPreview, + credentialPreview: new V2CredentialPreview({ + attributes: [], + }), offerAttachments: [offerAttachment], }) const credentialIssueMessage = new V2IssueCredentialMessage({ @@ -199,7 +180,6 @@ const getAgentMessageMock = async (agentContext: AgentContext, options: GetAgent // object to test our service would behave correctly. We use type assertion for `offer` attribute to `any`. const mockCredentialRecord = ({ state, - metadata, threadId, connectionId, tags, @@ -207,7 +187,6 @@ const mockCredentialRecord = ({ credentialAttributes, }: { state?: CredentialState - metadata?: IndyCredentialViewMetadata & { indyRequest: Record } tags?: CustomCredentialTags threadId?: string connectionId?: string @@ -216,13 +195,13 @@ const mockCredentialRecord = ({ } = {}) => { const credentialRecord = new CredentialExchangeRecord({ id, - credentialAttributes: credentialAttributes || credentialPreview.attributes, + credentialAttributes: credentialAttributes, state: state || CredentialState.OfferSent, threadId: threadId || 'thread-id', connectionId: connectionId ?? '123', credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'test', credentialRecordId: '123456', }, ], @@ -230,25 +209,37 @@ const mockCredentialRecord = ({ protocolVersion: 'v2', }) - if (metadata?.indyRequest) { - credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, { ...metadata.indyRequest }) - } - - if (metadata?.schemaId) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - schemaId: metadata.schemaId, - }) - } - - if (metadata?.credentialDefinitionId) { - credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { - credentialDefinitionId: metadata.credentialDefinitionId, - }) - } - return credentialRecord } +interface TestCredentialFormat extends CredentialFormat { + formatKey: 'test' + credentialRecordType: 'test' +} + +type TestCredentialFormatService = CredentialFormatService + +export const testCredentialFormatService = { + credentialRecordType: 'test', + formatKey: 'test', + supportsFormat: (_format: string) => true, + createOffer: async ( + _agentContext: AgentContext, + _options: CredentialFormatCreateOfferOptions + ) => ({ + attachment: offerAttachment, + format: offerFormat, + }), + acceptRequest: async ( + _agentContext: AgentContext, + _options: CredentialFormatAcceptRequestOptions + ) => ({ attachment: credentialAttachment, format: credentialFormat }), + deleteCredentialById: jest.fn(), + processCredential: jest.fn(), + acceptOffer: () => ({ attachment: requestAttachment, format: requestFormat }), + processRequest: jest.fn(), +} as unknown as TestCredentialFormatService + describe('credentialProtocol', () => { let credentialProtocol: V2CredentialProtocol @@ -264,7 +255,7 @@ describe('credentialProtocol', () => { ]) credentialProtocol = new V2CredentialProtocol({ - credentialFormats: [indyCredentialFormatService, jsonLdCredentialFormatService], + credentialFormats: [testCredentialFormatService], }) }) @@ -273,28 +264,17 @@ describe('credentialProtocol', () => { }) describe('acceptOffer', () => { - test(`updates state to ${CredentialState.RequestSent}, set request metadata`, async () => { + test(`updates state to ${CredentialState.RequestSent}`, async () => { const credentialRecord = mockCredentialRecord({ state: CredentialState.OfferReceived, threadId: 'fd9c5ddb-ec11-4acd-bc32-540736249746', connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.acceptOffer).mockResolvedValue({ - attachment: requestAttachment, - format: requestFormat, - }) - // when await credentialProtocol.acceptOffer(agentContext, { credentialRecord, - credentialFormats: { - indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - }, - }, + credentialFormats: {}, }) // then @@ -317,12 +297,6 @@ describe('credentialProtocol', () => { connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', }) - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.acceptOffer).mockResolvedValue({ - attachment: requestAttachment, - format: requestFormat, - }) - // when const { message: credentialRequest } = await credentialProtocol.acceptOffer(agentContext, { credentialRecord, @@ -357,8 +331,6 @@ describe('credentialProtocol', () => { describe('processRequest', () => { test(`updates state to ${CredentialState.RequestReceived}, set request and returns credential record`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - const credentialRecord = mockCredentialRecord({ state: CredentialState.OfferSent }) const messageContext = new InboundMessageContext(credentialRequestMessage, { connection, @@ -381,8 +353,6 @@ describe('credentialProtocol', () => { }) test(`emits stateChange event from ${CredentialState.OfferSent} to ${CredentialState.RequestReceived}`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - const credentialRecord = mockCredentialRecord({ state: CredentialState.OfferSent }) const messageContext = new InboundMessageContext(credentialRequestMessage, { connection, @@ -413,8 +383,6 @@ describe('credentialProtocol', () => { const validState = CredentialState.OfferSent const invalidCredentialStates = Object.values(CredentialState).filter((state) => state !== validState) test(`throws an error when state transition is invalid`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - const messageContext = new InboundMessageContext(credentialRequestMessage, { connection, agentContext, @@ -435,12 +403,6 @@ describe('credentialProtocol', () => { describe('acceptRequest', () => { test(`updates state to ${CredentialState.CredentialIssued}`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ - attachment: credentialAttachment, - format: credentialFormat, - }) - const credentialRecord = mockCredentialRecord({ state: CredentialState.RequestReceived, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', @@ -462,12 +424,6 @@ describe('credentialProtocol', () => { }) test(`emits stateChange event from ${CredentialState.RequestReceived} to ${CredentialState.CredentialIssued}`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ - attachment: credentialAttachment, - format: credentialFormat, - }) - const credentialRecord = mockCredentialRecord({ state: CredentialState.RequestReceived, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', @@ -501,12 +457,6 @@ describe('credentialProtocol', () => { }) test('returns credential response message base on credential request message', async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.acceptRequest).mockResolvedValue({ - attachment: credentialAttachment, - format: credentialFormat, - }) - const credentialRecord = mockCredentialRecord({ state: CredentialState.RequestReceived, connectionId: 'b1e2f039-aa39-40be-8643-6ce2797b5190', @@ -539,8 +489,6 @@ describe('credentialProtocol', () => { describe('processCredential', () => { test('finds credential record by thread ID and saves credential attachment into the wallet', async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - const credentialRecord = mockCredentialRecord({ state: CredentialState.RequestSent, }) @@ -553,10 +501,7 @@ describe('credentialProtocol', () => { // given mockFunction(credentialRepository.getSingleByQuery).mockResolvedValue(credentialRecord) - // when - const record = await credentialProtocol.processCredential(messageContext) - - expect(record.credentialAttributes?.length).toBe(2) + await credentialProtocol.processCredential(messageContext) }) }) @@ -808,8 +753,8 @@ describe('credentialProtocol', () => { expect(repositoryDeleteSpy).toHaveBeenNthCalledWith(1, agentContext, credentialRecord) }) - it('should call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is true', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + it('should call deleteCredentialById in testCredentialFormatService if deleteAssociatedCredential is true', async () => { + const deleteCredentialMock = mockFunction(testCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) @@ -826,8 +771,8 @@ describe('credentialProtocol', () => { ) }) - it('should not call deleteCredentialById in indyCredentialFormatService if deleteAssociatedCredential is false', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + it('should not call deleteCredentialById in testCredentialFormatService if deleteAssociatedCredential is false', async () => { + const deleteCredentialMock = mockFunction(testCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) @@ -841,7 +786,7 @@ describe('credentialProtocol', () => { }) it('deleteAssociatedCredentials should default to true', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + const deleteCredentialMock = mockFunction(testCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) @@ -855,7 +800,7 @@ describe('credentialProtocol', () => { ) }) it('deleteAssociatedDidCommMessages should default to true', async () => { - const deleteCredentialMock = mockFunction(indyCredentialFormatService.deleteCredentialById) + const deleteCredentialMock = mockFunction(testCredentialFormatService.deleteCredentialById) const credentialRecord = mockCredentialRecord() mockFunction(credentialRepository.getById).mockResolvedValue(credentialRecord) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts index 5ae8e56632..84d0a05779 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts @@ -1,4 +1,7 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import type { AgentContext } from '../../../../../agent' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' +import type { CredentialFormat, CredentialFormatCreateOfferOptions, CredentialFormatService } from '../../../formats' import type { CreateCredentialOfferOptions } from '../../CredentialProtocolOptions' import { Subject } from 'rxjs' @@ -12,25 +15,70 @@ import { DidCommMessageRepository } from '../../../../../storage' import { JsonTransformer } from '../../../../../utils' import { DidExchangeState } from '../../../../connections' import { ConnectionService } from '../../../../connections/services/ConnectionService' -import { IndyLedgerService } from '../../../../ledger/services' import { RoutingService } from '../../../../routing/services/RoutingService' import { CredentialEventTypes } from '../../../CredentialEvents' -import { credDef, schema } from '../../../__tests__/fixtures' -import { IndyCredentialFormatService } from '../../../formats/indy/IndyCredentialFormatService' -import { JsonLdCredentialFormatService } from '../../../formats/jsonld/JsonLdCredentialFormatService' import { CredentialFormatSpec } from '../../../models' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { CredentialRepository } from '../../../repository/CredentialRepository' -import { V1CredentialPreview } from '../../v1/messages/V1CredentialPreview' import { V2CredentialProtocol } from '../V2CredentialProtocol' +import { V2CredentialPreview } from '../messages' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' +const offerFormat = new CredentialFormatSpec({ + attachmentId: 'offer-attachment-id', + format: 'hlindy/cred-abstract@v2.0', +}) + +const offerAttachment = new Attachment({ + id: 'offer-attachment-id', + mimeType: 'application/json', + data: new AttachmentData({ + base64: + 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', + }), +}) + +interface TestCredentialFormat extends CredentialFormat { + formatKey: 'test' + credentialRecordType: 'test' +} + +type TestCredentialFormatService = CredentialFormatService + +export const testCredentialFormatService = { + credentialRecordType: 'test', + formatKey: 'test', + supportsFormat: (_format: string) => true, + createOffer: async ( + _agentContext: AgentContext, + _options: CredentialFormatCreateOfferOptions + ) => ({ + attachment: offerAttachment, + format: offerFormat, + previewAttributes: [ + { + mimeType: 'text/plain', + name: 'name', + value: 'John', + }, + { + mimeType: 'text/plain', + name: 'age', + value: '99', + }, + ], + }), + acceptRequest: jest.fn(), + deleteCredentialById: jest.fn(), + processCredential: jest.fn(), + acceptOffer: jest.fn(), + processRequest: jest.fn(), + processOffer: jest.fn(), +} as unknown as TestCredentialFormatService + // Mock classes jest.mock('../../../repository/CredentialRepository') -jest.mock('../../../../ledger/services/IndyLedgerService') -jest.mock('../../../formats/indy/IndyCredentialFormatService') -jest.mock('../../../formats/jsonld/JsonLdCredentialFormatService') jest.mock('../../../../../storage/didcomm/DidCommMessageRepository') jest.mock('../../../../routing/services/RoutingService') jest.mock('../../../../connections/services/ConnectionService') @@ -38,9 +86,6 @@ jest.mock('../../../../../agent/Dispatcher') // Mock typed object const CredentialRepositoryMock = CredentialRepository as jest.Mock -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock -const IndyCredentialFormatServiceMock = IndyCredentialFormatService as jest.Mock -const JsonLdCredentialFormatServiceMock = JsonLdCredentialFormatService as jest.Mock const DidCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock const RoutingServiceMock = RoutingService as jest.Mock const ConnectionServiceMock = ConnectionService as jest.Mock @@ -49,19 +94,9 @@ const DispatcherMock = Dispatcher as jest.Mock const credentialRepository = new CredentialRepositoryMock() const didCommMessageRepository = new DidCommMessageRepositoryMock() const routingService = new RoutingServiceMock() -const indyLedgerService = new IndyLedgerServiceMock() -const indyCredentialFormatService = new IndyCredentialFormatServiceMock() -const jsonLdCredentialFormatService = new JsonLdCredentialFormatServiceMock() const dispatcher = new DispatcherMock() const connectionService = new ConnectionServiceMock() -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -indyCredentialFormatService.formatKey = 'indy' - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -jsonLdCredentialFormatService.formatKey = 'jsonld' const agentConfig = getAgentConfig('V2CredentialProtocolOfferTest') const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) @@ -70,7 +105,6 @@ const agentContext = getAgentContext({ [CredentialRepository, credentialRepository], [DidCommMessageRepository, didCommMessageRepository], [RoutingService, routingService], - [IndyLedgerService, indyLedgerService], [Dispatcher, dispatcher], [ConnectionService, connectionService], [EventEmitter, eventEmitter], @@ -83,35 +117,15 @@ const connectionRecord = getMockConnection({ state: DidExchangeState.Completed, }) -const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', -}) -const offerFormat = new CredentialFormatSpec({ - attachmentId: 'offer-attachment-id', - format: 'hlindy/cred-abstract@v2.0', -}) - -const offerAttachment = new Attachment({ - id: 'offer-attachment-id', - mimeType: 'application/json', - data: new AttachmentData({ - base64: - 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', - }), -}) - describe('V2CredentialProtocolOffer', () => { let credentialProtocol: V2CredentialProtocol beforeEach(async () => { // mock function implementations mockFunction(connectionService.getById).mockResolvedValue(connectionRecord) - mockFunction(indyLedgerService.getCredentialDefinition).mockResolvedValue(credDef) - mockFunction(indyLedgerService.getSchema).mockResolvedValue(schema) credentialProtocol = new V2CredentialProtocol({ - credentialFormats: [indyCredentialFormatService, jsonLdCredentialFormatService], + credentialFormats: [testCredentialFormatService], }) }) @@ -120,24 +134,15 @@ describe('V2CredentialProtocolOffer', () => { }) describe('createOffer', () => { - const offerOptions: CreateCredentialOfferOptions<[IndyCredentialFormatService]> = { + const offerOptions: CreateCredentialOfferOptions<[TestCredentialFormatService]> = { comment: 'some comment', connectionRecord, credentialFormats: { - indy: { - attributes: credentialPreview.attributes, - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - }, + test: {}, }, } test(`creates credential record in ${CredentialState.OfferSent} state with offer, thread ID`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.createOffer).mockResolvedValue({ - attachment: offerAttachment, - format: offerFormat, - }) - // when await credentialProtocol.createOffer(agentContext, offerOptions) @@ -156,12 +161,6 @@ describe('V2CredentialProtocolOffer', () => { }) test(`emits stateChange event with a new credential in ${CredentialState.OfferSent} state`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.createOffer).mockResolvedValue({ - attachment: offerAttachment, - format: offerFormat, - }) - const eventListenerMock = jest.fn() eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) @@ -182,13 +181,6 @@ describe('V2CredentialProtocolOffer', () => { }) test('returns credential offer message', async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - mockFunction(indyCredentialFormatService.createOffer).mockResolvedValue({ - attachment: offerAttachment, - format: offerFormat, - previewAttributes: credentialPreview.attributes, - }) - const { message: credentialOffer } = await credentialProtocol.createOffer(agentContext, offerOptions) expect(credentialOffer.toJSON()).toMatchObject({ @@ -220,7 +212,9 @@ describe('V2CredentialProtocolOffer', () => { const credentialOfferMessage = new V2OfferCredentialMessage({ formats: [offerFormat], comment: 'some comment', - credentialPreview, + credentialPreview: new V2CredentialPreview({ + attributes: [], + }), offerAttachments: [offerAttachment], }) @@ -230,8 +224,6 @@ describe('V2CredentialProtocolOffer', () => { }) test(`creates and return credential record in ${CredentialState.OfferReceived} state with offer, thread ID`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - // when await credentialProtocol.processOffer(messageContext) @@ -251,8 +243,6 @@ describe('V2CredentialProtocolOffer', () => { }) test(`emits stateChange event with ${CredentialState.OfferReceived}`, async () => { - mockFunction(indyCredentialFormatService.supportsFormat).mockReturnValue(true) - const eventListenerMock = jest.fn() eventEmitter.on(CredentialEventTypes.CredentialStateChanged, eventListenerMock) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index 0c62263961..95c48a3ee9 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -1,4 +1,5 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import type { CredentialStateChangedEvent } from '../../../CredentialEvents' import type { AcceptCredentialOfferOptions, AcceptCredentialRequestOptions } from '../../../CredentialsApiOptions' @@ -6,7 +7,11 @@ import { ReplaySubject, Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' -import { prepareForIssuance, waitForCredentialRecordSubject, getAgentOptions } from '../../../../../../tests/helpers' +import { + getLegacyAnonCredsModules, + prepareForAnonCredsIssuance, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForCredentialRecordSubject, getAgentOptions } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' import { Agent } from '../../../../../agent/Agent' import { CredentialEventTypes } from '../../../CredentialEvents' @@ -15,13 +20,21 @@ import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { V2CredentialPreview } from '../messages' -const faberAgentOptions = getAgentOptions('Faber connection-less Credentials V2', { - endpoints: ['rxjs:faber'], -}) - -const aliceAgentOptions = getAgentOptions('Alice connection-less Credentials V2', { - endpoints: ['rxjs:alice'], -}) +const faberAgentOptions = getAgentOptions( + 'Faber connection-less Credentials V2', + { + endpoints: ['rxjs:faber'], + }, + getLegacyAnonCredsModules() +) + +const aliceAgentOptions = getAgentOptions( + 'Alice connection-less Credentials V2', + { + endpoints: ['rxjs:alice'], + }, + getLegacyAnonCredsModules() +) const credentialPreview = V2CredentialPreview.fromRecord({ name: 'John', @@ -29,8 +42,8 @@ const credentialPreview = V2CredentialPreview.fromRecord({ }) describe('V2 Connectionless Credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent let faberReplay: ReplaySubject let aliceReplay: ReplaySubject let credentialDefinitionId: string @@ -53,8 +66,11 @@ describe('V2 Connectionless Credentials', () => { aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - const { definition } = await prepareForIssuance(faberAgent, ['name', 'age']) - credentialDefinitionId = definition.id + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { + issuerId: faberAgent.publicDid?.did as string, + attributeNames: ['name', 'age'], + }) + credentialDefinitionId = credentialDefinition.credentialDefinitionId faberReplay = new ReplaySubject() aliceReplay = new ReplaySubject() @@ -144,14 +160,14 @@ describe('V2 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], @@ -165,7 +181,7 @@ describe('V2 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { credentialDefinitionId, }, }, @@ -225,14 +241,14 @@ describe('V2 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { credentialDefinitionId: credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts index 0bd25c345e..8d1b7cda57 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts @@ -1,23 +1,24 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { AcceptCredentialOfferOptions, AcceptCredentialProposalOptions } from '../../../CredentialsApiOptions' -import type { Schema } from 'indy-sdk' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' -import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' +import { setupAnonCredsTests } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForCredentialRecord, waitForCredentialRecordSubject } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' -import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { AutoAcceptCredential } from '../../../models/CredentialAutoAcceptType' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { V2CredentialPreview } from '../messages/V2CredentialPreview' -describe('v2 credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let schema: Schema - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord +describe('V2 Credentials Auto Accept', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let credentialDefinitionId: string + let schemaId: string + let faberConnectionId: string + let aliceConnectionId: string + const credentialPreview = V2CredentialPreview.fromRecord({ name: 'John', age: '99', @@ -31,13 +32,23 @@ describe('v2 credentials', () => { profile_picture: 'another profile picture', }) - describe('Auto accept on `always`', () => { + describe("Auto accept on 'always'", () => { beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: always v2', - 'alice agent: always v2', - AutoAcceptCredential.Always - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + schemaId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'faber agent: always v2', + holderName: 'alice agent: always v2', + autoAcceptCredentials: AutoAcceptCredential.Always, + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + })) }) afterAll(async () => { @@ -47,35 +58,31 @@ describe('v2 credentials', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { - testLogger.test('Alice begins listening for credential') - const aliceCredReceivedPromise = waitForCredentialRecord(aliceAgent, { - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Faber begins listening for credential ack') - const faberCredAckPromise = waitForCredentialRecord(faberAgent, { - state: CredentialState.Done, - }) - + test("Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on 'always'", async () => { testLogger.test('Alice sends credential proposal to Faber') - await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, comment: 'v2 propose credential test', }) testLogger.test('Alice waits for credential from Faber') - let aliceCredentialRecord = await aliceCredReceivedPromise + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + state: CredentialState.Done, + threadId: aliceCredentialRecord.threadId, + }) testLogger.test('Faber waits for credential ack from Alice') - aliceCredentialRecord = await faberCredAckPromise + await waitForCredentialRecordSubject(faberReplay, { + state: CredentialState.Done, + threadId: aliceCredentialRecord.threadId, + }) expect(aliceCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, @@ -83,9 +90,9 @@ describe('v2 credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { - schemaId: schema.id, - credentialDefinitionId: credDefId, + '_anonCreds/anonCredsCredential': { + schemaId, + credentialDefinitionId: credentialDefinitionId, }, }, }, @@ -93,48 +100,42 @@ describe('v2 credentials', () => { }) }) - test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `always`', async () => { - testLogger.test('Alice begins listening for credential') - const aliceCredReceivedPromise = waitForCredentialRecord(aliceAgent, { - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Faber begins listening for credential ack') - const faberCredAckPromise = waitForCredentialRecord(faberAgent, { - state: CredentialState.Done, - }) - + test("Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on 'always'", async () => { testLogger.test('Faber sends credential offer to Alice') - const schemaId = schema.id - await faberAgent.credentials.offerCredential({ + let faberCredentialRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v2', }) + testLogger.test('Alice waits for credential from Faber') - const aliceCredentialRecord = await aliceCredReceivedPromise + const aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + state: CredentialState.CredentialReceived, + threadId: faberCredentialRecord.threadId, + }) + expect(aliceCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), metadata: { data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { + '_anonCreds/anonCredsCredentialRequest': expect.any(Object), + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], @@ -142,7 +143,10 @@ describe('v2 credentials', () => { }) testLogger.test('Faber waits for credential ack from Alice') - const faberCredentialRecord: CredentialExchangeRecord = await faberCredAckPromise + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + state: CredentialState.Done, + threadId: faberCredentialRecord.threadId, + }) expect(faberCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), @@ -152,13 +156,24 @@ describe('v2 credentials', () => { }) }) - describe('Auto accept on `contentApproved`', () => { + describe("Auto accept on 'contentApproved'", () => { + // FIXME: we don't need to set up the agent and create all schemas/credential definitions again, just change the auto accept credential setting beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: contentApproved v2', - 'alice agent: contentApproved v2', - AutoAcceptCredential.ContentApproved - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + schemaId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Agent: Always V2', + holderName: 'Alice Agent: Always V2', + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + })) }) afterAll(async () => { @@ -168,90 +183,80 @@ describe('v2 credentials', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { + test("Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on 'contentApproved'", async () => { testLogger.test('Alice sends credential proposal to Faber') - const schemaId = schema.id - - testLogger.test('Faber starts listening for credential proposal from Alice') - const faberPropReceivedPromise = waitForCredentialRecord(faberAgent, { - state: CredentialState.ProposalReceived, - }) - await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }) testLogger.test('Faber waits for credential proposal from Alice') - const faberPropReceivedRecord = await faberPropReceivedPromise - - const aliceCredReceivedPromise = waitForCredentialRecord(aliceAgent, { - threadId: faberPropReceivedRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - const faberCredAckPromise = waitForCredentialRecord(faberAgent, { - threadId: faberPropReceivedRecord.threadId, - state: CredentialState.Done, + let faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + state: CredentialState.ProposalReceived, + threadId: aliceCredentialRecord.threadId, }) - const options: AcceptCredentialProposalOptions = { - credentialRecordId: faberPropReceivedRecord.id, + testLogger.test('Faber sends credential offer to Alice') + await faberAgent.credentials.acceptProposal({ + credentialRecordId: faberCredentialRecord.id, comment: 'V2 Indy Offer', credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, }, - } - testLogger.test('Faber sends credential offer to Alice') - options.credentialRecordId = faberPropReceivedRecord.id - await faberAgent.credentials.acceptProposal(options) + }) testLogger.test('Alice waits for credential from Faber') - const aliceCredReceivedRecord = await aliceCredReceivedPromise + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + state: CredentialState.Done, + threadId: faberCredentialRecord.threadId, + }) - expect(aliceCredReceivedRecord).toMatchObject({ + faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(aliceCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), metadata: { data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { + '_anonCreds/anonCredsCredentialRequest': expect.any(Object), + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], - state: CredentialState.CredentialReceived, + state: CredentialState.Done, }) - testLogger.test('Faber waits for credential ack from Alice') - const faberCredAckRecord = await faberCredAckPromise - - expect(faberCredAckRecord).toMatchObject({ + expect(faberCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), metadata: { data: { - '_internal/indyCredential': { + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }, @@ -259,86 +264,77 @@ describe('v2 credentials', () => { }) }) - test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `contentApproved`', async () => { - testLogger.test('Alice starts listening for credential offer from Faber') - const aliceOfferReceivedPromise = waitForCredentialRecord(aliceAgent, { - state: CredentialState.OfferReceived, - }) - + test("Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on 'contentApproved'", async () => { testLogger.test('Faber sends credential offer to Alice') - const schemaId = schema.id - await faberAgent.credentials.offerCredential({ + let faberCredentialRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v2', }) testLogger.test('Alice waits for credential offer from Faber') - const aliceOfferReceivedRecord = await aliceOfferReceivedPromise - - expect(JsonTransformer.toJSON(aliceOfferReceivedRecord)).toMatchObject({ + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { state: CredentialState.OfferReceived, + threadId: faberCredentialRecord.threadId, }) // below values are not in json object - expect(aliceOfferReceivedRecord.id).not.toBeNull() - expect(aliceOfferReceivedRecord.getTags()).toEqual({ - threadId: aliceOfferReceivedRecord.threadId, - state: aliceOfferReceivedRecord.state, - connectionId: aliceConnection.id, + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnectionId, credentialIds: [], }) testLogger.test('Alice received credential offer from Faber') - testLogger.test('Alice starts listening for credential from Faber') - const aliceCredReceivedPromise = waitForCredentialRecord(aliceAgent, { - state: CredentialState.CredentialReceived, + testLogger.test('alice sends credential request to faber') + await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, }) - const faberCredAckPromise = waitForCredentialRecord(faberAgent, { + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { state: CredentialState.Done, + threadId: faberCredentialRecord.threadId, }) - const acceptOfferOptions: AcceptCredentialOfferOptions = { - credentialRecordId: aliceOfferReceivedRecord.id, - } - testLogger.test('alice sends credential request to faber') - await aliceAgent.credentials.acceptOffer(acceptOfferOptions) - - testLogger.test('Alice waits for credential from Faber') - const aliceCredReceivedRecord = await aliceCredReceivedPromise - expect(aliceCredReceivedRecord).toMatchObject({ + expect(aliceCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), metadata: { data: { - '_internal/indyRequest': expect.any(Object), - '_internal/indyCredential': { + '_anonCreds/anonCredsCredentialRequest': expect.any(Object), + '_anonCreds/anonCredsCredential': { schemaId, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, }, credentials: [ { - credentialRecordType: 'indy', + credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String), }, ], - state: CredentialState.CredentialReceived, + state: CredentialState.Done, }) testLogger.test('Faber waits for credential ack from Alice') - const faberCredAckRecord = await faberCredAckPromise - expect(faberCredAckRecord).toMatchObject({ + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + + expect(faberCredentialRecord).toMatchObject({ type: CredentialExchangeRecord.type, id: expect.any(String), createdAt: expect.any(Date), @@ -346,122 +342,99 @@ describe('v2 credentials', () => { }) }) - test('Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { - testLogger.test('Faber starts listening for proposal from Alice') - const faberPropReceivedPromise = waitForCredentialRecord(faberAgent, { - state: CredentialState.ProposalReceived, - }) - + test("Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on 'contentApproved' and attributes did change", async () => { testLogger.test('Alice sends credential proposal to Faber') - const aliceCredProposal = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, comment: 'v2 propose credential test', }) - expect(aliceCredProposal.state).toBe(CredentialState.ProposalSent) + expect(aliceCredentialRecord.state).toBe(CredentialState.ProposalSent) testLogger.test('Faber waits for credential proposal from Alice') - const faberPropReceivedRecord = await faberPropReceivedPromise - - testLogger.test('Alice starts listening for credential offer from Faber') - const aliceOfferReceivedPromise = waitForCredentialRecord(aliceAgent, { - state: CredentialState.OfferReceived, + let faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + state: CredentialState.ProposalReceived, + threadId: aliceCredentialRecord.threadId, }) testLogger.test('Faber negotiated proposal, sending credential offer to Alice') - const faberOfferSentRecord = await faberAgent.credentials.negotiateProposal({ - credentialRecordId: faberPropReceivedRecord.id, + faberCredentialRecord = await faberAgent.credentials.negotiateProposal({ + credentialRecordId: faberCredentialRecord.id, credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: newCredentialPreview.attributes, }, }, }) testLogger.test('Alice waits for credential offer from Faber') - const aliceOfferReceivedRecord = await aliceOfferReceivedPromise + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + state: CredentialState.OfferReceived, + threadId: faberCredentialRecord.threadId, + }) // below values are not in json object - expect(aliceOfferReceivedRecord.id).not.toBeNull() - expect(aliceOfferReceivedRecord.getTags()).toEqual({ - threadId: aliceOfferReceivedRecord.threadId, - state: aliceOfferReceivedRecord.state, - connectionId: aliceConnection.id, + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnectionId, credentialIds: [], }) - - // Check if the state of the credential records did not change - const faberRecord = await faberAgent.credentials.getById(faberOfferSentRecord.id) - faberRecord.assertState(CredentialState.OfferSent) - - const aliceRecord = await aliceAgent.credentials.getById(aliceOfferReceivedRecord.id) - aliceRecord.assertState(CredentialState.OfferReceived) }) - test('Faber starts with V2 credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { - testLogger.test('Alice starts listening for offer from Faber') - const aliceCredentialExchangeRecordPromise = waitForCredentialRecord(aliceAgent, { - state: CredentialState.OfferReceived, - }) - + test("Faber starts with V2 credential offer to Alice, both have autoAcceptCredential on 'contentApproved' and attributes did change", async () => { testLogger.test('Faber sends credential offer to Alice') - await faberAgent.credentials.offerCredential({ + const faberCredentialRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v2', }) testLogger.test('Alice waits for credential offer from Faber') - const aliceOfferReceivedRecord = await aliceCredentialExchangeRecordPromise + const aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + state: CredentialState.OfferReceived, + threadId: faberCredentialRecord.threadId, + }) // below values are not in json object - expect(aliceOfferReceivedRecord.id).not.toBeNull() - expect(aliceOfferReceivedRecord.getTags()).toEqual({ - threadId: aliceOfferReceivedRecord.threadId, - state: aliceOfferReceivedRecord.state, - connectionId: aliceConnection.id, + expect(aliceCredentialRecord.id).not.toBeNull() + expect(aliceCredentialRecord.getTags()).toEqual({ + threadId: aliceCredentialRecord.threadId, + state: aliceCredentialRecord.state, + connectionId: aliceConnectionId, credentialIds: [], }) - testLogger.test('Faber starts listening for proposal received') - const faberProposalReceivedPromise = waitForCredentialRecord(faberAgent, { - state: CredentialState.ProposalReceived, - }) - testLogger.test('Alice sends credential request to Faber') - const aliceCredRequestRecord = await aliceAgent.credentials.negotiateOffer({ - credentialRecordId: aliceOfferReceivedRecord.id, + await aliceAgent.credentials.negotiateOffer({ + credentialRecordId: aliceCredentialRecord.id, credentialFormats: { indy: { attributes: newCredentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, comment: 'v2 propose credential test', }) - testLogger.test('Faber waits for credential proposal from Alice') - const faberCredProposalRecord = await faberProposalReceivedPromise - - // Check if the state of fabers credential record did not change - const faberRecord = await faberAgent.credentials.getById(faberCredProposalRecord.id) - faberRecord.assertState(CredentialState.ProposalReceived) - - const aliceRecord = await aliceAgent.credentials.getById(aliceCredRequestRecord.id) - aliceRecord.assertState(CredentialState.ProposalSent) + await waitForCredentialRecordSubject(faberReplay, { + state: CredentialState.ProposalReceived, + threadId: aliceCredentialRecord.threadId, + }) }) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts index c3b73fd6ef..202368c089 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials.e2e.test.ts @@ -1,27 +1,25 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { IndyCredPropose } from '../../../formats/indy/models/IndyCredPropose' -import type { ReplaySubject } from 'rxjs' +import type { AnonCredsHolderService } from '../../../../../../../anoncreds/src' +import type { LegacyIndyProposeCredentialFormat } from '../../../../../../../anoncreds/src/formats/LegacyIndyCredentialFormat' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' +import { AnonCredsHolderServiceSymbol } from '../../../../../../../anoncreds/src' import { - issueCredential, - setupCredentialTests, - waitForCredentialRecord, - waitForCredentialRecordSubject, -} from '../../../../../../tests/helpers' + issueLegacyAnonCredsCredential, + setupAnonCredsTests, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForCredentialRecord, waitForCredentialRecordSubject } from '../../../../../../tests' import testLogger from '../../../../../../tests/logger' import { DidCommMessageRepository } from '../../../../../storage' import { JsonTransformer } from '../../../../../utils' -import { IndyHolderService } from '../../../../indy/services/IndyHolderService' import { CredentialState } from '../../../models/CredentialState' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import { + V2CredentialPreview, V2IssueCredentialMessage, + V2OfferCredentialMessage, V2ProposeCredentialMessage, V2RequestCredentialMessage, - V2CredentialPreview, - V2OfferCredentialMessage, } from '../messages' const credentialPreview = V2CredentialPreview.fromRecord({ @@ -32,17 +30,16 @@ const credentialPreview = V2CredentialPreview.fromRecord({ }) describe('v2 credentials', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let aliceCredentialRecord: CredentialExchangeRecord - let faberCredentialRecord: CredentialExchangeRecord - let faberReplay: ReplaySubject - let aliceReplay: ReplaySubject - - let credPropose: IndyCredPropose + let faberAgent: AnonCredsTestsAgent + let aliceAgent: AnonCredsTestsAgent + let credentialDefinitionId: string + let faberConnectionId: string + let aliceConnectionId: string + + let faberReplay: EventReplaySubject + let aliceReplay: EventReplaySubject + + let indyCredentialProposal: LegacyIndyProposeCredentialFormat const newCredentialPreview = V2CredentialPreview.fromRecord({ name: 'John', @@ -52,11 +49,22 @@ describe('v2 credentials', () => { }) beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, faberReplay, aliceReplay } = - await setupCredentialTests('Faber Agent Credentials v2', 'Alice Agent Credentials v2')) - - credPropose = { - credentialDefinitionId: credDefId, + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Agent Credentials v2', + holderName: 'Alice Agent Credentials v2', + attributeNames: ['name', 'age', 'x-ray', 'profile_picture'], + })) + + indyCredentialProposal = { + credentialDefinitionId: credentialDefinitionId, schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', schemaName: 'ahoy', schemaVersion: '1.0', @@ -76,7 +84,7 @@ describe('v2 credentials', () => { testLogger.test('Alice sends (v2) credential proposal to Faber') const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { indy: { @@ -93,14 +101,14 @@ describe('v2 credentials', () => { }) expect(credentialExchangeRecord).toMatchObject({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', state: CredentialState.ProposalSent, threadId: expect.any(String), }) testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + let faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: credentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) @@ -111,14 +119,14 @@ describe('v2 credentials', () => { comment: 'V2 Indy Proposal', credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, }, }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) @@ -180,7 +188,7 @@ describe('v2 credentials', () => { }) expect(offerCredentialExchangeRecord).toMatchObject({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', state: CredentialState.RequestSent, threadId: expect.any(String), @@ -216,34 +224,36 @@ describe('v2 credentials', () => { }) test('Faber issues credential which is then deleted from Alice`s wallet', async () => { - const { holderCredential } = await issueCredential({ + const { holderCredentialExchangeRecord } = await issueLegacyAnonCredsCredential({ issuerAgent: faberAgent, - issuerConnectionId: faberConnection.id, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnectionId, holderAgent: aliceAgent, - credentialTemplate: { - credentialDefinitionId: credDefId, + holderReplay: aliceReplay, + offer: { + credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, }) // test that delete credential removes from both repository and wallet - // latter is tested by spying on holder service (Indy) to + // latter is tested by spying on holder service to // see if deleteCredential is called - const holderService = aliceAgent.dependencyManager.resolve(IndyHolderService) + const holderService = aliceAgent.dependencyManager.resolve(AnonCredsHolderServiceSymbol) const deleteCredentialSpy = jest.spyOn(holderService, 'deleteCredential') - await aliceAgent.credentials.deleteById(holderCredential.id, { + await aliceAgent.credentials.deleteById(holderCredentialExchangeRecord.id, { deleteAssociatedCredentials: true, deleteAssociatedDidCommMessages: true, }) expect(deleteCredentialSpy).toHaveBeenNthCalledWith( 1, aliceAgent.context, - holderCredential.credentials[0].credentialRecordId + holderCredentialExchangeRecord.credentials[0].credentialRecordId ) - return expect(aliceAgent.credentials.getById(holderCredential.id)).rejects.toThrowError( - `CredentialRecord: record with id ${holderCredential.id} not found.` + return expect(aliceAgent.credentials.getById(holderCredentialExchangeRecord.id)).rejects.toThrowError( + `CredentialRecord: record with id ${holderCredentialExchangeRecord.id} not found.` ) }) @@ -256,11 +266,11 @@ describe('v2 credentials', () => { testLogger.test('Alice sends credential proposal to Faber') let aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { indy: { - ...credPropose, + ...indyCredentialProposal, attributes: credentialPreview.attributes, }, }, @@ -280,7 +290,7 @@ describe('v2 credentials', () => { credentialRecordId: faberCredentialRecord.id, credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: newCredentialPreview.attributes, }, }, @@ -306,7 +316,7 @@ describe('v2 credentials', () => { credentialRecordId: aliceCredentialRecord.id, credentialFormats: { indy: { - ...credPropose, + ...indyCredentialProposal, attributes: newCredentialPreview.attributes, }, }, @@ -326,7 +336,7 @@ describe('v2 credentials', () => { credentialRecordId: faberCredentialRecord.id, credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: newCredentialPreview.attributes, }, }, @@ -341,7 +351,7 @@ describe('v2 credentials', () => { }) expect(offerCredentialExchangeRecord).toMatchObject({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, state: CredentialState.RequestSent, protocolVersion: 'v2', threadId: aliceCredentialExchangeRecord.threadId, @@ -391,18 +401,18 @@ describe('v2 credentials', () => { testLogger.test('Faber sends credential offer to Alice') let faberCredentialRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v2', }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await aliceCredentialRecordPromise + let aliceCredentialRecord = await aliceCredentialRecordPromise let faberCredentialRecordPromise = waitForCredentialRecord(faberAgent, { threadId: aliceCredentialRecord.threadId, @@ -413,7 +423,7 @@ describe('v2 credentials', () => { credentialRecordId: aliceCredentialRecord.id, credentialFormats: { indy: { - ...credPropose, + ...indyCredentialProposal, attributes: newCredentialPreview.attributes, }, }, @@ -432,7 +442,7 @@ describe('v2 credentials', () => { credentialRecordId: faberCredentialRecord.id, credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: newCredentialPreview.attributes, }, }, @@ -451,7 +461,7 @@ describe('v2 credentials', () => { credentialRecordId: aliceCredentialRecord.id, credentialFormats: { indy: { - ...credPropose, + ...indyCredentialProposal, attributes: newCredentialPreview.attributes, }, }, @@ -473,7 +483,7 @@ describe('v2 credentials', () => { comment: 'V2 Indy Proposal', credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, attributes: credentialPreview.attributes, }, }, @@ -492,7 +502,7 @@ describe('v2 credentials', () => { }) expect(offerCredentialExchangeRecord).toMatchObject({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, state: CredentialState.RequestSent, protocolVersion: 'v2', }) @@ -630,18 +640,18 @@ describe('v2 credentials', () => { testLogger.test('Faber sends credential offer to Alice') const faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { indy: { attributes: credentialPreview.attributes, - credentialDefinitionId: credDefId, + credentialDefinitionId: credentialDefinitionId, }, }, protocolVersion: 'v2', }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts index ea148db552..7472cca215 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts @@ -1,70 +1,16 @@ -import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import type { Wallet } from '../../../../../wallet' -import type { CredentialStateChangedEvent } from '../../../CredentialEvents' -import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' +import type { EventReplaySubject, JsonLdTestsAgent } from '../../../../../../tests' import type { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' -import { ReplaySubject, Subject } from 'rxjs' - -import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' -import { getAgentOptions, prepareForIssuance, waitForCredentialRecordSubject } from '../../../../../../tests/helpers' +import { setupJsonLdTests, waitForCredentialRecordSubject } from '../../../../../../tests' import testLogger from '../../../../../../tests/logger' -import { Agent } from '../../../../../agent/Agent' -import { InjectionSymbols } from '../../../../../constants' import { KeyType } from '../../../../../crypto' import { TypedArrayEncoder } from '../../../../../utils' -import { JsonEncoder } from '../../../../../utils/JsonEncoder' -import { W3cVcModule } from '../../../../vc' -import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader' import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc/constants' -import { CredentialEventTypes } from '../../../CredentialEvents' -import { CredentialsModule } from '../../../CredentialsModule' -import { JsonLdCredentialFormatService } from '../../../formats' import { CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository' -import { V2CredentialProtocol } from '../V2CredentialProtocol' - -const faberAgentOptions = getAgentOptions( - 'Faber LD connection-less Credentials V2', - { - endpoints: ['rxjs:faber'], - }, - { - credentials: new CredentialsModule({ - credentialProtocols: [new V2CredentialProtocol({ credentialFormats: [new JsonLdCredentialFormatService()] })], - }), - w3cVc: new W3cVcModule({ - documentLoader: customDocumentLoader, - }), - } -) - -const aliceAgentOptions = getAgentOptions( - 'Alice LD connection-less Credentials V2', - { - endpoints: ['rxjs:alice'], - }, - { - credentials: new CredentialsModule({ - credentialProtocols: [new V2CredentialProtocol({ credentialFormats: [new JsonLdCredentialFormatService()] })], - }), - w3cVc: new W3cVcModule({ - documentLoader: customDocumentLoader, - }), - } -) - -let wallet -let signCredentialOptions: JsonLdCredentialDetailFormat -describe('credentials', () => { - let faberAgent: Agent<(typeof faberAgentOptions)['modules']> - let aliceAgent: Agent<(typeof aliceAgentOptions)['modules']> - let faberReplay: ReplaySubject - let aliceReplay: ReplaySubject - const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') - const TEST_LD_DOCUMENT: JsonCredential = { +const signCredentialOptions = { + credential: { '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], type: ['VerifiableCredential', 'UniversityDegreeCredential'], issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', @@ -75,47 +21,35 @@ describe('credentials', () => { name: 'Bachelor of Science and Arts', }, }, - } + }, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, +} + +describe('credentials', () => { + let faberAgent: JsonLdTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: JsonLdTestsAgent + let aliceReplay: EventReplaySubject + beforeEach(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await aliceAgent.initialize() - - await prepareForIssuance(faberAgent, ['name', 'age']) - - faberReplay = new ReplaySubject() - aliceReplay = new ReplaySubject() - - faberAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(faberReplay) - aliceAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(aliceReplay) - wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - - await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) - - signCredentialOptions = { - credential: TEST_LD_DOCUMENT, - options: { - proofType: 'Ed25519Signature2018', - proofPurpose: 'assertionMethod', - }, - } + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + } = await setupJsonLdTests({ + issuerName: 'Faber LD connection-less Credentials V2', + holderName: 'Alice LD connection-less Credentials V2', + createConnections: false, + })) + + await faberAgent.context.wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + keyType: KeyType.Ed25519, + }) }) afterEach(async () => { @@ -137,36 +71,34 @@ describe('credentials', () => { protocolVersion: 'v2', }) - const offerMsg = message as V2OfferCredentialMessage - const attachment = offerMsg?.offerAttachments[0] - - if (attachment.data.base64) { - expect(JsonEncoder.fromBase64(attachment.data.base64)).toMatchObject({ - credential: { - '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'], - type: ['VerifiableCredential', 'UniversityDegreeCredential'], - issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - issuanceDate: '2017-10-22T12:23:48Z', - credentialSubject: { - degree: { - name: 'Bachelor of Science and Arts', - type: 'BachelorDegree', - }, + const offerMessage = message as V2OfferCredentialMessage + const attachment = offerMessage?.offerAttachments[0] + + expect(attachment?.getDataAsJson()).toMatchObject({ + credential: { + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + degree: { + name: 'Bachelor of Science and Arts', + type: 'BachelorDegree', }, }, - options: { - proofType: 'Ed25519Signature2018', - proofPurpose: 'assertionMethod', - }, - }) - } + }, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, + }) - const { message: offerMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + const { message: connectionlessOfferMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ recordId: faberCredentialRecord.id, message, domain: 'https://a-domain.com', }) - await aliceAgent.receiveMessage(offerMessage.toJSON()) + await aliceAgent.receiveMessage(connectionlessOfferMessage.toJSON()) let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index d8a3521a27..84792602cd 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -1,11 +1,8 @@ -import type { CredentialTestsAgent } from '../../../../../../tests/helpers' -import type { Wallet } from '../../../../../wallet' -import type { ConnectionRecord } from '../../../../connections' -import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' +import type { JsonLdTestsAgent } from '../../../../../../tests' -import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' +import { setupJsonLdTests } from '../../../../../../tests' +import { waitForCredentialRecord } from '../../../../../../tests/helpers' import testLogger from '../../../../../../tests/logger' -import { InjectionSymbols } from '../../../../../constants' import { KeyType } from '../../../../../crypto' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { TypedArrayEncoder } from '../../../../../utils' @@ -13,47 +10,50 @@ import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc/constants' import { AutoAcceptCredential, CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' -const TEST_LD_DOCUMENT: JsonCredential = { - '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], - type: ['VerifiableCredential', 'UniversityDegreeCredential'], - issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - issuanceDate: '2017-10-22T12:23:48Z', - credentialSubject: { - degree: { - type: 'BachelorDegree', - name: 'Bachelor of Science and Arts', +const signCredentialOptions = { + credential: { + '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], + type: ['VerifiableCredential', 'UniversityDegreeCredential'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2017-10-22T12:23:48Z', + credentialSubject: { + degree: { + type: 'BachelorDegree', + name: 'Bachelor of Science and Arts', + }, }, }, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, } -describe('credentials', () => { - let faberAgent: CredentialTestsAgent - let aliceAgent: CredentialTestsAgent - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let aliceCredentialRecord: CredentialExchangeRecord - let signCredentialOptions: JsonLdCredentialDetailFormat - let wallet - const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') - - describe('Auto accept on `always`', () => { +describe('V2 Credentials - JSON-LD - Auto Accept Always', () => { + let faberAgent: JsonLdTestsAgent + let aliceAgent: JsonLdTestsAgent + let faberConnectionId: string + let aliceConnectionId: string + + describe("Auto accept on 'always'", () => { beforeAll(async () => { - ;({ faberAgent, aliceAgent, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: always v2 jsonld', - 'alice agent: always v2 jsonld', - AutoAcceptCredential.Always - )) - - wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) - signCredentialOptions = { - credential: TEST_LD_DOCUMENT, - options: { - proofType: 'Ed25519Signature2018', - proofPurpose: 'assertionMethod', - }, - } + ;({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupJsonLdTests({ + issuerName: 'faber agent: always v2 jsonld', + holderName: 'alice agent: always v2 jsonld', + autoAcceptCredentials: AutoAcceptCredential.Always, + })) + + await faberAgent.context.wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + keyType: KeyType.Ed25519, + }) }) + afterAll(async () => { await faberAgent.shutdown() await faberAgent.wallet.delete() @@ -61,11 +61,11 @@ describe('credentials', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `always`', async () => { + test("Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on 'always'", async () => { testLogger.test('Alice sends credential proposal to Faber') const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { jsonld: signCredentialOptions, @@ -75,7 +75,7 @@ describe('credentials', () => { testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: aliceCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, }) @@ -93,19 +93,19 @@ describe('credentials', () => { state: CredentialState.Done, }) }) - test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `always`', async () => { + test("Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on 'always'", async () => { testLogger.test('Faber sends V2 credential offer to Alice as start of protocol process') const faberCredentialExchangeRecord: CredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { jsonld: signCredentialOptions, }, protocolVersion: 'v2', }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) @@ -136,22 +136,23 @@ describe('credentials', () => { }) }) - describe('Auto accept on `contentApproved`', () => { + describe("Auto accept on 'contentApproved'", () => { beforeAll(async () => { - ;({ faberAgent, aliceAgent, faberConnection, aliceConnection } = await setupCredentialTests( - 'faber agent: content-approved v2 jsonld', - 'alice agent: content-approved v2 jsonld', - AutoAcceptCredential.ContentApproved - )) - wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) - signCredentialOptions = { - credential: TEST_LD_DOCUMENT, - options: { - proofType: 'Ed25519Signature2018', - proofPurpose: 'assertionMethod', - }, - } + ;({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupJsonLdTests({ + issuerName: 'faber agent: ContentApproved v2 jsonld', + holderName: 'alice agent: ContentApproved v2 jsonld', + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + })) + + await faberAgent.context.wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + keyType: KeyType.Ed25519, + }) }) afterAll(async () => { @@ -161,10 +162,10 @@ describe('credentials', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on `contentApproved`', async () => { + test("Alice starts with V2 credential proposal to Faber, both with autoAcceptCredential on 'contentApproved'", async () => { testLogger.test('Alice sends credential proposal to Faber') const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { jsonld: signCredentialOptions, @@ -185,7 +186,7 @@ describe('credentials', () => { }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + const aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.CredentialReceived, }) @@ -213,12 +214,12 @@ describe('credentials', () => { state: CredentialState.Done, }) }) - test('Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on `contentApproved`', async () => { + test("Faber starts with V2 credential offer to Alice, both with autoAcceptCredential on 'contentApproved'", async () => { testLogger.test('Faber sends credential offer to Alice') let faberCredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { jsonld: signCredentialOptions, }, @@ -226,7 +227,7 @@ describe('credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) @@ -236,7 +237,7 @@ describe('credentials', () => { expect(aliceCredentialRecord.getTags()).toEqual({ threadId: aliceCredentialRecord.threadId, state: aliceCredentialRecord.state, - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, credentialIds: [], }) expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) @@ -279,19 +280,19 @@ describe('credentials', () => { state: CredentialState.Done, }) }) - test('Faber starts with V2 credential offer to Alice, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + test("Faber starts with V2 credential offer to Alice, both have autoAcceptCredential on 'contentApproved' and attributes did change", async () => { testLogger.test('Faber sends credential offer to Alice') const faberCredentialExchangeRecord: CredentialExchangeRecord = await faberAgent.credentials.offerCredential({ comment: 'some comment about credential', - connectionId: faberConnection.id, + connectionId: faberConnectionId, credentialFormats: { jsonld: signCredentialOptions, }, protocolVersion: 'v2', }) testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { threadId: faberCredentialExchangeRecord.threadId, state: CredentialState.OfferReceived, }) @@ -301,7 +302,7 @@ describe('credentials', () => { expect(aliceCredentialRecord.getTags()).toEqual({ threadId: aliceCredentialRecord.threadId, state: aliceCredentialRecord.state, - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, credentialIds: [], }) expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) @@ -340,10 +341,10 @@ describe('credentials', () => { aliceCredentialRecord.assertState(CredentialState.ProposalSent) }) - test('Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on `contentApproved` and attributes did change', async () => { + test("Alice starts with V2 credential proposal to Faber, both have autoAcceptCredential on 'contentApproved' and attributes did change", async () => { testLogger.test('Alice sends credential proposal to Faber') const aliceCredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { jsonld: signCredentialOptions, @@ -386,7 +387,7 @@ describe('credentials', () => { expect(record.getTags()).toEqual({ threadId: record.threadId, state: record.state, - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, credentialIds: [], }) expect(record.type).toBe(CredentialExchangeRecord.type) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 98621d7a40..0c7bd46567 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -1,31 +1,52 @@ -import type { Awaited } from '../../../../../types' -import type { Wallet } from '../../../../../wallet' -import type { ConnectionRecord } from '../../../../connections' -import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../../formats/jsonld/JsonLdCredentialFormat' - -import { setupCredentialTests, waitForCredentialRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { InjectionSymbols } from '../../../../../constants' +import type { EventReplaySubject } from '../../../../../../tests' + +import { randomUUID } from 'crypto' + +import { + LegacyIndyCredentialFormatService, + LegacyIndyProofFormatService, + V1CredentialProtocol, + V1ProofProtocol, + AnonCredsModule, +} from '../../../../../../../anoncreds/src' +import { prepareForAnonCredsIssuance } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { + IndySdkAnonCredsRegistry, + IndySdkModule, + IndySdkSovDidRegistrar, + IndySdkSovDidResolver, +} from '../../../../../../../indy-sdk/src' +import { indySdk } from '../../../../../../../indy-sdk/tests/setupIndySdkModule' +import { + setupEventReplaySubjects, + setupSubjectTransports, + genesisPath, + taaAcceptanceMechanism, + taaVersion, + getAgentOptions, + waitForCredentialRecordSubject, + testLogger, + makeConnection, +} from '../../../../../../tests' +import { Agent } from '../../../../../agent/Agent' import { KeyType } from '../../../../../crypto' -import { DidCommMessageRepository } from '../../../../../storage' import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { CacheModule, InMemoryLruCache } from '../../../../cache' +import { DidsModule, KeyDidRegistrar, KeyDidResolver } from '../../../../dids' +import { ProofEventTypes, ProofsModule, V2ProofProtocol } from '../../../../proofs' +import { W3cVcModule } from '../../../../vc' +import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader' +import { CredentialEventTypes } from '../../../CredentialEvents' +import { CredentialsModule } from '../../../CredentialsModule' +import { JsonLdCredentialFormatService } from '../../../formats' import { CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' +import { V2CredentialProtocol } from '../V2CredentialProtocol' import { V2CredentialPreview } from '../messages' -import { V2IssueCredentialMessage } from '../messages/V2IssueCredentialMessage' -import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' -describe('credentials', () => { - let faberAgent: Awaited>['faberAgent'] - let aliceAgent: Awaited>['aliceAgent'] - let aliceConnection: ConnectionRecord - let aliceCredentialRecord: CredentialExchangeRecord - let faberCredentialRecord: CredentialExchangeRecord - - let didCommMessageRepository: DidCommMessageRepository - - const inputDocAsJson: JsonCredential = { +const signCredentialOptions = { + credential: { '@context': [ 'https://www.w3.org/2018/credentials/v1', 'https://w3id.org/citizenship/v1', @@ -53,28 +74,110 @@ describe('credentials', () => { birthCountry: 'Bahamas', birthDate: '1958-07-17', }, - } - - let signCredentialOptions: JsonLdCredentialDetailFormat - - let wallet - const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') - let credDefId: string + }, + options: { + proofType: 'Ed25519Signature2018', + proofPurpose: 'assertionMethod', + }, +} + +const indyCredentialFormat = new LegacyIndyCredentialFormatService() +const jsonLdCredentialFormat = new JsonLdCredentialFormatService() +const indyProofFormat = new LegacyIndyProofFormatService() + +const getIndyJsonLdModules = () => + ({ + credentials: new CredentialsModule({ + credentialProtocols: [ + new V1CredentialProtocol({ indyCredentialFormat }), + new V2CredentialProtocol({ + credentialFormats: [indyCredentialFormat, jsonLdCredentialFormat], + }), + ], + }), + proofs: new ProofsModule({ + proofProtocols: [ + new V1ProofProtocol({ indyProofFormat }), + new V2ProofProtocol({ + proofFormats: [indyProofFormat], + }), + ], + }), + anoncreds: new AnonCredsModule({ + registries: [new IndySdkAnonCredsRegistry()], + }), + dids: new DidsModule({ + resolvers: [new IndySdkSovDidResolver(), new KeyDidResolver()], + registrars: [new IndySdkSovDidRegistrar(), new KeyDidRegistrar()], + }), + indySdk: new IndySdkModule({ + indySdk, + networks: [ + { + isProduction: false, + genesisPath, + id: randomUUID(), + indyNamespace: `pool:localtest`, + transactionAuthorAgreement: { version: taaVersion, acceptanceMechanism: taaAcceptanceMechanism }, + }, + ], + }), + cache: new CacheModule({ + cache: new InMemoryLruCache({ limit: 100 }), + }), + w3cVc: new W3cVcModule({ + documentLoader: customDocumentLoader, + }), + } as const) + +// TODO: extract these very specific tests to the jsonld format +describe('V2 Credentials - JSON-LD - Ed25519', () => { + let faberAgent: Agent> + let faberReplay: EventReplaySubject + let aliceAgent: Agent> + let aliceReplay: EventReplaySubject + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, aliceConnection } = await setupCredentialTests( - 'Faber Agent Credentials LD', - 'Alice Agent Credentials LD' - )) - wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) - signCredentialOptions = { - credential: inputDocAsJson, - options: { - proofType: 'Ed25519Signature2018', - proofPurpose: 'assertionMethod', - }, - } + faberAgent = new Agent( + getAgentOptions( + 'Faber Agent Indy/JsonLD', + { + endpoints: ['rxjs:faber'], + }, + getIndyJsonLdModules() + ) + ) + aliceAgent = new Agent( + getAgentOptions( + 'Alice Agent Indy/JsonLD', + { + endpoints: ['rxjs:alice'], + }, + getIndyJsonLdModules() + ) + ) + + setupSubjectTransports([faberAgent, aliceAgent]) + ;[faberReplay, aliceReplay] = setupEventReplaySubjects( + [faberAgent, aliceAgent], + [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + ) + await faberAgent.initialize() + await aliceAgent.initialize() + ;[, { id: aliceConnectionId }] = await makeConnection(faberAgent, aliceAgent) + + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { + attributeNames: ['name', 'age', 'profile_picture', 'x-ray'], + issuerId: faberAgent.publicDid?.did as string, + }) + credentialDefinitionId = credentialDefinition.credentialDefinitionId + + await faberAgent.context.wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), + keyType: KeyType.Ed25519, + }) }) afterAll(async () => { @@ -87,8 +190,8 @@ describe('credentials', () => { test('Alice starts with V2 (ld format, Ed25519 signature) credential proposal to Faber', async () => { testLogger.test('Alice sends (v2 jsonld) credential proposal to Faber') - const credentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { jsonld: signCredentialOptions, @@ -96,13 +199,13 @@ describe('credentials', () => { comment: 'v2 propose credential test for W3C Credentials', }) - expect(credentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(credentialExchangeRecord.connectionId).toEqual(aliceConnectionId) expect(credentialExchangeRecord.protocolVersion).toEqual('v2') expect(credentialExchangeRecord.state).toEqual(CredentialState.ProposalSent) expect(credentialExchangeRecord.threadId).not.toBeNull() testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + let faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: credentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) @@ -114,18 +217,12 @@ describe('credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const offerMessage = await didCommMessageRepository.findAgentMessage(aliceAgent.context, { - associatedRecordId: aliceCredentialRecord.id, - messageClass: V2OfferCredentialMessage, - }) - + const offerMessage = await aliceAgent.credentials.findOfferMessage(aliceCredentialRecord.id) expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({ '@type': 'https://didcomm.org/issue-credential/2.0/offer-credential', '@id': expect.any(String), @@ -163,93 +260,87 @@ describe('credentials', () => { expect(aliceCredentialRecord.id).not.toBeNull() expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) - if (aliceCredentialRecord.connectionId) { - const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ - credentialRecordId: aliceCredentialRecord.id, - credentialFormats: { - jsonld: {}, + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + credentialFormats: { + jsonld: {}, + }, + }) + + expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnectionId) + expect(offerCredentialExchangeRecord.protocolVersion).toEqual('v2') + expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) + expect(offerCredentialExchangeRecord.threadId).not.toBeNull() + + testLogger.test('Faber waits for credential request from Alice') + await waitForCredentialRecordSubject(faberReplay, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Alice sends credential ack to Faber') + await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: expect.any(String), + connectionId: expect.any(String), + state: CredentialState.CredentialReceived, + }) + + const credentialMessage = await faberAgent.credentials.findCredentialMessage(faberCredentialRecord.id) + expect(JsonTransformer.toJSON(credentialMessage)).toMatchObject({ + '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', + '@id': expect.any(String), + comment: 'V2 Indy Credential', + formats: [ + { + attach_id: expect.any(String), + format: 'aries/ld-proof-vc@1.0', }, - }) - - expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnection.id) - expect(offerCredentialExchangeRecord.protocolVersion).toEqual('v2') - expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) - expect(offerCredentialExchangeRecord.threadId).not.toBeNull() - - testLogger.test('Faber waits for credential request from Alice') - await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.RequestReceived, - }) - - testLogger.test('Faber sends credential to Alice') - - await faberAgent.credentials.acceptRequest({ - credentialRecordId: faberCredentialRecord.id, - comment: 'V2 Indy Credential', - }) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Alice sends credential ack to Faber') - await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.Done, - }) - expect(aliceCredentialRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: expect.any(String), - connectionId: expect.any(String), - state: CredentialState.CredentialReceived, - }) - - const credentialMessage = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberCredentialRecord.id, - messageClass: V2IssueCredentialMessage, - }) - - expect(JsonTransformer.toJSON(credentialMessage)).toMatchObject({ - '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', - '@id': expect.any(String), - comment: 'V2 Indy Credential', - formats: [ - { - attach_id: expect.any(String), - format: 'aries/ld-proof-vc@1.0', - }, - ], - 'credentials~attach': [ - { - '@id': expect.any(String), - 'mime-type': 'application/json', - data: expect.any(Object), - lastmod_time: undefined, - byte_count: undefined, - }, - ], - '~thread': { - thid: expect.any(String), - pthid: undefined, - sender_order: undefined, - received_orders: undefined, + ], + 'credentials~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, }, - '~please_ack': { on: ['RECEIPT'] }, - '~service': undefined, - '~attach': undefined, - '~timing': undefined, - '~transport': undefined, - '~l10n': undefined, - }) - } + ], + '~thread': { + thid: expect.any(String), + pthid: undefined, + sender_order: undefined, + received_orders: undefined, + }, + '~please_ack': { on: ['RECEIPT'] }, + '~service': undefined, + '~attach': undefined, + '~timing': undefined, + '~transport': undefined, + '~l10n': undefined, + }) }) test('Multiple Formats: Alice starts with V2 (both ld and indy formats) credential proposal to Faber', async () => { @@ -261,35 +352,34 @@ describe('credentials', () => { 'x-ray': 'some x-ray', profile_picture: 'profile picture', }) - const testAttributes = { - attributes: credentialPreview.attributes, - schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - schemaName: 'ahoy', - schemaVersion: '1.0', - schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', - credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', - } testLogger.test('Alice sends (v2, Indy) credential proposal to Faber') - const credentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ - connectionId: aliceConnection.id, + const credentialExchangeRecord = await aliceAgent.credentials.proposeCredential({ + connectionId: aliceConnectionId, protocolVersion: 'v2', credentialFormats: { - indy: testAttributes, + indy: { + attributes: credentialPreview.attributes, + schemaIssuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + schemaName: 'ahoy', + schemaVersion: '1.0', + schemaId: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', + issuerDid: 'GMm4vMw8LLrLJjp81kRRLp', + credentialDefinitionId: 'GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag', + }, jsonld: signCredentialOptions, }, comment: 'v2 propose credential test', }) - expect(credentialExchangeRecord.connectionId).toEqual(aliceConnection.id) + expect(credentialExchangeRecord.connectionId).toEqual(aliceConnectionId) expect(credentialExchangeRecord.protocolVersion).toEqual('v2') expect(credentialExchangeRecord.state).toEqual(CredentialState.ProposalSent) expect(credentialExchangeRecord.threadId).not.toBeNull() testLogger.test('Faber waits for credential proposal from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { + let faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { threadId: credentialExchangeRecord.threadId, state: CredentialState.ProposalReceived, }) @@ -301,7 +391,7 @@ describe('credentials', () => { comment: 'V2 W3C & INDY Proposals', credentialFormats: { indy: { - credentialDefinitionId: credDefId, + credentialDefinitionId, attributes: credentialPreview.attributes, }, jsonld: {}, // this is to ensure both services are formatted @@ -309,20 +399,14 @@ describe('credentials', () => { }) testLogger.test('Alice waits for credential offer from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { + let aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { threadId: faberCredentialRecord.threadId, state: CredentialState.OfferReceived, }) - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const offerMessage = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberCredentialRecord.id, - messageClass: V2OfferCredentialMessage, - }) - - const credOfferJson = offerMessage?.offerAttachments[1].getDataAsJson() - expect(credOfferJson).toMatchObject({ + const offerMessage = await faberAgent.credentials.findOfferMessage(faberCredentialRecord.id) + const credentialOfferJson = offerMessage?.offerAttachments[1].getDataAsJson() + expect(credentialOfferJson).toMatchObject({ credential: { '@context': [ 'https://www.w3.org/2018/credentials/v1', @@ -408,139 +492,132 @@ describe('credentials', () => { expect(aliceCredentialRecord.id).not.toBeNull() expect(aliceCredentialRecord.type).toBe(CredentialExchangeRecord.type) - if (aliceCredentialRecord.connectionId) { - const offerCredentialExchangeRecord: CredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ - credentialRecordId: aliceCredentialRecord.id, - }) - - expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnection.id) - expect(offerCredentialExchangeRecord.protocolVersion).toEqual('v2') - expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) - expect(offerCredentialExchangeRecord.threadId).not.toBeNull() - - testLogger.test('Faber waits for credential request from Alice') - await waitForCredentialRecord(faberAgent, { - threadId: aliceCredentialRecord.threadId, - state: CredentialState.RequestReceived, - }) - - testLogger.test('Faber sends credential to Alice') - - await faberAgent.credentials.acceptRequest({ - credentialRecordId: faberCredentialRecord.id, - comment: 'V2 Indy Credential', - }) - - testLogger.test('Alice waits for credential from Faber') - aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.CredentialReceived, - }) - - testLogger.test('Alice sends credential ack to Faber') - await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) - - testLogger.test('Faber waits for credential ack from Alice') - faberCredentialRecord = await waitForCredentialRecord(faberAgent, { - threadId: faberCredentialRecord.threadId, - state: CredentialState.Done, - }) - expect(aliceCredentialRecord).toMatchObject({ - type: CredentialExchangeRecord.type, - id: expect.any(String), - createdAt: expect.any(Date), - threadId: expect.any(String), - connectionId: expect.any(String), - state: CredentialState.CredentialReceived, - }) - - const credentialMessage = await didCommMessageRepository.getAgentMessage(faberAgent.context, { - associatedRecordId: faberCredentialRecord.id, - messageClass: V2IssueCredentialMessage, - }) - - const w3cCredential = credentialMessage.credentialAttachments[1].getDataAsJson() - - expect(w3cCredential).toMatchObject({ - context: [ - 'https://www.w3.org/2018/credentials/v1', - 'https://w3id.org/citizenship/v1', - 'https://w3id.org/security/bbs/v1', - ], - id: 'https://issuer.oidp.uscis.gov/credentials/83627465', - type: ['VerifiableCredential', 'PermanentResidentCard'], - issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - issuanceDate: '2019-12-03T12:19:52Z', - expirationDate: '2029-12-03T12:19:52Z', - identifier: '83627465', - name: 'Permanent Resident Card', - credentialSubject: { - id: 'did:example:b34ca6cd37bbf23', - type: ['PermanentResident', 'Person'], - givenName: 'JOHN', - familyName: 'SMITH', - gender: 'Male', - image: 'data:image/png;base64,iVBORw0KGgokJggg==', - residentSince: '2015-01-01', - description: 'Government of Example Permanent Resident Card.', - lprCategory: 'C09', - lprNumber: '999-999-999', - commuterClassification: 'C1', - birthCountry: 'Bahamas', - birthDate: '1958-07-17', + const offerCredentialExchangeRecord = await aliceAgent.credentials.acceptOffer({ + credentialRecordId: aliceCredentialRecord.id, + }) + + expect(offerCredentialExchangeRecord.connectionId).toEqual(aliceConnectionId) + expect(offerCredentialExchangeRecord.protocolVersion).toEqual('v2') + expect(offerCredentialExchangeRecord.state).toEqual(CredentialState.RequestSent) + expect(offerCredentialExchangeRecord.threadId).not.toBeNull() + + testLogger.test('Faber waits for credential request from Alice') + await waitForCredentialRecordSubject(faberReplay, { + threadId: aliceCredentialRecord.threadId, + state: CredentialState.RequestReceived, + }) + + testLogger.test('Faber sends credential to Alice') + + await faberAgent.credentials.acceptRequest({ + credentialRecordId: faberCredentialRecord.id, + comment: 'V2 Indy Credential', + }) + + testLogger.test('Alice waits for credential from Faber') + aliceCredentialRecord = await waitForCredentialRecordSubject(aliceReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.CredentialReceived, + }) + + testLogger.test('Alice sends credential ack to Faber') + await aliceAgent.credentials.acceptCredential({ credentialRecordId: aliceCredentialRecord.id }) + + testLogger.test('Faber waits for credential ack from Alice') + faberCredentialRecord = await waitForCredentialRecordSubject(faberReplay, { + threadId: faberCredentialRecord.threadId, + state: CredentialState.Done, + }) + expect(aliceCredentialRecord).toMatchObject({ + type: CredentialExchangeRecord.type, + id: expect.any(String), + createdAt: expect.any(Date), + threadId: expect.any(String), + connectionId: expect.any(String), + state: CredentialState.CredentialReceived, + }) + + const credentialMessage = await faberAgent.credentials.findCredentialMessage(faberCredentialRecord.id) + const w3cCredential = credentialMessage?.credentialAttachments[1].getDataAsJson() + expect(w3cCredential).toMatchObject({ + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/citizenship/v1', + 'https://w3id.org/security/bbs/v1', + ], + id: 'https://issuer.oidp.uscis.gov/credentials/83627465', + type: ['VerifiableCredential', 'PermanentResidentCard'], + issuer: 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + issuanceDate: '2019-12-03T12:19:52Z', + expirationDate: '2029-12-03T12:19:52Z', + identifier: '83627465', + name: 'Permanent Resident Card', + credentialSubject: { + id: 'did:example:b34ca6cd37bbf23', + type: ['PermanentResident', 'Person'], + givenName: 'JOHN', + familyName: 'SMITH', + gender: 'Male', + image: 'data:image/png;base64,iVBORw0KGgokJggg==', + residentSince: '2015-01-01', + description: 'Government of Example Permanent Resident Card.', + lprCategory: 'C09', + lprNumber: '999-999-999', + commuterClassification: 'C1', + birthCountry: 'Bahamas', + birthDate: '1958-07-17', + }, + proof: { + type: 'Ed25519Signature2018', + created: expect.any(String), + verificationMethod: + 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', + proofPurpose: 'assertionMethod', + }, + }) + + expect(JsonTransformer.toJSON(credentialMessage)).toMatchObject({ + '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', + '@id': expect.any(String), + comment: 'V2 Indy Credential', + formats: [ + { + attach_id: expect.any(String), + format: 'hlindy/cred@v2.0', }, - proof: { - type: 'Ed25519Signature2018', - created: expect.any(String), - verificationMethod: - 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL', - proofPurpose: 'assertionMethod', + { + attach_id: expect.any(String), + format: 'aries/ld-proof-vc@1.0', }, - }) - - expect(JsonTransformer.toJSON(credentialMessage)).toMatchObject({ - '@type': 'https://didcomm.org/issue-credential/2.0/issue-credential', - '@id': expect.any(String), - comment: 'V2 Indy Credential', - formats: [ - { - attach_id: expect.any(String), - format: 'hlindy/cred@v2.0', - }, - { - attach_id: expect.any(String), - format: 'aries/ld-proof-vc@1.0', - }, - ], - 'credentials~attach': [ - { - '@id': expect.any(String), - 'mime-type': 'application/json', - data: expect.any(Object), - lastmod_time: undefined, - byte_count: undefined, - }, - { - '@id': expect.any(String), - 'mime-type': 'application/json', - data: expect.any(Object), - lastmod_time: undefined, - byte_count: undefined, - }, - ], - '~thread': { - thid: expect.any(String), - pthid: undefined, - sender_order: undefined, - received_orders: undefined, + ], + 'credentials~attach': [ + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, }, - '~please_ack': { on: ['RECEIPT'] }, - '~service': undefined, - '~attach': undefined, - '~timing': undefined, - '~transport': undefined, - '~l10n': undefined, - }) - } + { + '@id': expect.any(String), + 'mime-type': 'application/json', + data: expect.any(Object), + lastmod_time: undefined, + byte_count: undefined, + }, + ], + '~thread': { + thid: expect.any(String), + pthid: undefined, + sender_order: undefined, + received_orders: undefined, + }, + '~please_ack': { on: ['RECEIPT'] }, + '~service': undefined, + '~attach': undefined, + '~timing': undefined, + '~transport': undefined, + '~l10n': undefined, + }) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/errors/V2CredentialProblemReportError.ts b/packages/core/src/modules/credentials/protocol/v2/errors/V2CredentialProblemReportError.ts new file mode 100644 index 0000000000..0db9672621 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/errors/V2CredentialProblemReportError.ts @@ -0,0 +1,23 @@ +import type { ProblemReportErrorOptions } from '../../../../problem-reports' +import type { CredentialProblemReportReason } from '../../../models/CredentialProblemReportReason' + +import { ProblemReportError } from '../../../../problem-reports/errors/ProblemReportError' +import { V2CredentialProblemReportMessage } from '../messages/V2CredentialProblemReportMessage' + +export interface V2CredentialProblemReportErrorOptions extends ProblemReportErrorOptions { + problemCode: CredentialProblemReportReason +} + +export class V2CredentialProblemReportError extends ProblemReportError { + public problemReport: V2CredentialProblemReportMessage + + public constructor(message: string, { problemCode }: V2CredentialProblemReportErrorOptions) { + super(message, { problemCode }) + this.problemReport = new V2CredentialProblemReportMessage({ + description: { + en: message, + code: problemCode, + }, + }) + } +} diff --git a/packages/core/src/modules/credentials/protocol/v2/errors/index.ts b/packages/core/src/modules/credentials/protocol/v2/errors/index.ts new file mode 100644 index 0000000000..846017e442 --- /dev/null +++ b/packages/core/src/modules/credentials/protocol/v2/errors/index.ts @@ -0,0 +1 @@ +export { V2CredentialProblemReportError, V2CredentialProblemReportErrorOptions } from './V2CredentialProblemReportError' diff --git a/packages/core/src/modules/credentials/protocol/v2/index.ts b/packages/core/src/modules/credentials/protocol/v2/index.ts index c6d6213662..f50d673645 100644 --- a/packages/core/src/modules/credentials/protocol/v2/index.ts +++ b/packages/core/src/modules/credentials/protocol/v2/index.ts @@ -1,2 +1,3 @@ export * from './V2CredentialProtocol' export * from './messages' +export * from './errors' diff --git a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts index 290865b83a..c9af8da909 100644 --- a/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts +++ b/packages/core/src/modules/credentials/repository/CredentialExchangeRecord.ts @@ -1,4 +1,3 @@ -import type { CredentialMetadata } from './CredentialMetadataTypes' import type { TagsBase } from '../../../storage/BaseRecord' import type { AutoAcceptCredential } from '../models/CredentialAutoAcceptType' import type { CredentialState } from '../models/CredentialState' @@ -10,11 +9,8 @@ import { Attachment } from '../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../error' import { BaseRecord } from '../../../storage/BaseRecord' import { uuid } from '../../../utils/uuid' -import { IndyCredentialView } from '../formats/indy/models/IndyCredentialView' import { CredentialPreviewAttribute } from '../models/CredentialPreviewAttribute' -import { CredentialMetadataKeys } from './CredentialMetadataTypes' - export interface CredentialExchangeRecordProps { id?: string createdAt?: Date @@ -38,8 +34,6 @@ export type DefaultCredentialTags = { connectionId?: string state: CredentialState credentialIds: string[] - indyRevocationRegistryId?: string - indyCredentialRevocationId?: string } export interface CredentialRecordBinding { @@ -47,11 +41,7 @@ export interface CredentialRecordBinding { credentialRecordId: string } -export class CredentialExchangeRecord extends BaseRecord< - DefaultCredentialTags, - CustomCredentialTags, - CredentialMetadata -> { +export class CredentialExchangeRecord extends BaseRecord { public connectionId?: string public threadId!: string public state!: CredentialState @@ -92,7 +82,6 @@ export class CredentialExchangeRecord extends BaseRecord< } public getTags() { - const metadata = this.metadata.get(CredentialMetadataKeys.IndyCredential) const ids = this.credentials.map((c) => c.credentialRecordId) return { @@ -101,29 +90,9 @@ export class CredentialExchangeRecord extends BaseRecord< connectionId: this.connectionId, state: this.state, credentialIds: ids, - indyRevocationRegistryId: metadata?.indyRevocationRegistryId, - indyCredentialRevocationId: metadata?.indyCredentialRevocationId, } } - public getCredentialInfo(): IndyCredentialView | null { - if (!this.credentialAttributes) return null - - const claims = this.credentialAttributes.reduce( - (accumulator, current) => ({ - ...accumulator, - [current.name]: current.value, - }), - {} - ) - - return new IndyCredentialView({ - claims, - attachments: this.linkedAttachments, - metadata: this.metadata.data, - }) - } - public assertProtocolVersion(version: string) { if (this.protocolVersion != version) { throw new AriesFrameworkError( diff --git a/packages/core/src/modules/credentials/repository/CredentialMetadataTypes.ts b/packages/core/src/modules/credentials/repository/CredentialMetadataTypes.ts deleted file mode 100644 index 7c645333cb..0000000000 --- a/packages/core/src/modules/credentials/repository/CredentialMetadataTypes.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { CredReqMetadata } from 'indy-sdk' - -export enum CredentialMetadataKeys { - IndyCredential = '_internal/indyCredential', - IndyRequest = '_internal/indyRequest', -} - -export type CredentialMetadata = { - [CredentialMetadataKeys.IndyCredential]: { - schemaId?: string - credentialDefinitionId?: string - indyRevocationRegistryId?: string - indyCredentialRevocationId?: string - } - [CredentialMetadataKeys.IndyRequest]: CredReqMetadata -} diff --git a/packages/core/src/modules/credentials/repository/__tests__/CredentialExchangeRecord.test.ts b/packages/core/src/modules/credentials/repository/__tests__/CredentialExchangeRecord.test.ts deleted file mode 100644 index 688f21bad1..0000000000 --- a/packages/core/src/modules/credentials/repository/__tests__/CredentialExchangeRecord.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { CredentialPreviewAttribute } from '../../models/CredentialPreviewAttribute' -import { CredentialState } from '../../models/CredentialState' -import { CredentialExchangeRecord } from '../CredentialExchangeRecord' -import { CredentialMetadataKeys } from '../CredentialMetadataTypes' - -describe('CredentialExchangeRecord', () => { - describe('getCredentialInfo()', () => { - test('creates credential info object from credential record data', () => { - const credentialRecord = new CredentialExchangeRecord({ - connectionId: '28790bfe-1345-4c64-b21a-7d98982b3894', - threadId: 'threadId', - state: CredentialState.Done, - credentialAttributes: [ - new CredentialPreviewAttribute({ - name: 'age', - value: '25', - }), - ], - protocolVersion: 'v1', - }) - - credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, { - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', - }) - - const credentialInfo = credentialRecord.getCredentialInfo() - - expect(credentialInfo).toEqual({ - claims: { - age: '25', - }, - metadata: { - '_internal/indyCredential': { - credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', - schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', - }, - }, - }) - }) - }) -}) diff --git a/packages/core/src/modules/credentials/repository/index.ts b/packages/core/src/modules/credentials/repository/index.ts index b7b986ad3e..980f320cfd 100644 --- a/packages/core/src/modules/credentials/repository/index.ts +++ b/packages/core/src/modules/credentials/repository/index.ts @@ -1,3 +1,2 @@ export * from './CredentialExchangeRecord' export * from './CredentialRepository' -export * from './CredentialMetadataTypes' diff --git a/packages/core/src/modules/dids/DidsModuleConfig.ts b/packages/core/src/modules/dids/DidsModuleConfig.ts index 24acbf38bf..057772d8d8 100644 --- a/packages/core/src/modules/dids/DidsModuleConfig.ts +++ b/packages/core/src/modules/dids/DidsModuleConfig.ts @@ -1,14 +1,6 @@ import type { DidRegistrar, DidResolver } from './domain' -import { - KeyDidRegistrar, - IndySdkSovDidRegistrar, - PeerDidRegistrar, - KeyDidResolver, - PeerDidResolver, - IndySdkSovDidResolver, - WebDidResolver, -} from './methods' +import { KeyDidRegistrar, PeerDidRegistrar, KeyDidResolver, PeerDidResolver, WebDidResolver } from './methods' /** * DidsModuleConfigOptions defines the interface for the options of the DidsModuleConfig class. @@ -23,7 +15,7 @@ export interface DidsModuleConfigOptions { * registered, as it is needed for the connections and out of band module to function. Other did methods can be * disabled. * - * @default [KeyDidRegistrar, IndySdkSovDidRegistrar, PeerDidRegistrar] + * @default [KeyDidRegistrar, PeerDidRegistrar] */ registrars?: DidRegistrar[] @@ -35,7 +27,7 @@ export interface DidsModuleConfigOptions { * registered, as it is needed for the connections and out of band module to function. Other did methods can be * disabled. * - * @default [IndySdkSovDidResolver, WebDidResolver, KeyDidResolver, PeerDidResolver] + * @default [WebDidResolver, KeyDidResolver, PeerDidResolver] */ resolvers?: DidResolver[] } @@ -54,11 +46,7 @@ export class DidsModuleConfig { // This prevents creating new instances every time this property is accessed if (this._registrars) return this._registrars - let registrars = this.options.registrars ?? [ - new KeyDidRegistrar(), - new IndySdkSovDidRegistrar(), - new PeerDidRegistrar(), - ] + let registrars = this.options.registrars ?? [new KeyDidRegistrar(), new PeerDidRegistrar()] // Add peer did registrar if it is not included yet if (!registrars.find((registrar) => registrar instanceof PeerDidRegistrar)) { @@ -79,12 +67,7 @@ export class DidsModuleConfig { // This prevents creating new instances every time this property is accessed if (this._resolvers) return this._resolvers - let resolvers = this.options.resolvers ?? [ - new IndySdkSovDidResolver(), - new WebDidResolver(), - new KeyDidResolver(), - new PeerDidResolver(), - ] + let resolvers = this.options.resolvers ?? [new WebDidResolver(), new KeyDidResolver(), new PeerDidResolver()] // Add peer did resolver if it is not included yet if (!resolvers.find((resolver) => resolver instanceof PeerDidResolver)) { diff --git a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts index 53e5ed3203..797a7f8615 100644 --- a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts @@ -1,27 +1,14 @@ import type { DidRegistrar, DidResolver } from '../domain' -import { - KeyDidRegistrar, - IndySdkSovDidRegistrar, - PeerDidRegistrar, - KeyDidResolver, - PeerDidResolver, - IndySdkSovDidResolver, - WebDidResolver, -} from '..' +import { KeyDidRegistrar, PeerDidRegistrar, KeyDidResolver, PeerDidResolver, WebDidResolver } from '..' import { DidsModuleConfig } from '../DidsModuleConfig' describe('DidsModuleConfig', () => { test('sets default values', () => { const config = new DidsModuleConfig() - expect(config.registrars).toEqual([ - expect.any(KeyDidRegistrar), - expect.any(IndySdkSovDidRegistrar), - expect.any(PeerDidRegistrar), - ]) + expect(config.registrars).toEqual([expect.any(KeyDidRegistrar), expect.any(PeerDidRegistrar)]) expect(config.resolvers).toEqual([ - expect.any(IndySdkSovDidResolver), expect.any(WebDidResolver), expect.any(KeyDidResolver), expect.any(PeerDidResolver), diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index ccd60edf71..70aa731310 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -1,31 +1,24 @@ import type { KeyDidCreateOptions } from '../methods/key/KeyDidRegistrar' import type { PeerDidNumAlgo0CreateOptions } from '../methods/peer/PeerDidRegistrar' -import type { SovDidCreateOptions } from '../methods/sov/IndySdkSovDidRegistrar' -import type { Wallet } from '@aries-framework/core' -import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' - -import { genesisPath, getAgentOptions } from '../../../../tests/helpers' +import { IndySdkModule } from '../../../../../indy-sdk/src' +import { indySdk } from '../../../../tests' +import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { KeyType } from '../../../crypto' -import { TypedArrayEncoder } from '../../../utils' -import { indyDidFromPublicKeyBase58 } from '../../../utils/did' - -import { InjectionSymbols, JsonTransformer } from '@aries-framework/core' - import { PeerDidNumAlgo } from '../methods/peer/didPeer' -const agentOptions = getAgentOptions('Faber Dids Registrar', { - indyLedgers: [ - { - id: `localhost`, - isProduction: false, - genesisPath, - indyNamespace: 'localhost', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - ], -}) +import { JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' + +const agentOptions = getAgentOptions( + 'Faber Dids Registrar', + {}, + { + indySdk: new IndySdkModule({ + indySdk, + }), + } +) describe('dids', () => { let agent: Agent @@ -164,108 +157,4 @@ describe('dids', () => { }, }) }) - - it('should create a did:sov did', async () => { - // Generate a seed and the indy did. This allows us to create a new did every time - // but still check if the created output document is as expected. - const privateKey = TypedArrayEncoder.fromString( - Array(32 + 1) - .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) - .slice(0, 32) - ) - - const publicKeyEd25519 = generateKeyPairFromSeed(privateKey).publicKey - const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(publicKeyEd25519)) - const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) - const indyDid = indyDidFromPublicKeyBase58(ed25519PublicKeyBase58) - - const wallet = agent.dependencyManager.resolve(InjectionSymbols.Wallet) - // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain, @typescript-eslint/no-non-null-assertion - const submitterDid = `did:sov:${wallet.publicDid?.did!}` - - const did = await agent.dids.create({ - method: 'sov', - options: { - submitterDid, - alias: 'Alias', - endpoints: { - endpoint: 'https://example.com/endpoint', - types: ['DIDComm', 'did-communication', 'endpoint'], - routingKeys: ['a-routing-key'], - }, - }, - secret: { - privateKey, - }, - }) - - expect(JsonTransformer.toJSON(did)).toMatchObject({ - didDocumentMetadata: { - qualifiedIndyDid: `did:indy:localhost:${indyDid}`, - }, - didRegistrationMetadata: { - didIndyNamespace: 'localhost', - }, - didState: { - state: 'finished', - did: `did:sov:${indyDid}`, - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - 'https://didcomm.org/messaging/contexts/v2', - ], - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - id: `did:sov:${indyDid}#key-1`, - type: 'Ed25519VerificationKey2018', - controller: `did:sov:${indyDid}`, - publicKeyBase58: ed25519PublicKeyBase58, - }, - { - id: `did:sov:${indyDid}#key-agreement-1`, - type: 'X25519KeyAgreementKey2019', - controller: `did:sov:${indyDid}`, - publicKeyBase58: x25519PublicKeyBase58, - }, - ], - service: [ - { - id: `did:sov:${indyDid}#endpoint`, - serviceEndpoint: 'https://example.com/endpoint', - type: 'endpoint', - }, - { - accept: ['didcomm/aip2;env=rfc19'], - id: `did:sov:${indyDid}#did-communication`, - priority: 0, - recipientKeys: [`did:sov:${indyDid}#key-agreement-1`], - routingKeys: ['a-routing-key'], - serviceEndpoint: 'https://example.com/endpoint', - type: 'did-communication', - }, - { - accept: ['didcomm/v2'], - id: `did:sov:${indyDid}#didcomm-1`, - routingKeys: ['a-routing-key'], - serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', - }, - ], - authentication: [`did:sov:${indyDid}#key-1`], - assertionMethod: [`did:sov:${indyDid}#key-1`], - keyAgreement: [`did:sov:${indyDid}#key-agreement-1`], - capabilityInvocation: undefined, - capabilityDelegation: undefined, - id: `did:sov:${indyDid}`, - }, - secret: { - privateKey: privateKey.toString(), - }, - }, - }) - }) }) diff --git a/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts index 09d64b2b70..3e46ada4f0 100644 --- a/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-resolver.e2e.test.ts @@ -1,16 +1,23 @@ -import type { SovDidCreateOptions } from '../methods' - +import { IndySdkModule } from '../../../../../indy-sdk/src' +import { indySdk } from '../../../../tests' import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' -import { AriesFrameworkError } from '../../../error' import { JsonTransformer } from '../../../utils' -import { sleep } from '../../../utils/sleep' -describe('dids', () => { - let agent: Agent +const agent = new Agent( + getAgentOptions( + 'Faber Dids', + {}, + { + indySdk: new IndySdkModule({ + indySdk, + }), + } + ) +) +describe('dids', () => { beforeAll(async () => { - agent = new Agent(getAgentOptions('Faber Dids')) await agent.initialize() }) @@ -19,64 +26,6 @@ describe('dids', () => { await agent.wallet.delete() }) - it('should resolve a did:sov did', async () => { - const publicDid = agent.publicDid?.did - - if (!publicDid) throw new Error('Agent has no public did') - - const createResult = await agent.dids.create({ - method: 'sov', - options: { - submitterDid: `did:sov:${publicDid}`, - alias: 'Alias', - role: 'TRUSTEE', - }, - }) - - // Terrible, but the did can't be immediately resolved, so we need to wait a bit - await sleep(1000) - - if (!createResult.didState.did) throw new AriesFrameworkError('Unable to register did') - const didResult = await agent.dids.resolve(createResult.didState.did) - - expect(JsonTransformer.toJSON(didResult)).toMatchObject({ - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - ], - id: createResult.didState.did, - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - type: 'Ed25519VerificationKey2018', - controller: createResult.didState.did, - id: `${createResult.didState.did}#key-1`, - publicKeyBase58: expect.any(String), - }, - { - controller: createResult.didState.did, - type: 'X25519KeyAgreementKey2019', - id: `${createResult.didState.did}#key-agreement-1`, - publicKeyBase58: expect.any(String), - }, - ], - capabilityDelegation: undefined, - capabilityInvocation: undefined, - authentication: [`${createResult.didState.did}#key-1`], - assertionMethod: [`${createResult.didState.did}#key-1`], - keyAgreement: [`${createResult.didState.did}#key-agreement-1`], - service: undefined, - }, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - it('should resolve a did:key did', async () => { const did = await agent.dids.resolve('did:key:z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index c352fc0383..7ec76f20cb 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -1,15 +1,17 @@ import type { AgentContext } from '../../../agent' +import type { Wallet } from '../../../wallet' import { Subject } from 'rxjs' +import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentConfig, getAgentContext } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Key, KeyType } from '../../../crypto' import { SigningProviderRegistry } from '../../../crypto/signing-provider' -import { IndyStorageService } from '../../../storage/IndyStorageService' import { JsonTransformer, TypedArrayEncoder } from '../../../utils' -import { IndyWallet } from '../../../wallet/IndyWallet' import { DidsModuleConfig } from '../DidsModuleConfig' import { DidCommV1Service, DidDocument, DidDocumentBuilder } from '../domain' import { DidDocumentRole } from '../domain/DidDocumentRole' @@ -29,13 +31,13 @@ describe('peer dids', () => { let didRepository: DidRepository let didResolverService: DidResolverService - let wallet: IndyWallet + let wallet: Wallet let agentContext: AgentContext let eventEmitter: EventEmitter beforeEach(async () => { - wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) - const storageService = new IndyStorageService(config.agentDependencies) + wallet = new IndySdkWallet(indySdk, config.logger, new SigningProviderRegistry([])) + const storageService = new InMemoryStorageService() eventEmitter = new EventEmitter(config.agentDependencies, new Subject()) didRepository = new DidRepository(storageService, eventEmitter) @@ -46,8 +48,7 @@ describe('peer dids', () => { [InjectionSymbols.StorageService, storageService], ], }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) + await wallet.createAndOpen(config.walletConfig) didResolverService = new DidResolverService( config.logger, diff --git a/packages/core/src/modules/dids/methods/index.ts b/packages/core/src/modules/dids/methods/index.ts index ebacc7f2c2..12f78247af 100644 --- a/packages/core/src/modules/dids/methods/index.ts +++ b/packages/core/src/modules/dids/methods/index.ts @@ -1,4 +1,3 @@ export * from './key' export * from './peer' -export * from './sov' export * from './web' diff --git a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts deleted file mode 100644 index 21781642ab..0000000000 --- a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts +++ /dev/null @@ -1,247 +0,0 @@ -import type { AgentContext } from '../../../../agent' -import type { Buffer } from '../../../../utils' -import type { IndyEndpointAttrib, IndyPool } from '../../../ledger' -import type { DidRegistrar } from '../../domain/DidRegistrar' -import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' -import type * as Indy from 'indy-sdk' - -import { IndySdkError } from '../../../../error' -import { injectable } from '../../../../plugins' -import { isIndyError } from '../../../../utils/indyError' -import { assertIndyWallet } from '../../../../wallet/util/assertIndyWallet' -import { IndyPoolService } from '../../../ledger' -import { DidDocumentRole } from '../../domain/DidDocumentRole' -import { DidRecord, DidRepository } from '../../repository' - -import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './util' - -@injectable() -export class IndySdkSovDidRegistrar implements DidRegistrar { - public readonly supportedMethods = ['sov'] - - public async create(agentContext: AgentContext, options: SovDidCreateOptions): Promise { - const indy = agentContext.config.agentDependencies.indy - const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) - const didRepository = agentContext.dependencyManager.resolve(DidRepository) - - const { alias, role, submitterDid, indyNamespace } = options.options - const privateKey = options.secret?.privateKey - - if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid private key provided', - }, - } - } - - if (!submitterDid.startsWith('did:sov:')) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Submitter did must be a valid did:sov did', - }, - } - } - - try { - // NOTE: we need to use the createAndStoreMyDid method from indy to create the did - // If we just create a key and handle the creating of the did ourselves, indy will throw a - // WalletItemNotFound when it needs to sign ledger transactions using this did. This means we need - // to rely directly on the indy SDK, as we don't want to expose a createDid method just for. - // FIXME: once askar/indy-vdr is supported we need to adjust this to work with both indy-sdk and askar - assertIndyWallet(agentContext.wallet) - const [unqualifiedIndyDid, verkey] = await indy.createAndStoreMyDid(agentContext.wallet.handle, { - seed: privateKey?.toString(), - }) - - const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` - const unqualifiedSubmitterDid = submitterDid.replace('did:sov:', '') - - // TODO: it should be possible to pass the pool used for writing to the indy ledger service. - // The easiest way to do this would be to make the submitterDid a fully qualified did, including the indy namespace. - const pool = indyPoolService.getPoolForNamespace(indyNamespace) - await this.registerPublicDid(agentContext, unqualifiedSubmitterDid, unqualifiedIndyDid, verkey, alias, pool, role) - - // Create did document - const didDocumentBuilder = sovDidDocumentFromDid(qualifiedSovDid, verkey) - - // Add services if endpoints object was passed. - if (options.options.endpoints) { - await this.setEndpointsForDid(agentContext, unqualifiedIndyDid, options.options.endpoints, pool) - addServicesFromEndpointsAttrib( - didDocumentBuilder, - qualifiedSovDid, - options.options.endpoints, - `${qualifiedSovDid}#key-agreement-1` - ) - } - - // Build did document. - const didDocument = didDocumentBuilder.build() - - const didIndyNamespace = pool.config.indyNamespace - const qualifiedIndyDid = `did:indy:${didIndyNamespace}:${unqualifiedIndyDid}` - - // Save the did so we know we created it and can issue with it - const didRecord = new DidRecord({ - did: qualifiedSovDid, - role: DidDocumentRole.Created, - tags: { - recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), - qualifiedIndyDid, - }, - }) - await didRepository.save(agentContext, didRecord) - - return { - didDocumentMetadata: { - qualifiedIndyDid, - }, - didRegistrationMetadata: { - didIndyNamespace, - }, - didState: { - state: 'finished', - did: qualifiedSovDid, - didDocument, - secret: { - // FIXME: the uni-registrar creates the seed in the registrar method - // if it doesn't exist so the seed can always be returned. Currently - // we can only return it if the seed was passed in by the user. Once - // we have a secure method for generating seeds we should use the same - // approach - privateKey: options.secret?.privateKey?.toString(), - }, - }, - } - } catch (error) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `unknownError: ${error.message}`, - }, - } - } - } - - public async update(): Promise { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: updating did:sov not implemented yet`, - }, - } - } - - public async deactivate(): Promise { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: deactivating did:sov not implemented yet`, - }, - } - } - - public async registerPublicDid( - agentContext: AgentContext, - submitterDid: string, - targetDid: string, - verkey: string, - alias: string, - pool: IndyPool, - role?: Indy.NymRole - ) { - const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) - const indy = agentContext.config.agentDependencies.indy - - try { - agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool.id}'`) - - const request = await indy.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) - - const response = await indyPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) - - agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.id}'`, { - response, - }) - - return targetDid - } catch (error) { - agentContext.config.logger.error(`Error registering public did '${targetDid}' on ledger '${pool.id}'`, { - error, - submitterDid, - targetDid, - verkey, - alias, - role, - pool: pool.id, - }) - - throw error - } - } - - public async setEndpointsForDid( - agentContext: AgentContext, - did: string, - endpoints: IndyEndpointAttrib, - pool: IndyPool - ): Promise { - const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) - const indy = agentContext.config.agentDependencies.indy - - try { - agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.id}'`, endpoints) - - const request = await indy.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) - - const response = await indyPoolService.submitWriteRequest(agentContext, pool, request, did) - agentContext.config.logger.debug(`Successfully set endpoints for did '${did}' on ledger '${pool.id}'`, { - response, - endpoints, - }) - } catch (error) { - agentContext.config.logger.error(`Error setting endpoints for did '${did}' on ledger '${pool.id}'`, { - error, - did, - endpoints, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} - -export interface SovDidCreateOptions extends DidCreateOptions { - method: 'sov' - did?: undefined - // As did:sov is so limited, we require everything needed to construct the did document to be passed - // through the options object. Once we support did:indy we can allow the didDocument property. - didDocument?: never - options: { - alias: string - role?: Indy.NymRole - endpoints?: IndyEndpointAttrib - indyNamespace?: string - submitterDid: string - } - secret?: { - privateKey?: Buffer - } -} - -// Update and Deactivate not supported for did:sov -export type IndyDidUpdateOptions = never -export type IndyDidDeactivateOptions = never diff --git a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts deleted file mode 100644 index bae98c5587..0000000000 --- a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidResolver.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { AgentContext } from '../../../../agent' -import type { IndyEndpointAttrib } from '../../../ledger' -import type { DidResolver } from '../../domain/DidResolver' -import type { DidResolutionResult, ParsedDid } from '../../types' - -import { IndySdkError } from '../../../../error' -import { injectable } from '../../../../plugins' -import { isIndyError } from '../../../../utils/indyError' -import { IndyPoolService } from '../../../ledger' - -import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './util' - -@injectable() -export class IndySdkSovDidResolver implements DidResolver { - public readonly supportedMethods = ['sov'] - - public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { - const didDocumentMetadata = {} - - try { - const nym = await this.getPublicDid(agentContext, parsed.id) - const endpoints = await this.getEndpointsForDid(agentContext, parsed.id) - - const keyAgreementId = `${parsed.did}#key-agreement-1` - const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) - addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) - - return { - didDocument: builder.build(), - didDocumentMetadata, - didResolutionMetadata: { contentType: 'application/did+ld+json' }, - } - } catch (error) { - return { - didDocument: null, - didDocumentMetadata, - didResolutionMetadata: { - error: 'notFound', - message: `resolver_error: Unable to resolve did '${did}': ${error}`, - }, - } - } - } - - private async getPublicDid(agentContext: AgentContext, did: string) { - const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) - - // Getting the pool for a did also retrieves the DID. We can just use that - const { did: didResponse } = await indyPoolService.getPoolForDid(agentContext, did) - - return didResponse - } - - private async getEndpointsForDid(agentContext: AgentContext, did: string) { - const indyPoolService = agentContext.dependencyManager.resolve(IndyPoolService) - const indy = agentContext.config.agentDependencies.indy - - const { pool } = await indyPoolService.getPoolForDid(agentContext, did) - - try { - agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.id}'`) - - const request = await indy.buildGetAttribRequest(null, did, 'endpoint', null, null) - - agentContext.config.logger.debug(`Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.id}'`) - const response = await indyPoolService.submitReadRequest(pool, request) - - if (!response.result.data) return {} - - const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib - agentContext.config.logger.debug( - `Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.id}'`, - { - response, - endpoints, - } - ) - - return endpoints ?? {} - } catch (error) { - agentContext.config.logger.error(`Error retrieving endpoints for did '${did}' from ledger '${pool.id}'`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts deleted file mode 100644 index 7837772932..0000000000 --- a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts +++ /dev/null @@ -1,374 +0,0 @@ -import type { AgentConfig } from '../../../../../agent/AgentConfig' -import type { Wallet } from '../../../../../wallet' -import type { IndyPool } from '../../../../ledger' -import type * as Indy from 'indy-sdk' - -import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../../../tests/helpers' -import { SigningProviderRegistry } from '../../../../../crypto/signing-provider' -import { TypedArrayEncoder } from '../../../../../utils' -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { IndyWallet } from '../../../../../wallet/IndyWallet' -import { IndyPoolService } from '../../../../ledger/services/IndyPoolService' -import { DidDocumentRole } from '../../../domain/DidDocumentRole' -import { DidRepository } from '../../../repository/DidRepository' -import { IndySdkSovDidRegistrar } from '../IndySdkSovDidRegistrar' - -jest.mock('../../../repository/DidRepository') -const DidRepositoryMock = DidRepository as jest.Mock - -jest.mock('../../../../ledger/services/IndyPoolService') -const IndyPoolServiceMock = IndyPoolService as jest.Mock -const indyPoolServiceMock = new IndyPoolServiceMock() -mockFunction(indyPoolServiceMock.getPoolForNamespace).mockReturnValue({ - config: { id: 'pool1', indyNamespace: 'pool1' }, -} as IndyPool) - -const agentConfig = getAgentConfig('IndySdkSovDidRegistrar') -const createDidMock = jest.fn(async () => ['R1xKJw17sUoXhejEpugMYJ', 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu']) - -const wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) -mockProperty(wallet, 'handle', 10) - -const didRepositoryMock = new DidRepositoryMock() -const agentContext = getAgentContext({ - wallet, - registerInstances: [ - [DidRepository, didRepositoryMock], - [IndyPoolService, indyPoolServiceMock], - ], - agentConfig: { - ...agentConfig, - agentDependencies: { - ...agentConfig.agentDependencies, - indy: { createAndStoreMyDid: createDidMock } as unknown as typeof Indy, - }, - } as AgentConfig, -}) - -const indySdkSovDidRegistrar = new IndySdkSovDidRegistrar() - -describe('DidRegistrar', () => { - describe('IndySdkSovDidRegistrar', () => { - afterEach(() => { - jest.clearAllMocks() - }) - - it('should return an error state if an invalid private key is provided', async () => { - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - - options: { - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - alias: 'Hello', - }, - secret: { - privateKey: TypedArrayEncoder.fromString('invalid'), - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid private key provided', - }, - }) - }) - - it('should return an error state if the wallet is not an indy wallet', async () => { - const agentContext = getAgentContext({ - wallet: {} as unknown as Wallet, - agentConfig, - }) - - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - - options: { - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - alias: 'Hello', - }, - secret: { - privateKey: TypedArrayEncoder.fromString('12345678901234567890123456789012'), - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'unknownError: Expected wallet to be instance of IndyWallet, found Object', - }, - }) - }) - - it('should return an error state if the submitter did is not qualified with did:sov', async () => { - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', - alias: 'Hello', - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Submitter did must be a valid did:sov did', - }, - }) - }) - - it('should correctly create a did:sov document without services', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) - - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - alias: 'Hello', - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - role: 'STEWARD', - }, - secret: { - privateKey, - }, - }) - - expect(registerPublicDidSpy).toHaveBeenCalledWith( - agentContext, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // Unqualified created indy did - 'R1xKJw17sUoXhejEpugMYJ', - // Verkey - 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - // Alias - 'Hello', - // Pool - { config: { id: 'pool1', indyNamespace: 'pool1' } }, - // Role - 'STEWARD' - ) - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: { - qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - }, - didRegistrationMetadata: { - didIndyNamespace: 'pool1', - }, - didState: { - state: 'finished', - did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - ], - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - verificationMethod: [ - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', - type: 'Ed25519VerificationKey2018', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', - type: 'X25519KeyAgreementKey2019', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', - }, - ], - authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - }, - secret: { - privateKey: privateKey.toString(), - }, - }, - }) - }) - - it('should correctly create a did:sov document with services', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) - - const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') - setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) - - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - alias: 'Hello', - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - role: 'STEWARD', - endpoints: { - endpoint: 'https://example.com/endpoint', - routingKeys: ['key-1'], - types: ['DIDComm', 'did-communication', 'endpoint'], - }, - }, - secret: { - privateKey, - }, - }) - - expect(registerPublicDidSpy).toHaveBeenCalledWith( - agentContext, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // Unqualified created indy did - 'R1xKJw17sUoXhejEpugMYJ', - // Verkey - 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - // Alias - 'Hello', - // Pool - { config: { id: 'pool1', indyNamespace: 'pool1' } }, - // Role - 'STEWARD' - ) - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: { - qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - }, - didRegistrationMetadata: { - didIndyNamespace: 'pool1', - }, - didState: { - state: 'finished', - did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - 'https://didcomm.org/messaging/contexts/v2', - ], - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - verificationMethod: [ - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', - type: 'Ed25519VerificationKey2018', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', - type: 'X25519KeyAgreementKey2019', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', - }, - ], - service: [ - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#endpoint', - serviceEndpoint: 'https://example.com/endpoint', - type: 'endpoint', - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#did-communication', - serviceEndpoint: 'https://example.com/endpoint', - type: 'did-communication', - priority: 0, - recipientKeys: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - routingKeys: ['key-1'], - accept: ['didcomm/aip2;env=rfc19'], - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#didcomm-1', - serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', - routingKeys: ['key-1'], - accept: ['didcomm/v2'], - }, - ], - authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - }, - secret: { - privateKey: privateKey.toString(), - }, - }, - }) - }) - - it('should store the did document', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('did')) - - const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') - setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) - - await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - alias: 'Hello', - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - role: 'STEWARD', - endpoints: { - endpoint: 'https://example.com/endpoint', - routingKeys: ['key-1'], - types: ['DIDComm', 'did-communication', 'endpoint'], - }, - }, - secret: { - privateKey, - }, - }) - - expect(didRepositoryMock.save).toHaveBeenCalledTimes(1) - const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] - - expect(didRecord).toMatchObject({ - did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - role: DidDocumentRole.Created, - _tags: { - recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], - qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - }, - didDocument: undefined, - }) - }) - - it('should return an error state when calling update', async () => { - const result = await indySdkSovDidRegistrar.update() - - expect(result).toEqual({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: updating did:sov not implemented yet`, - }, - }) - }) - - it('should return an error state when calling deactivate', async () => { - const result = await indySdkSovDidRegistrar.deactivate() - - expect(result).toEqual({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: deactivating did:sov not implemented yet`, - }, - }) - }) - }) -}) diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts deleted file mode 100644 index 6d082792f0..0000000000 --- a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidResolver.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -import type { IndyPool } from '../../../../ledger' -import type { IndyEndpointAttrib } from '../../../../ledger/services/IndyLedgerService' -import type { GetNymResponse } from 'indy-sdk' - -import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../../../tests/helpers' -import { SigningProviderRegistry } from '../../../../../crypto/signing-provider' -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { IndyWallet } from '../../../../../wallet/IndyWallet' -import { IndyPoolService } from '../../../../ledger/services/IndyPoolService' -import didSovR1xKJw17sUoXhejEpugMYJFixture from '../../../__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' -import didSovWJz9mHyW9BZksioQnRsrAoFixture from '../../../__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' -import { parseDid } from '../../../domain/parse' -import { IndySdkSovDidResolver } from '../IndySdkSovDidResolver' - -jest.mock('../../../../ledger/services/IndyPoolService') -const IndyPoolServiceMock = IndyPoolService as jest.Mock -const indyPoolServiceMock = new IndyPoolServiceMock() -mockFunction(indyPoolServiceMock.getPoolForNamespace).mockReturnValue({ - config: { id: 'pool1', indyNamespace: 'pool1' }, -} as IndyPool) - -const agentConfig = getAgentConfig('IndySdkSovDidResolver') - -const wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) -mockProperty(wallet, 'handle', 10) - -const agentContext = getAgentContext({ - agentConfig, - registerInstances: [[IndyPoolService, indyPoolServiceMock]], -}) - -const indySdkSovDidResolver = new IndySdkSovDidResolver() - -describe('DidResolver', () => { - describe('IndySdkSovDidResolver', () => { - it('should correctly resolve a did:sov document', async () => { - const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' - - const nymResponse: GetNymResponse = { - did: 'R1xKJw17sUoXhejEpugMYJ', - verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - role: 'ENDORSER', - } - - const endpoints: IndyEndpointAttrib = { - endpoint: 'https://ssi.com', - profile: 'https://profile.com', - hub: 'https://hub.com', - } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) - - const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocument: didSovR1xKJw17sUoXhejEpugMYJFixture, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - - it('should resolve a did:sov document with routingKeys and types entries in the attrib', async () => { - const did = 'did:sov:WJz9mHyW9BZksioQnRsrAo' - - const nymResponse: GetNymResponse = { - did: 'WJz9mHyW9BZksioQnRsrAo', - verkey: 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8', - role: 'ENDORSER', - } - - const endpoints: IndyEndpointAttrib = { - endpoint: 'https://agent.com', - types: ['endpoint', 'did-communication', 'DIDComm'], - routingKeys: ['routingKey1', 'routingKey2'], - } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) - - const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocument: didSovWJz9mHyW9BZksioQnRsrAoFixture, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - - it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { - const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockRejectedValue(new Error('Error retrieving did')) - - const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) - - expect(result).toMatchObject({ - didDocument: null, - didDocumentMetadata: {}, - didResolutionMetadata: { - error: 'notFound', - message: `resolver_error: Unable to resolve did 'did:sov:R1xKJw17sUoXhejEpugMYJ': Error: Error retrieving did`, - }, - }) - }) - }) -}) diff --git a/packages/core/src/modules/dids/methods/sov/index.ts b/packages/core/src/modules/dids/methods/sov/index.ts deleted file mode 100644 index 13a8e8aa2f..0000000000 --- a/packages/core/src/modules/dids/methods/sov/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './IndySdkSovDidRegistrar' -export * from './IndySdkSovDidResolver' diff --git a/packages/core/src/modules/dids/methods/sov/util.ts b/packages/core/src/modules/dids/methods/sov/util.ts deleted file mode 100644 index 638779dd21..0000000000 --- a/packages/core/src/modules/dids/methods/sov/util.ts +++ /dev/null @@ -1,123 +0,0 @@ -import type { IndyEndpointAttrib } from '../../../ledger' - -import { TypedArrayEncoder } from '../../../../utils' -import { getFullVerkey } from '../../../../utils/did' -import { SECURITY_X25519_CONTEXT_URL } from '../../../vc/constants' -import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../../vc/signature-suites/ed25519/constants' -import { DidDocumentService, DidDocumentBuilder, DidCommV1Service, DidCommV2Service } from '../../domain' -import { convertPublicKeyToX25519 } from '../../domain/key-type/ed25519' - -export function sovDidDocumentFromDid(fullDid: string, verkey: string) { - const verificationMethodId = `${fullDid}#key-1` - const keyAgreementId = `${fullDid}#key-agreement-1` - - const publicKeyBase58 = getFullVerkey(fullDid, verkey) - const publicKeyX25519 = TypedArrayEncoder.toBase58( - convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(publicKeyBase58)) - ) - - const builder = new DidDocumentBuilder(fullDid) - .addContext(ED25519_SUITE_CONTEXT_URL_2018) - .addContext(SECURITY_X25519_CONTEXT_URL) - .addVerificationMethod({ - controller: fullDid, - id: verificationMethodId, - publicKeyBase58: publicKeyBase58, - type: 'Ed25519VerificationKey2018', - }) - .addVerificationMethod({ - controller: fullDid, - id: keyAgreementId, - publicKeyBase58: publicKeyX25519, - type: 'X25519KeyAgreementKey2019', - }) - .addAuthentication(verificationMethodId) - .addAssertionMethod(verificationMethodId) - .addKeyAgreement(keyAgreementId) - - return builder -} - -// Process Indy Attrib Endpoint Types according to: https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html > Read (Resolve) > DID Service Endpoint -function processEndpointTypes(types?: string[]) { - const expectedTypes = ['endpoint', 'did-communication', 'DIDComm'] - const defaultTypes = ['endpoint', 'did-communication'] - - // Return default types if types "is NOT present [or] empty" - if (!types || types.length <= 0) { - return defaultTypes - } - - // Return default types if types "contain any other values" - for (const type of types) { - if (!expectedTypes.includes(type)) { - return defaultTypes - } - } - - // Return provided types - return types -} - -export function addServicesFromEndpointsAttrib( - builder: DidDocumentBuilder, - did: string, - endpoints: IndyEndpointAttrib, - keyAgreementId: string -) { - const { endpoint, routingKeys, types, ...otherEndpoints } = endpoints - - if (endpoint) { - const processedTypes = processEndpointTypes(types) - - // If 'endpoint' included in types, add id to the services array - if (processedTypes.includes('endpoint')) { - builder.addService( - new DidDocumentService({ - id: `${did}#endpoint`, - serviceEndpoint: endpoint, - type: 'endpoint', - }) - ) - } - - // If 'did-communication' included in types, add DIDComm v1 entry - if (processedTypes.includes('did-communication')) { - builder.addService( - new DidCommV1Service({ - id: `${did}#did-communication`, - serviceEndpoint: endpoint, - priority: 0, - routingKeys: routingKeys ?? [], - recipientKeys: [keyAgreementId], - accept: ['didcomm/aip2;env=rfc19'], - }) - ) - - // If 'DIDComm' included in types, add DIDComm v2 entry - if (processedTypes.includes('DIDComm')) { - builder - .addService( - new DidCommV2Service({ - id: `${did}#didcomm-1`, - serviceEndpoint: endpoint, - routingKeys: routingKeys ?? [], - accept: ['didcomm/v2'], - }) - ) - .addContext('https://didcomm.org/messaging/contexts/v2') - } - } - } - - // Add other endpoint types - for (const [type, endpoint] of Object.entries(otherEndpoints)) { - builder.addService( - new DidDocumentService({ - id: `${did}#${type}`, - serviceEndpoint: endpoint as string, - type, - }) - ) - } -} diff --git a/packages/core/src/modules/dids/services/DidResolverService.ts b/packages/core/src/modules/dids/services/DidResolverService.ts index e23206cc9a..7f97d3f9d1 100644 --- a/packages/core/src/modules/dids/services/DidResolverService.ts +++ b/packages/core/src/modules/dids/services/DidResolverService.ts @@ -46,7 +46,10 @@ export class DidResolverService { if (!resolver) { return { ...result, - didResolutionMetadata: { error: 'unsupportedDidMethod' }, + didResolutionMetadata: { + error: 'unsupportedDidMethod', + message: `No did resolver registered for did method ${parsed.method}`, + }, } } diff --git a/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts b/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts index 81f250f294..00b17ad458 100644 --- a/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts +++ b/packages/core/src/modules/dids/services/__tests__/DidResolverService.test.ts @@ -65,6 +65,7 @@ describe('DidResolverService', () => { didDocumentMetadata: {}, didResolutionMetadata: { error: 'unsupportedDidMethod', + message: 'No did resolver registered for did method example', }, }) }) diff --git a/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts b/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts index 19e48cd386..68fb1a0103 100644 --- a/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts +++ b/packages/core/src/modules/discover-features/__tests__/v1-discover-features.e2e.test.ts @@ -1,47 +1,47 @@ -import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' import type { ConnectionRecord } from '../../connections' import type { DiscoverFeaturesDisclosureReceivedEvent, DiscoverFeaturesQueryReceivedEvent, } from '../DiscoverFeaturesEvents' -import { ReplaySubject, Subject } from 'rxjs' +import { ReplaySubject } from 'rxjs' -import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' +import { setupSubjectTransports } from '../../../../tests' import { getAgentOptions, makeConnection } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { DiscoverFeaturesEventTypes } from '../DiscoverFeaturesEvents' import { waitForDisclosureSubject, waitForQuerySubject } from './helpers' +const faberAgentOptions = getAgentOptions( + 'Faber Discover Features V1 E2E', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() +) + +const aliceAgentOptions = getAgentOptions( + 'Alice Discover Features V1 E2E', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) + describe('v1 discover features', () => { let faberAgent: Agent let aliceAgent: Agent let faberConnection: ConnectionRecord beforeAll(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - const faberAgentOptions = getAgentOptions('Faber Discover Features V1 E2E', { - endpoints: ['rxjs:faber'], - }) - - const aliceAgentOptions = getAgentOptions('Alice Discover Features V1 E2E', { - endpoints: ['rxjs:alice'], - }) faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + + setupSubjectTransports([faberAgent, aliceAgent]) + + await faberAgent.initialize() await aliceAgent.initialize() ;[faberConnection] = await makeConnection(faberAgent, aliceAgent) }) @@ -53,7 +53,7 @@ describe('v1 discover features', () => { await aliceAgent.wallet.delete() }) - test('Faber asks Alice for issue credential protocol support', async () => { + test('Faber asks Alice for revocation notification protocol support', async () => { const faberReplay = new ReplaySubject() const aliceReplay = new ReplaySubject() @@ -67,14 +67,14 @@ describe('v1 discover features', () => { await faberAgent.discovery.queryFeatures({ connectionId: faberConnection.id, protocolVersion: 'v1', - queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/revocation_notification/*' }], }) const query = await waitForQuerySubject(aliceReplay, { timeoutMs: 10000 }) expect(query).toMatchObject({ protocolVersion: 'v1', - queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/revocation_notification/*' }], }) const disclosure = await waitForDisclosureSubject(faberReplay, { timeoutMs: 10000 }) @@ -82,24 +82,24 @@ describe('v1 discover features', () => { expect(disclosure).toMatchObject({ protocolVersion: 'v1', disclosures: [ - { type: 'protocol', id: 'https://didcomm.org/issue-credential/1.0', roles: ['holder', 'issuer'] }, - { type: 'protocol', id: 'https://didcomm.org/issue-credential/2.0', roles: ['holder', 'issuer'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/1.0', roles: ['holder'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/2.0', roles: ['holder'] }, ], }) }) - test('Faber asks Alice for issue credential protocol support synchronously', async () => { + test('Faber asks Alice for revocation notification protocol support synchronously', async () => { const matchingFeatures = await faberAgent.discovery.queryFeatures({ connectionId: faberConnection.id, protocolVersion: 'v1', - queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/revocation_notification/*' }], awaitDisclosures: true, }) expect(matchingFeatures).toMatchObject({ features: [ - { type: 'protocol', id: 'https://didcomm.org/issue-credential/1.0', roles: ['holder', 'issuer'] }, - { type: 'protocol', id: 'https://didcomm.org/issue-credential/2.0', roles: ['holder', 'issuer'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/1.0', roles: ['holder'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/2.0', roles: ['holder'] }, ], }) }) diff --git a/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts b/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts index 20e2d72e2b..f5a4b9f782 100644 --- a/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts +++ b/packages/core/src/modules/discover-features/__tests__/v2-discover-features.e2e.test.ts @@ -1,14 +1,13 @@ -import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' import type { ConnectionRecord } from '../../connections' import type { DiscoverFeaturesDisclosureReceivedEvent, DiscoverFeaturesQueryReceivedEvent, } from '../DiscoverFeaturesEvents' -import { ReplaySubject, Subject } from 'rxjs' +import { ReplaySubject } from 'rxjs' -import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' +import { setupSubjectTransports } from '../../../../tests' import { getAgentOptions, makeConnection } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { GoalCode, Feature } from '../../../agent/models' @@ -16,6 +15,22 @@ import { DiscoverFeaturesEventTypes } from '../DiscoverFeaturesEvents' import { waitForDisclosureSubject, waitForQuerySubject } from './helpers' +const faberAgentOptions = getAgentOptions( + 'Faber Discover Features V2 E2E', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() +) + +const aliceAgentOptions = getAgentOptions( + 'Alice Discover Features V2 E2E', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) + describe('v2 discover features', () => { let faberAgent: Agent let aliceAgent: Agent @@ -23,27 +38,11 @@ describe('v2 discover features', () => { let faberConnection: ConnectionRecord beforeAll(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - const faberAgentOptions = getAgentOptions('Faber Discover Features V2 E2E', { - endpoints: ['rxjs:faber'], - }) - - const aliceAgentOptions = getAgentOptions('Alice Discover Features V2 E2E', { - endpoints: ['rxjs:alice'], - }) faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + setupSubjectTransports([faberAgent, aliceAgent]) + + await faberAgent.initialize() await aliceAgent.initialize() ;[faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) }) @@ -70,14 +69,14 @@ describe('v2 discover features', () => { await faberAgent.discovery.queryFeatures({ connectionId: faberConnection.id, protocolVersion: 'v2', - queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/revocation_notification/*' }], }) const query = await waitForQuerySubject(aliceReplay, { timeoutMs: 10000 }) expect(query).toMatchObject({ protocolVersion: 'v2', - queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/revocation_notification/*' }], }) const disclosure = await waitForDisclosureSubject(faberReplay, { timeoutMs: 10000 }) @@ -85,8 +84,8 @@ describe('v2 discover features', () => { expect(disclosure).toMatchObject({ protocolVersion: 'v2', disclosures: [ - { type: 'protocol', id: 'https://didcomm.org/issue-credential/1.0', roles: ['holder', 'issuer'] }, - { type: 'protocol', id: 'https://didcomm.org/issue-credential/2.0', roles: ['holder', 'issuer'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/1.0', roles: ['holder'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/2.0', roles: ['holder'] }, ], }) }) @@ -220,14 +219,14 @@ describe('v2 discover features', () => { const matchingFeatures = await faberAgent.discovery.queryFeatures({ connectionId: faberConnection.id, protocolVersion: 'v2', - queries: [{ featureType: 'protocol', match: 'https://didcomm.org/issue-credential/*' }], + queries: [{ featureType: 'protocol', match: 'https://didcomm.org/revocation_notification/*' }], awaitDisclosures: true, }) expect(matchingFeatures).toMatchObject({ features: [ - { type: 'protocol', id: 'https://didcomm.org/issue-credential/1.0', roles: ['holder', 'issuer'] }, - { type: 'protocol', id: 'https://didcomm.org/issue-credential/2.0', roles: ['holder', 'issuer'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/1.0', roles: ['holder'] }, + { type: 'protocol', id: 'https://didcomm.org/revocation_notification/2.0', roles: ['holder'] }, ], }) }) diff --git a/packages/core/src/modules/indy/IndyModule.ts b/packages/core/src/modules/indy/IndyModule.ts deleted file mode 100644 index 563a853874..0000000000 --- a/packages/core/src/modules/indy/IndyModule.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { DependencyManager, Module } from '../../plugins' - -import { IndyRevocationService, IndyUtilitiesService } from './services' -import { IndyHolderService } from './services/IndyHolderService' -import { IndyIssuerService } from './services/IndyIssuerService' -import { IndyVerifierService } from './services/IndyVerifierService' - -export class IndyModule implements Module { - public register(dependencyManager: DependencyManager) { - dependencyManager.registerSingleton(IndyIssuerService) - dependencyManager.registerSingleton(IndyHolderService) - dependencyManager.registerSingleton(IndyVerifierService) - dependencyManager.registerSingleton(IndyRevocationService) - dependencyManager.registerSingleton(IndyUtilitiesService) - } -} diff --git a/packages/core/src/modules/indy/__tests__/IndyModule.test.ts b/packages/core/src/modules/indy/__tests__/IndyModule.test.ts deleted file mode 100644 index edad08f2d6..0000000000 --- a/packages/core/src/modules/indy/__tests__/IndyModule.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { DependencyManager } from '../../../plugins/DependencyManager' -import { IndyModule } from '../IndyModule' -import { - IndyHolderService, - IndyIssuerService, - IndyVerifierService, - IndyRevocationService, - IndyUtilitiesService, -} from '../services' - -jest.mock('../../../plugins/DependencyManager') -const DependencyManagerMock = DependencyManager as jest.Mock - -const dependencyManager = new DependencyManagerMock() - -describe('IndyModule', () => { - test('registers dependencies on the dependency manager', () => { - new IndyModule().register(dependencyManager) - - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(5) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyHolderService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyIssuerService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyRevocationService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyVerifierService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyUtilitiesService) - }) -}) diff --git a/packages/core/src/modules/indy/index.ts b/packages/core/src/modules/indy/index.ts deleted file mode 100644 index 5b289c3de8..0000000000 --- a/packages/core/src/modules/indy/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './services' -export * from './IndyModule' diff --git a/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts deleted file mode 100644 index 699abb6148..0000000000 --- a/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRecord.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { CredDef } from 'indy-sdk' - -import { BaseRecord } from '../../../storage/BaseRecord' -import { uuid } from '../../../utils/uuid' - -export interface AnonCredsCredentialDefinitionRecordProps { - credentialDefinition: CredDef -} - -export class AnonCredsCredentialDefinitionRecord extends BaseRecord { - public static readonly type = 'AnonCredsCredentialDefinitionRecord' - public readonly type = AnonCredsCredentialDefinitionRecord.type - - public readonly credentialDefinition!: CredDef - - public constructor(props: AnonCredsCredentialDefinitionRecordProps) { - super() - - if (props) { - this.id = uuid() - this.credentialDefinition = props.credentialDefinition - } - } - - public getTags() { - return { - ...this._tags, - credentialDefinitionId: this.credentialDefinition.id, - } - } -} diff --git a/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRepository.ts b/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRepository.ts deleted file mode 100644 index 706c7e2cac..0000000000 --- a/packages/core/src/modules/indy/repository/AnonCredsCredentialDefinitionRepository.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { AgentContext } from '../../../agent/context/AgentContext' - -import { EventEmitter } from '../../../agent/EventEmitter' -import { InjectionSymbols } from '../../../constants' -import { injectable, inject } from '../../../plugins' -import { Repository } from '../../../storage/Repository' -import { StorageService } from '../../../storage/StorageService' - -import { AnonCredsCredentialDefinitionRecord } from './AnonCredsCredentialDefinitionRecord' - -@injectable() -export class AnonCredsCredentialDefinitionRepository extends Repository { - public constructor( - @inject(InjectionSymbols.StorageService) storageService: StorageService, - eventEmitter: EventEmitter - ) { - super(AnonCredsCredentialDefinitionRecord, storageService, eventEmitter) - } - - public async getByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { - return this.getSingleByQuery(agentContext, { credentialDefinitionId }) - } - - public async findByCredentialDefinitionId(agentContext: AgentContext, credentialDefinitionId: string) { - return this.findSingleByQuery(agentContext, { credentialDefinitionId }) - } -} diff --git a/packages/core/src/modules/indy/repository/AnonCredsSchemaRecord.ts b/packages/core/src/modules/indy/repository/AnonCredsSchemaRecord.ts deleted file mode 100644 index 70eb12df38..0000000000 --- a/packages/core/src/modules/indy/repository/AnonCredsSchemaRecord.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { Schema } from 'indy-sdk' - -import { BaseRecord } from '../../../storage/BaseRecord' -import { didFromSchemaId } from '../../../utils/did' -import { uuid } from '../../../utils/uuid' - -export interface AnonCredsSchemaRecordProps { - schema: Schema - id?: string -} - -export type DefaultAnonCredsSchemaTags = { - schemaId: string - schemaIssuerDid: string - schemaName: string - schemaVersion: string -} - -export class AnonCredsSchemaRecord extends BaseRecord { - public static readonly type = 'AnonCredsSchemaRecord' - public readonly type = AnonCredsSchemaRecord.type - - public readonly schema!: Schema - - public constructor(props: AnonCredsSchemaRecordProps) { - super() - - if (props) { - this.id = props.id ?? uuid() - this.schema = props.schema - } - } - - public getTags() { - return { - ...this._tags, - schemaId: this.schema.id, - schemaIssuerDid: didFromSchemaId(this.schema.id), - schemaName: this.schema.name, - schemaVersion: this.schema.version, - } - } -} diff --git a/packages/core/src/modules/indy/repository/AnonCredsSchemaRepository.ts b/packages/core/src/modules/indy/repository/AnonCredsSchemaRepository.ts deleted file mode 100644 index 311931f1f6..0000000000 --- a/packages/core/src/modules/indy/repository/AnonCredsSchemaRepository.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { AgentContext } from '../../../agent/context/AgentContext' - -import { EventEmitter } from '../../../agent/EventEmitter' -import { InjectionSymbols } from '../../../constants' -import { injectable, inject } from '../../../plugins' -import { Repository } from '../../../storage/Repository' -import { StorageService } from '../../../storage/StorageService' - -import { AnonCredsSchemaRecord } from './AnonCredsSchemaRecord' - -@injectable() -export class AnonCredsSchemaRepository extends Repository { - public constructor( - @inject(InjectionSymbols.StorageService) storageService: StorageService, - eventEmitter: EventEmitter - ) { - super(AnonCredsSchemaRecord, storageService, eventEmitter) - } - - public async getBySchemaId(agentContext: AgentContext, schemaId: string) { - return this.getSingleByQuery(agentContext, { schemaId: schemaId }) - } - - public async findBySchemaId(agentContext: AgentContext, schemaId: string) { - return await this.findSingleByQuery(agentContext, { schemaId: schemaId }) - } -} diff --git a/packages/core/src/modules/indy/services/IndyHolderService.ts b/packages/core/src/modules/indy/services/IndyHolderService.ts deleted file mode 100644 index a53f2b7049..0000000000 --- a/packages/core/src/modules/indy/services/IndyHolderService.ts +++ /dev/null @@ -1,294 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { RequestedCredentials } from '../../proofs/formats/indy/models/RequestedCredentials' -import type * as Indy from 'indy-sdk' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { IndySdkError } from '../../../error/IndySdkError' -import { Logger } from '../../../logger' -import { injectable, inject } from '../../../plugins' -import { isIndyError } from '../../../utils/indyError' -import { assertIndyWallet } from '../../../wallet/util/assertIndyWallet' - -import { IndyRevocationService } from './IndyRevocationService' - -@injectable() -export class IndyHolderService { - private indy: typeof Indy - private logger: Logger - private indyRevocationService: IndyRevocationService - - public constructor( - indyRevocationService: IndyRevocationService, - @inject(InjectionSymbols.Logger) logger: Logger, - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies - ) { - this.indy = agentDependencies.indy - this.indyRevocationService = indyRevocationService - this.logger = logger - } - - /** - * Creates an Indy Proof in response to a proof request. Will create revocation state if the proof request requests proof of non-revocation - * - * @param proofRequest a Indy proof request - * @param requestedCredentials the requested credentials to use for the proof creation - * @param schemas schemas to use in proof creation - * @param credentialDefinitions credential definitions to use in proof creation - * @throws {Error} if there is an error during proof generation or revocation state generation - * @returns a promise of Indy Proof - * - * @todo support attribute non_revoked fields - */ - public async createProof( - agentContext: AgentContext, - { proofRequest, requestedCredentials, schemas, credentialDefinitions }: CreateProofOptions - ): Promise { - assertIndyWallet(agentContext.wallet) - try { - this.logger.debug('Creating Indy Proof') - const revocationStates: Indy.RevStates = await this.indyRevocationService.createRevocationState( - agentContext, - proofRequest, - requestedCredentials - ) - - const indyProof: Indy.IndyProof = await this.indy.proverCreateProof( - agentContext.wallet.handle, - proofRequest, - requestedCredentials.toJSON(), - agentContext.wallet.masterSecretId, - schemas, - credentialDefinitions, - revocationStates - ) - - this.logger.trace('Created Indy Proof', { - indyProof, - }) - - return indyProof - } catch (error) { - this.logger.error(`Error creating Indy Proof`, { - error, - proofRequest, - requestedCredentials, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Store a credential in the wallet. - * - * @returns The credential id - */ - public async storeCredential( - agentContext: AgentContext, - { - credentialRequestMetadata, - credential, - credentialDefinition, - credentialId, - revocationRegistryDefinition, - }: StoreCredentialOptions - ): Promise { - assertIndyWallet(agentContext.wallet) - try { - return await this.indy.proverStoreCredential( - agentContext.wallet.handle, - credentialId ?? null, - credentialRequestMetadata, - credential, - credentialDefinition, - revocationRegistryDefinition ?? null - ) - } catch (error) { - this.logger.error(`Error storing Indy Credential '${credentialId}'`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Get a credential stored in the wallet by id. - * - * @param credentialId the id (referent) of the credential - * @throws {Error} if the credential is not found - * @returns the credential - * - * @todo handle record not found - */ - public async getCredential( - agentContext: AgentContext, - credentialId: Indy.CredentialId - ): Promise { - assertIndyWallet(agentContext.wallet) - try { - return await this.indy.proverGetCredential(agentContext.wallet.handle, credentialId) - } catch (error) { - this.logger.error(`Error getting Indy Credential '${credentialId}'`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Create a credential request for the given credential offer. - * - * @returns The credential request and the credential request metadata - */ - public async createCredentialRequest( - agentContext: AgentContext, - { holderDid, credentialOffer, credentialDefinition }: CreateCredentialRequestOptions - ): Promise<[Indy.CredReq, Indy.CredReqMetadata]> { - assertIndyWallet(agentContext.wallet) - try { - return await this.indy.proverCreateCredentialReq( - agentContext.wallet.handle, - holderDid, - credentialOffer, - credentialDefinition, - agentContext.wallet.masterSecretId - ) - } catch (error) { - this.logger.error(`Error creating Indy Credential Request`, { - error, - credentialOffer, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Retrieve the credentials that are available for an attribute referent in the proof request. - * - * @param proofRequest The proof request to retrieve the credentials for - * @param attributeReferent An attribute referent from the proof request to retrieve the credentials for - * @param start Starting index - * @param limit Maximum number of records to return - * - * @returns List of credentials that are available for building a proof for the given proof request - * - */ - public async getCredentialsForProofRequest( - agentContext: AgentContext, - { proofRequest, attributeReferent, start = 0, limit = 256, extraQuery }: GetCredentialForProofRequestOptions - ): Promise { - assertIndyWallet(agentContext.wallet) - try { - // Open indy credential search - const searchHandle = await this.indy.proverSearchCredentialsForProofReq( - agentContext.wallet.handle, - proofRequest, - extraQuery ?? null - ) - - try { - // Make sure database cursors start at 'start' (bit ugly, but no way around in indy) - if (start > 0) { - await this.fetchCredentialsForReferent(searchHandle, attributeReferent, start) - } - - // Fetch the credentials - const credentials = await this.fetchCredentialsForReferent(searchHandle, attributeReferent, limit) - - // TODO: sort the credentials (irrevocable first) - return credentials - } finally { - // Always close search - await this.indy.proverCloseCredentialsSearchForProofReq(searchHandle) - } - } catch (error) { - if (isIndyError(error)) { - throw new IndySdkError(error) - } - - throw error - } - } - - /** - * Delete a credential stored in the wallet by id. - * - * @param credentialId the id (referent) of the credential - * - */ - public async deleteCredential(agentContext: AgentContext, credentialId: Indy.CredentialId): Promise { - assertIndyWallet(agentContext.wallet) - try { - return await this.indy.proverDeleteCredential(agentContext.wallet.handle, credentialId) - } catch (error) { - this.logger.error(`Error deleting Indy Credential from Wallet`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async fetchCredentialsForReferent(searchHandle: number, referent: string, limit?: number) { - try { - let credentials: Indy.IndyCredential[] = [] - - // Allow max of 256 per fetch operation - const chunk = limit ? Math.min(256, limit) : 256 - - // Loop while limit not reached (or no limit specified) - while (!limit || credentials.length < limit) { - // Retrieve credentials - const credentialsJson = await this.indy.proverFetchCredentialsForProofReq(searchHandle, referent, chunk) - credentials = [...credentials, ...credentialsJson] - - // If the number of credentials returned is less than chunk - // It means we reached the end of the iterator (no more credentials) - if (credentialsJson.length < chunk) { - return credentials - } - } - - return credentials - } catch (error) { - this.logger.error(`Error Fetching Indy Credentials For Referent`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} - -export interface GetCredentialForProofRequestOptions { - proofRequest: Indy.IndyProofRequest - attributeReferent: string - start?: number - limit?: number - extraQuery?: Indy.ReferentWalletQuery -} - -export interface CreateCredentialRequestOptions { - holderDid: string - credentialOffer: Indy.CredOffer - credentialDefinition: Indy.CredDef -} - -export interface StoreCredentialOptions { - credentialRequestMetadata: Indy.CredReqMetadata - credential: Indy.Cred - credentialDefinition: Indy.CredDef - credentialId?: Indy.CredentialId - revocationRegistryDefinition?: Indy.RevocRegDef -} - -export interface CreateProofOptions { - proofRequest: Indy.IndyProofRequest - requestedCredentials: RequestedCredentials - schemas: Indy.Schemas - credentialDefinitions: Indy.CredentialDefs -} diff --git a/packages/core/src/modules/indy/services/IndyIssuerService.ts b/packages/core/src/modules/indy/services/IndyIssuerService.ts deleted file mode 100644 index 58e9917cf0..0000000000 --- a/packages/core/src/modules/indy/services/IndyIssuerService.ts +++ /dev/null @@ -1,166 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { - Cred, - CredDef, - CredDefId, - CredOffer, - CredReq, - CredRevocId, - CredValues, - default as Indy, - Schema, -} from 'indy-sdk' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' -import { IndySdkError } from '../../../error/IndySdkError' -import { injectable, inject } from '../../../plugins' -import { isIndyError } from '../../../utils/indyError' -import { assertIndyWallet } from '../../../wallet/util/assertIndyWallet' - -import { IndyUtilitiesService } from './IndyUtilitiesService' - -@injectable() -export class IndyIssuerService { - private indy: typeof Indy - private indyUtilitiesService: IndyUtilitiesService - - public constructor( - indyUtilitiesService: IndyUtilitiesService, - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies - ) { - this.indy = agentDependencies.indy - this.indyUtilitiesService = indyUtilitiesService - } - - /** - * Create a new credential schema. - * - * @returns the schema. - */ - public async createSchema( - agentContext: AgentContext, - { originDid, name, version, attributes }: CreateSchemaOptions - ): Promise { - assertIndyWallet(agentContext.wallet) - try { - const [, schema] = await this.indy.issuerCreateSchema(originDid, name, version, attributes) - - return schema - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Create a new credential definition and store it in the wallet. - * - * @returns the credential definition. - */ - public async createCredentialDefinition( - agentContext: AgentContext, - { - issuerDid, - schema, - tag = 'default', - signatureType = 'CL', - supportRevocation = false, - }: CreateCredentialDefinitionOptions - ): Promise { - assertIndyWallet(agentContext.wallet) - try { - const [, credentialDefinition] = await this.indy.issuerCreateAndStoreCredentialDef( - agentContext.wallet.handle, - issuerDid, - schema, - tag, - signatureType, - { - support_revocation: supportRevocation, - } - ) - - return credentialDefinition - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Create a credential offer for the given credential definition id. - * - * @param credentialDefinitionId The credential definition to create an offer for - * @returns The created credential offer - */ - public async createCredentialOffer(agentContext: AgentContext, credentialDefinitionId: CredDefId) { - assertIndyWallet(agentContext.wallet) - try { - return await this.indy.issuerCreateCredentialOffer(agentContext.wallet.handle, credentialDefinitionId) - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * Create a credential. - * - * @returns Credential and revocation id - */ - public async createCredential( - agentContext: AgentContext, - { - credentialOffer, - credentialRequest, - credentialValues, - revocationRegistryId, - tailsFilePath, - }: CreateCredentialOptions - ): Promise<[Cred, CredRevocId]> { - assertIndyWallet(agentContext.wallet) - try { - // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present - const tailsReaderHandle = tailsFilePath ? await this.indyUtilitiesService.createTailsReader(tailsFilePath) : 0 - - if (revocationRegistryId || tailsFilePath) { - throw new AriesFrameworkError('Revocation not supported yet') - } - - const [credential, credentialRevocationId] = await this.indy.issuerCreateCredential( - agentContext.wallet.handle, - credentialOffer, - credentialRequest, - credentialValues, - revocationRegistryId ?? null, - tailsReaderHandle - ) - - return [credential, credentialRevocationId] - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} - -export interface CreateCredentialDefinitionOptions { - issuerDid: string - schema: Schema - tag?: string - signatureType?: 'CL' - supportRevocation?: boolean -} - -export interface CreateCredentialOptions { - credentialOffer: CredOffer - credentialRequest: CredReq - credentialValues: CredValues - revocationRegistryId?: string - tailsFilePath?: string -} - -export interface CreateSchemaOptions { - originDid: string - name: string - version: string - attributes: string[] -} diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts deleted file mode 100644 index c1caf5b297..0000000000 --- a/packages/core/src/modules/indy/services/IndyRevocationService.ts +++ /dev/null @@ -1,198 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { IndyRevocationInterval } from '../../credentials' -import type { RequestedCredentials } from '../../proofs/formats/indy/models/RequestedCredentials' -import type { default as Indy, RevStates } from 'indy-sdk' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' -import { IndySdkError } from '../../../error/IndySdkError' -import { Logger } from '../../../logger' -import { injectable, inject } from '../../../plugins' -import { isIndyError } from '../../../utils/indyError' -import { IndyLedgerService } from '../../ledger' - -import { IndyUtilitiesService } from './IndyUtilitiesService' - -enum RequestReferentType { - Attribute = 'attribute', - Predicate = 'predicate', - SelfAttestedAttribute = 'self-attested-attribute', -} -@injectable() -export class IndyRevocationService { - private indy: typeof Indy - private indyUtilitiesService: IndyUtilitiesService - private ledgerService: IndyLedgerService - private logger: Logger - - public constructor( - indyUtilitiesService: IndyUtilitiesService, - ledgerService: IndyLedgerService, - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, - @inject(InjectionSymbols.Logger) logger: Logger - ) { - this.indy = agentDependencies.indy - this.indyUtilitiesService = indyUtilitiesService - this.logger = logger - this.ledgerService = ledgerService - } - - public async createRevocationState( - agentContext: AgentContext, - proofRequest: Indy.IndyProofRequest, - requestedCredentials: RequestedCredentials - ): Promise { - try { - this.logger.debug(`Creating Revocation State(s) for proof request`, { - proofRequest, - requestedCredentials, - }) - const revocationStates: RevStates = {} - const referentCredentials = [] - - //Retrieve information for referents and push to single array - for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedAttributes)) { - referentCredentials.push({ - referent, - credentialInfo: requestedCredential.credentialInfo, - type: RequestReferentType.Attribute, - }) - } - for (const [referent, requestedCredential] of Object.entries(requestedCredentials.requestedPredicates)) { - referentCredentials.push({ - referent, - credentialInfo: requestedCredential.credentialInfo, - type: RequestReferentType.Predicate, - }) - } - - for (const { referent, credentialInfo, type } of referentCredentials) { - if (!credentialInfo) { - throw new AriesFrameworkError( - `Credential for referent '${referent} does not have credential info for revocation state creation` - ) - } - - // Prefer referent-specific revocation interval over global revocation interval - const referentRevocationInterval = - type === RequestReferentType.Predicate - ? proofRequest.requested_predicates[referent].non_revoked - : proofRequest.requested_attributes[referent].non_revoked - const requestRevocationInterval = referentRevocationInterval ?? proofRequest.non_revoked - const credentialRevocationId = credentialInfo.credentialRevocationId - const revocationRegistryId = credentialInfo.revocationRegistryId - - // If revocation interval is present and the credential is revocable then create revocation state - if (requestRevocationInterval && credentialRevocationId && revocationRegistryId) { - this.logger.trace( - `Presentation is requesting proof of non revocation for ${type} referent '${referent}', creating revocation state for credential`, - { - requestRevocationInterval, - credentialRevocationId, - revocationRegistryId, - } - ) - - this.assertRevocationInterval(requestRevocationInterval) - - const { revocationRegistryDefinition } = await this.ledgerService.getRevocationRegistryDefinition( - agentContext, - revocationRegistryId - ) - - const { revocationRegistryDelta, deltaTimestamp } = await this.ledgerService.getRevocationRegistryDelta( - agentContext, - revocationRegistryId, - requestRevocationInterval?.to, - 0 - ) - - const { tailsLocation, tailsHash } = revocationRegistryDefinition.value - const tails = await this.indyUtilitiesService.downloadTails(tailsHash, tailsLocation) - - const revocationState = await this.indy.createRevocationState( - tails, - revocationRegistryDefinition, - revocationRegistryDelta, - deltaTimestamp, - credentialRevocationId - ) - const timestamp = revocationState.timestamp - - if (!revocationStates[revocationRegistryId]) { - revocationStates[revocationRegistryId] = {} - } - revocationStates[revocationRegistryId][timestamp] = revocationState - } - } - - this.logger.debug(`Created Revocation States for Proof Request`, { - revocationStates, - }) - - return revocationStates - } catch (error) { - this.logger.error(`Error creating Indy Revocation State for Proof Request`, { - error, - proofRequest, - requestedCredentials, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - // Get revocation status for credential (given a from-to) - // Note from-to interval details: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals - public async getRevocationStatus( - agentContext: AgentContext, - credentialRevocationId: string, - revocationRegistryDefinitionId: string, - requestRevocationInterval: IndyRevocationInterval - ): Promise<{ revoked: boolean; deltaTimestamp: number }> { - this.logger.trace( - `Fetching Credential Revocation Status for Credential Revocation Id '${credentialRevocationId}' with revocation interval with to '${requestRevocationInterval.to}' & from '${requestRevocationInterval.from}'` - ) - - this.assertRevocationInterval(requestRevocationInterval) - - const { revocationRegistryDelta, deltaTimestamp } = await this.ledgerService.getRevocationRegistryDelta( - agentContext, - revocationRegistryDefinitionId, - requestRevocationInterval.to, - 0 - ) - - const revoked: boolean = revocationRegistryDelta.value.revoked?.includes(parseInt(credentialRevocationId)) || false - this.logger.trace( - `Credential with Credential Revocation Id '${credentialRevocationId}' is ${ - revoked ? '' : 'not ' - }revoked with revocation interval with to '${requestRevocationInterval.to}' & from '${ - requestRevocationInterval.from - }'` - ) - - return { - revoked, - deltaTimestamp, - } - } - - // TODO: Add Test - // Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints - private assertRevocationInterval(requestRevocationInterval: IndyRevocationInterval) { - if (!requestRevocationInterval.to) { - throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) - } - - if ( - (requestRevocationInterval.from || requestRevocationInterval.from === 0) && - requestRevocationInterval.to !== requestRevocationInterval.from - ) { - throw new AriesFrameworkError( - `Presentation requests proof of non-revocation with an interval from: '${requestRevocationInterval.from}' that does not match the interval to: '${requestRevocationInterval.to}', as specified in Aries RFC 0441` - ) - } - } -} diff --git a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts deleted file mode 100644 index 44b9713352..0000000000 --- a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { BlobReaderHandle, default as Indy } from 'indy-sdk' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { AriesFrameworkError } from '../../../error' -import { IndySdkError } from '../../../error/IndySdkError' -import { Logger } from '../../../logger' -import { injectable, inject } from '../../../plugins' -import { FileSystem } from '../../../storage/FileSystem' -import { isIndyError } from '../../../utils/indyError' -import { getDirFromFilePath } from '../../../utils/path' - -@injectable() -export class IndyUtilitiesService { - private indy: typeof Indy - private logger: Logger - private fileSystem: FileSystem - - public constructor( - @inject(InjectionSymbols.Logger) logger: Logger, - @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies - ) { - this.indy = agentDependencies.indy - this.logger = logger - this.fileSystem = fileSystem - } - - /** - * Get a handler for the blob storage tails file reader. - * - * @param tailsFilePath The path of the tails file - * @returns The blob storage reader handle - */ - public async createTailsReader(tailsFilePath: string): Promise { - try { - this.logger.debug(`Opening tails reader at path ${tailsFilePath}`) - const tailsFileExists = await this.fileSystem.exists(tailsFilePath) - - // Extract directory from path (should also work with windows paths) - const dirname = getDirFromFilePath(tailsFilePath) - - if (!tailsFileExists) { - throw new AriesFrameworkError(`Tails file does not exist at path ${tailsFilePath}`) - } - - const tailsReaderConfig = { - base_dir: dirname, - } - - const tailsReader = await this.indy.openBlobStorageReader('default', tailsReaderConfig) - this.logger.debug(`Opened tails reader at path ${tailsFilePath}`) - return tailsReader - } catch (error) { - if (isIndyError(error)) { - throw new IndySdkError(error) - } - - throw error - } - } - - public async downloadTails(hash: string, tailsLocation: string): Promise { - try { - this.logger.debug(`Checking to see if tails file for URL ${tailsLocation} has been stored in the FileSystem`) - const filePath = `${this.fileSystem.cachePath}/tails/${hash}` - - const tailsExists = await this.fileSystem.exists(filePath) - this.logger.debug(`Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${filePath}`) - if (!tailsExists) { - this.logger.debug(`Retrieving tails file from URL ${tailsLocation}`) - - await this.fileSystem.downloadToFile(tailsLocation, filePath) - this.logger.debug(`Saved tails file to FileSystem at path ${filePath}`) - - //TODO: Validate Tails File Hash - } - - this.logger.debug(`Tails file for URL ${tailsLocation} is stored in the FileSystem, opening tails reader`) - return this.createTailsReader(filePath) - } catch (error) { - this.logger.error(`Error while retrieving tails file from URL ${tailsLocation}`, { - error, - }) - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} diff --git a/packages/core/src/modules/indy/services/IndyVerifierService.ts b/packages/core/src/modules/indy/services/IndyVerifierService.ts deleted file mode 100644 index c6ad15bb77..0000000000 --- a/packages/core/src/modules/indy/services/IndyVerifierService.ts +++ /dev/null @@ -1,85 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type * as Indy from 'indy-sdk' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { IndySdkError } from '../../../error' -import { injectable, inject } from '../../../plugins' -import { isIndyError } from '../../../utils/indyError' -import { IndyLedgerService } from '../../ledger/services/IndyLedgerService' - -@injectable() -export class IndyVerifierService { - private indy: typeof Indy - private ledgerService: IndyLedgerService - - public constructor( - ledgerService: IndyLedgerService, - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies - ) { - this.indy = agentDependencies.indy - this.ledgerService = ledgerService - } - - public async verifyProof( - agentContext: AgentContext, - { proofRequest, proof, schemas, credentialDefinitions }: VerifyProofOptions - ): Promise { - try { - const { revocationRegistryDefinitions, revocationRegistries } = await this.getRevocationRegistries( - agentContext, - proof - ) - - return await this.indy.verifierVerifyProof( - proofRequest, - proof, - schemas, - credentialDefinitions, - revocationRegistryDefinitions, - revocationRegistries - ) - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async getRevocationRegistries(agentContext: AgentContext, proof: Indy.IndyProof) { - const revocationRegistryDefinitions: Indy.RevocRegDefs = {} - const revocationRegistries: Indy.RevRegs = Object.create(null) - for (const identifier of proof.identifiers) { - const revocationRegistryId = identifier.rev_reg_id - const timestamp = identifier.timestamp - - //Fetch Revocation Registry Definition if not already fetched - if (revocationRegistryId && !revocationRegistryDefinitions[revocationRegistryId]) { - const { revocationRegistryDefinition } = await this.ledgerService.getRevocationRegistryDefinition( - agentContext, - revocationRegistryId - ) - revocationRegistryDefinitions[revocationRegistryId] = revocationRegistryDefinition - } - - //Fetch Revocation Registry by Timestamp if not already fetched - if (revocationRegistryId && timestamp && !revocationRegistries[revocationRegistryId]?.[timestamp]) { - if (!revocationRegistries[revocationRegistryId]) { - revocationRegistries[revocationRegistryId] = Object.create(null) - } - const { revocationRegistry } = await this.ledgerService.getRevocationRegistry( - agentContext, - revocationRegistryId, - timestamp - ) - revocationRegistries[revocationRegistryId][timestamp] = revocationRegistry - } - } - return { revocationRegistryDefinitions, revocationRegistries } - } -} - -export interface VerifyProofOptions { - proofRequest: Indy.IndyProofRequest - proof: Indy.IndyProof - schemas: Indy.Schemas - credentialDefinitions: Indy.CredentialDefs -} diff --git a/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts b/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts deleted file mode 100644 index 35afdc14ab..0000000000 --- a/packages/core/src/modules/indy/services/__mocks__/IndyHolderService.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { CreateCredentialRequestOptions, StoreCredentialOptions } from '../IndyHolderService' - -export const IndyHolderService = jest.fn(() => ({ - storeCredential: jest.fn((_, { credentialId }: StoreCredentialOptions) => - Promise.resolve(credentialId ?? 'some-random-uuid') - ), - deleteCredential: jest.fn(() => Promise.resolve()), - createCredentialRequest: jest.fn((_, { holderDid, credentialDefinition }: CreateCredentialRequestOptions) => - Promise.resolve([ - { - prover_did: holderDid, - cred_def_id: credentialDefinition.id, - blinded_ms: {}, - blinded_ms_correctness_proof: {}, - nonce: 'nonce', - }, - { cred_req: 'meta-data' }, - ]) - ), -})) diff --git a/packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts b/packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts deleted file mode 100644 index 823e961a15..0000000000 --- a/packages/core/src/modules/indy/services/__mocks__/IndyIssuerService.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const IndyIssuerService = jest.fn(() => ({ - createCredential: jest.fn(() => - Promise.resolve([ - { - schema_id: 'schema_id', - cred_def_id: 'cred_def_id', - rev_reg_def_id: 'rev_reg_def_id', - values: {}, - signature: 'signature', - signature_correctness_proof: 'signature_correctness_proof', - }, - '1', - ]) - ), - - createCredentialOffer: jest.fn((_, credentialDefinitionId: string) => - Promise.resolve({ - schema_id: 'aaa', - cred_def_id: credentialDefinitionId, - // Fields below can depend on Cred Def type - nonce: 'nonce', - key_correctness_proof: {}, - }) - ), -})) diff --git a/packages/core/src/modules/indy/services/__mocks__/IndyVerifierService.ts b/packages/core/src/modules/indy/services/__mocks__/IndyVerifierService.ts deleted file mode 100644 index 483a384f67..0000000000 --- a/packages/core/src/modules/indy/services/__mocks__/IndyVerifierService.ts +++ /dev/null @@ -1 +0,0 @@ -export const IndyVerifierService = jest.fn(() => ({})) diff --git a/packages/core/src/modules/indy/services/index.ts b/packages/core/src/modules/indy/services/index.ts deleted file mode 100644 index fa01eaf419..0000000000 --- a/packages/core/src/modules/indy/services/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './IndyHolderService' -export * from './IndyIssuerService' -export * from './IndyVerifierService' -export * from './IndyUtilitiesService' -export * from './IndyRevocationService' diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts deleted file mode 100644 index d8abffc113..0000000000 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ /dev/null @@ -1,209 +0,0 @@ -import type { AgentDependencies } from '../../agent/AgentDependencies' -import type { Logger } from '../../logger' -import type { FileSystem } from '../../storage/FileSystem' -import type { DidIndyNamespace } from '../../utils/indyIdentifiers' -import type * as Indy from 'indy-sdk' -import type { Subject } from 'rxjs' - -import { AriesFrameworkError, IndySdkError } from '../../error' -import { isIndyError } from '../../utils/indyError' - -import { LedgerError } from './error/LedgerError' -import { isLedgerRejectResponse, isLedgerReqnackResponse } from './ledgerUtil' - -export interface TransactionAuthorAgreement { - version: `${number}.${number}` | `${number}` - acceptanceMechanism: string -} - -export interface IndyPoolConfig { - genesisPath?: string - genesisTransactions?: string - id: string - isProduction: boolean - indyNamespace: DidIndyNamespace - transactionAuthorAgreement?: TransactionAuthorAgreement -} - -export class IndyPool { - private indy: typeof Indy - private logger: Logger - private fileSystem: FileSystem - private poolConfig: IndyPoolConfig - private _poolHandle?: number - private poolConnected?: Promise - public authorAgreement?: AuthorAgreement | null - - public constructor( - poolConfig: IndyPoolConfig, - agentDependencies: AgentDependencies, - logger: Logger, - stop$: Subject, - fileSystem: FileSystem - ) { - this.indy = agentDependencies.indy - this.fileSystem = fileSystem - this.poolConfig = poolConfig - this.logger = logger - - // Listen to stop$ (shutdown) and close pool - stop$.subscribe(async () => { - if (this._poolHandle) { - await this.close() - } - }) - } - - public get didIndyNamespace(): string { - return this.didIndyNamespace - } - - public get id() { - return this.poolConfig.id - } - - public get config() { - return this.poolConfig - } - - public async close() { - const poolHandle = this._poolHandle - - if (!poolHandle) { - return - } - - this._poolHandle = undefined - this.poolConnected = undefined - - await this.indy.closePoolLedger(poolHandle) - } - - public async delete() { - // Close the pool if currently open - if (this._poolHandle) { - await this.close() - } - - await this.indy.deletePoolLedgerConfig(this.poolConfig.id) - } - - public async connect() { - if (!this.poolConnected) { - // Save the promise of connectToLedger to determine if we are done connecting - this.poolConnected = this.connectToLedger() - this.poolConnected.catch((error) => { - // Set poolConnected to undefined so we can retry connection upon failure - this.poolConnected = undefined - this.logger.error('Connection to pool: ' + this.poolConfig.genesisPath + ' failed.', { error }) - }) - return this.poolConnected - } else { - throw new AriesFrameworkError('Cannot attempt connection to ledger, already connecting.') - } - } - - private async connectToLedger() { - const poolName = this.poolConfig.id - const genesisPath = await this.getGenesisPath() - - if (!genesisPath) { - throw new AriesFrameworkError('Cannot connect to ledger without genesis file') - } - - this.logger.debug(`Connecting to ledger pool '${poolName}'`, { genesisPath }) - await this.indy.setProtocolVersion(2) - - try { - this._poolHandle = await this.indy.openPoolLedger(poolName) - return this._poolHandle - } catch (error) { - if (!isIndyError(error, 'PoolLedgerNotCreatedError')) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - this.logger.debug(`Pool '${poolName}' does not exist yet, creating.`, { - indyError: 'PoolLedgerNotCreatedError', - }) - try { - await this.indy.createPoolLedgerConfig(poolName, { genesis_txn: genesisPath }) - this._poolHandle = await this.indy.openPoolLedger(poolName) - return this._poolHandle - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async submitRequest(request: Indy.LedgerRequest) { - return this.indy.submitRequest(await this.getPoolHandle(), request) - } - - public async submitReadRequest(request: Indy.LedgerRequest) { - const response = await this.submitRequest(request) - - if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { - throw new LedgerError(`Ledger '${this.id}' rejected read transaction request: ${response.reason}`) - } - - return response as Indy.LedgerReadReplyResponse - } - - public async submitWriteRequest(request: Indy.LedgerRequest) { - const response = await this.submitRequest(request) - - if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { - throw new LedgerError(`Ledger '${this.id}' rejected write transaction request: ${response.reason}`) - } - - return response as Indy.LedgerWriteReplyResponse - } - - private async getPoolHandle() { - if (this.poolConnected) { - // If we have tried to already connect to pool wait for it - try { - await this.poolConnected - } catch (error) { - this.logger.error('Connection to pool: ' + this.poolConfig.genesisPath + ' failed.', { error }) - } - } - - if (!this._poolHandle) { - return this.connect() - } - - return this._poolHandle - } - - private async getGenesisPath() { - // If the path is already provided return it - if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath - - // Determine the genesisPath - const genesisPath = this.fileSystem.tempPath + `/genesis-${this.poolConfig.id}.txn` - // Store genesis data if provided - if (this.poolConfig.genesisTransactions) { - await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) - this.poolConfig.genesisPath = genesisPath - return genesisPath - } - - // No genesisPath - return null - } -} - -export interface AuthorAgreement { - digest: string - version: string - text: string - ratification_ts: number - acceptanceMechanisms: AcceptanceMechanisms -} - -export interface AcceptanceMechanisms { - aml: Record - amlContext: string - version: string -} diff --git a/packages/core/src/modules/ledger/LedgerApi.ts b/packages/core/src/modules/ledger/LedgerApi.ts deleted file mode 100644 index bb836cf50d..0000000000 --- a/packages/core/src/modules/ledger/LedgerApi.ts +++ /dev/null @@ -1,217 +0,0 @@ -import type { IndyPoolConfig } from './IndyPool' -import type { SchemaTemplate, CredentialDefinitionTemplate } from './services' -import type { CredDef, NymRole, Schema } from 'indy-sdk' - -import { AgentContext } from '../../agent' -import { AriesFrameworkError } from '../../error' -import { IndySdkError } from '../../error/IndySdkError' -import { injectable } from '../../plugins' -import { isIndyError } from '../../utils/indyError' -import { - getLegacyCredentialDefinitionId, - getLegacySchemaId, - getQualifiedIndyCredentialDefinitionId, - getQualifiedIndySchemaId, -} from '../../utils/indyIdentifiers' -import { AnonCredsCredentialDefinitionRecord } from '../indy/repository/AnonCredsCredentialDefinitionRecord' -import { AnonCredsCredentialDefinitionRepository } from '../indy/repository/AnonCredsCredentialDefinitionRepository' -import { AnonCredsSchemaRecord } from '../indy/repository/AnonCredsSchemaRecord' -import { AnonCredsSchemaRepository } from '../indy/repository/AnonCredsSchemaRepository' - -import { LedgerModuleConfig } from './LedgerModuleConfig' -import { IndyLedgerService } from './services' - -@injectable() -export class LedgerApi { - public config: LedgerModuleConfig - - private ledgerService: IndyLedgerService - private agentContext: AgentContext - private anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository - private anonCredsSchemaRepository: AnonCredsSchemaRepository - - public constructor( - ledgerService: IndyLedgerService, - agentContext: AgentContext, - anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository, - anonCredsSchemaRepository: AnonCredsSchemaRepository, - config: LedgerModuleConfig - ) { - this.ledgerService = ledgerService - this.agentContext = agentContext - this.anonCredsCredentialDefinitionRepository = anonCredsCredentialDefinitionRepository - this.anonCredsSchemaRepository = anonCredsSchemaRepository - this.config = config - } - - public setPools(poolConfigs: IndyPoolConfig[]) { - return this.ledgerService.setPools(poolConfigs) - } - - /** - * Connect to all the ledger pools - */ - public async connectToPools() { - await this.ledgerService.connectToPools() - } - - /** - * @deprecated use agent.dids.create instead - */ - public async registerPublicDid(did: string, verkey: string, alias: string, role?: NymRole) { - const myPublicDid = this.agentContext.wallet.publicDid?.did - - if (!myPublicDid) { - throw new AriesFrameworkError('Agent has no public DID.') - } - - return this.ledgerService.registerPublicDid(this.agentContext, myPublicDid, did, verkey, alias, role) - } - - /** - * @deprecated use agent.dids.resolve instead - */ - public async getPublicDid(did: string) { - return this.ledgerService.getPublicDid(this.agentContext, did) - } - - public async getSchema(id: string) { - return this.ledgerService.getSchema(this.agentContext, id) - } - - public async registerSchema(schema: SchemaTemplate): Promise { - const did = this.agentContext.wallet.publicDid?.did - - if (!did) { - throw new AriesFrameworkError('Agent has no public DID.') - } - - const schemaId = getLegacySchemaId(did, schema.name, schema.version) - - // Generate the qualified ID - const qualifiedIdentifier = getQualifiedIndySchemaId(this.ledgerService.getDidIndyWriteNamespace(), schemaId) - - // Try find the schema in the wallet - const schemaRecord = await this.anonCredsSchemaRepository.findById(this.agentContext, qualifiedIdentifier) - // Schema in wallet - if (schemaRecord) { - // Transform qualified to unqualified - return { - ...schemaRecord.schema, - id: schemaId, - } - } - - const schemaFromLedger = await this.findBySchemaIdOnLedger(schemaId) - - if (schemaFromLedger) return schemaFromLedger - const createdSchema = await this.ledgerService.registerSchema(this.agentContext, did, schema) - - const anonCredsSchema = new AnonCredsSchemaRecord({ - schema: { ...createdSchema, id: qualifiedIdentifier }, - }) - await this.anonCredsSchemaRepository.save(this.agentContext, anonCredsSchema) - - return createdSchema - } - - private async findBySchemaIdOnLedger(schemaId: string) { - try { - return await this.ledgerService.getSchema(this.agentContext, schemaId) - } catch (e) { - if (e instanceof IndySdkError && isIndyError(e.cause, 'LedgerNotFound')) return null - - throw e - } - } - - private async findByCredentialDefinitionIdOnLedger(credentialDefinitionId: string): Promise { - try { - return await this.ledgerService.getCredentialDefinition(this.agentContext, credentialDefinitionId) - } catch (e) { - if (e instanceof IndySdkError && isIndyError(e.cause, 'LedgerNotFound')) return null - - throw e - } - } - - public async registerCredentialDefinition( - credentialDefinitionTemplate: Omit - ) { - const did = this.agentContext.wallet.publicDid?.did - - if (!did) { - throw new AriesFrameworkError('Agent has no public DID.') - } - - // Construct credential definition ID - const credentialDefinitionId = getLegacyCredentialDefinitionId( - did, - credentialDefinitionTemplate.schema.seqNo, - credentialDefinitionTemplate.tag - ) - - // Construct qualified identifier - const qualifiedIdentifier = getQualifiedIndyCredentialDefinitionId( - this.ledgerService.getDidIndyWriteNamespace(), - credentialDefinitionId - ) - - // Check if the credential exists in wallet. If so, return it - const credentialDefinitionRecord = await this.anonCredsCredentialDefinitionRepository.findById( - this.agentContext, - qualifiedIdentifier - ) - - // Credential Definition in wallet - if (credentialDefinitionRecord) { - // Transform qualified to unqualified - return { - ...credentialDefinitionRecord.credentialDefinition, - id: credentialDefinitionId, - } - } - - // Check for the credential on the ledger. - const credentialDefinitionOnLedger = await this.findByCredentialDefinitionIdOnLedger(credentialDefinitionId) - if (credentialDefinitionOnLedger) { - throw new AriesFrameworkError( - `No credential definition record found and credential definition ${credentialDefinitionId} already exists on the ledger.` - ) - } - - // Register the credential - const registeredDefinition = await this.ledgerService.registerCredentialDefinition(this.agentContext, did, { - ...credentialDefinitionTemplate, - signatureType: 'CL', - }) - // Replace the unqualified with qualified Identifier in anonCred - const anonCredCredential = new AnonCredsCredentialDefinitionRecord({ - credentialDefinition: { ...registeredDefinition, id: qualifiedIdentifier }, - }) - await this.anonCredsCredentialDefinitionRepository.save(this.agentContext, anonCredCredential) - - return registeredDefinition - } - - public async getCredentialDefinition(id: string) { - return this.ledgerService.getCredentialDefinition(this.agentContext, id) - } - - public async getRevocationRegistryDefinition(revocationRegistryDefinitionId: string) { - return this.ledgerService.getRevocationRegistryDefinition(this.agentContext, revocationRegistryDefinitionId) - } - - public async getRevocationRegistryDelta( - revocationRegistryDefinitionId: string, - fromSeconds = 0, - toSeconds = new Date().getTime() - ) { - return this.ledgerService.getRevocationRegistryDelta( - this.agentContext, - revocationRegistryDefinitionId, - fromSeconds, - toSeconds - ) - } -} diff --git a/packages/core/src/modules/ledger/LedgerModule.ts b/packages/core/src/modules/ledger/LedgerModule.ts deleted file mode 100644 index 4090d146ab..0000000000 --- a/packages/core/src/modules/ledger/LedgerModule.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { LedgerModuleConfigOptions } from './LedgerModuleConfig' -import type { DependencyManager, Module } from '../../plugins' - -import { AnonCredsCredentialDefinitionRepository } from '../indy/repository/AnonCredsCredentialDefinitionRepository' -import { AnonCredsSchemaRepository } from '../indy/repository/AnonCredsSchemaRepository' - -import { LedgerApi } from './LedgerApi' -import { LedgerModuleConfig } from './LedgerModuleConfig' -import { IndyLedgerService, IndyPoolService } from './services' - -export class LedgerModule implements Module { - public readonly config: LedgerModuleConfig - public readonly api = LedgerApi - - public constructor(config?: LedgerModuleConfigOptions) { - this.config = new LedgerModuleConfig(config) - } - - /** - * Registers the dependencies of the ledger module on the dependency manager. - */ - public register(dependencyManager: DependencyManager) { - // Api - dependencyManager.registerContextScoped(LedgerApi) - - // Config - dependencyManager.registerInstance(LedgerModuleConfig, this.config) - - // Services - dependencyManager.registerSingleton(IndyLedgerService) - dependencyManager.registerSingleton(IndyPoolService) - - // Repositories - dependencyManager.registerSingleton(AnonCredsCredentialDefinitionRepository) - dependencyManager.registerSingleton(AnonCredsSchemaRepository) - } -} diff --git a/packages/core/src/modules/ledger/LedgerModuleConfig.ts b/packages/core/src/modules/ledger/LedgerModuleConfig.ts deleted file mode 100644 index 12c9d99fc0..0000000000 --- a/packages/core/src/modules/ledger/LedgerModuleConfig.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { IndyPoolConfig } from './IndyPool' - -/** - * LedgerModuleConfigOptions defines the interface for the options of the RecipientModuleConfig class. - * This can contain optional parameters that have default values in the config class itself. - */ -export interface LedgerModuleConfigOptions { - /** - * Whether to automatically connect to all {@link LedgerModuleConfigOptions.indyLedgers} on startup. - * This will be done asynchronously, so the initialization of the agent won't be impacted. However, - * this does mean there may be unneeded connections to the ledger. - * - * @default true - */ - connectToIndyLedgersOnStartup?: boolean - - /** - * Array of configurations of indy ledgers to connect to. Each item in the list must include either the `genesisPath` or `genesisTransactions` property. - * - * The first ledger in the list will be used for writing transactions to the ledger. - * - * @default [] - */ - indyLedgers?: IndyPoolConfig[] -} - -export class LedgerModuleConfig { - private options: LedgerModuleConfigOptions - - public constructor(options?: LedgerModuleConfigOptions) { - this.options = options ?? {} - } - - /** See {@link LedgerModuleConfigOptions.connectToIndyLedgersOnStartup} */ - public get connectToIndyLedgersOnStartup() { - return this.options.connectToIndyLedgersOnStartup ?? true - } - - /** See {@link LedgerModuleConfigOptions.indyLedgers} */ - public get indyLedgers() { - return this.options.indyLedgers ?? [] - } -} diff --git a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts b/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts deleted file mode 100644 index b34d2b6fcf..0000000000 --- a/packages/core/src/modules/ledger/__tests__/IndyPoolService.test.ts +++ /dev/null @@ -1,427 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { Cache } from '../../cache' -import type { IndyPoolConfig } from '../IndyPool' -import type { CachedDidResponse } from '../services/IndyPoolService' - -import { Subject } from 'rxjs' - -import { NodeFileSystem } from '../../../../../node/src/NodeFileSystem' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../tests/helpers' -import { SigningProviderRegistry } from '../../../crypto/signing-provider' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' -import { IndyWallet } from '../../../wallet/IndyWallet' -import { CacheModuleConfig, InMemoryLruCache } from '../../cache' -import { LedgerError } from '../error/LedgerError' -import { LedgerNotConfiguredError } from '../error/LedgerNotConfiguredError' -import { LedgerNotFoundError } from '../error/LedgerNotFoundError' -import { IndyPoolService } from '../services/IndyPoolService' - -import { getDidResponsesForDid } from './didResponses' - -const pools: IndyPoolConfig[] = [ - { - id: 'sovrinMain', - indyNamespace: 'sovrin', - isProduction: true, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - { - id: 'sovrinBuilder', - indyNamespace: 'sovrin:builder', - isProduction: false, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - { - id: 'sovringStaging', - indyNamespace: 'sovrin:staging', - isProduction: false, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - { - id: 'indicioMain', - indyNamespace: 'indicio', - isProduction: true, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, - { - id: 'bcovrinTest', - indyNamespace: 'bcovrin:test', - isProduction: false, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, -] - -describe('IndyPoolService', () => { - const config = getAgentConfig('IndyPoolServiceTest', { - indyLedgers: pools, - }) - let agentContext: AgentContext - let wallet: IndyWallet - let poolService: IndyPoolService - let cache: Cache - - beforeAll(async () => { - wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) - }) - - afterAll(async () => { - await wallet.delete() - }) - - beforeEach(async () => { - cache = new InMemoryLruCache({ limit: 200 }) - agentContext = getAgentContext({ - registerInstances: [[CacheModuleConfig, new CacheModuleConfig({ cache })]], - }) - poolService = new IndyPoolService(agentDependencies, config.logger, new Subject(), new NodeFileSystem()) - - poolService.setPools(pools) - }) - - describe('ledgerWritePool', () => { - it('should return the first pool', async () => { - expect(poolService.ledgerWritePool).toBe(poolService.pools[0]) - }) - - it('should throw a LedgerNotConfiguredError error if no pools are configured on the pool service', async () => { - poolService.setPools([]) - - expect(() => poolService.ledgerWritePool).toThrow(LedgerNotConfiguredError) - }) - }) - - describe('getPoolForDid', () => { - it('should throw a LedgerNotConfiguredError error if no pools are configured on the pool service', async () => { - poolService.setPools([]) - - expect(poolService.getPoolForDid(agentContext, 'some-did')).rejects.toThrow(LedgerNotConfiguredError) - }) - - it('should throw a LedgerError if all ledger requests throw an error other than NotFoundError', async () => { - const did = 'Y5bj4SjCiTM9PgeheKAiXx' - - poolService.pools.forEach((pool) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(() => Promise.reject(new AriesFrameworkError('Something went wrong'))) - }) - - expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(LedgerError) - }) - - it('should throw a LedgerNotFoundError if all pools did not find the did on the ledger', async () => { - const did = 'Y5bj4SjCiTM9PgeheKAiXx' - // Not found on any of the ledgers - const responses = getDidResponsesForDid(did, pools, {}) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(LedgerNotFoundError) - }) - - it('should return the pool if the did was only found on one ledger', async () => { - const did = 'TL1EaPFCZ8Si5aUrqScBDt' - // Only found on one ledger - const responses = getDidResponsesForDid(did, pools, { - sovrinMain: '~43X4NhAFqREffK7eWdKgFH', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe('sovrinMain') - }) - - it('should return the first pool with a self certifying DID if at least one did is self certifying ', async () => { - const did = 'did:sov:q7ATwTYbQDgiigVijUAej' - // Found on one production and one non production ledger - const responses = getDidResponsesForDid(did, pools, { - indicioMain: '~43X4NhAFqREffK7eWdKgFH', - bcovrinTest: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', - sovrinBuilder: '~43X4NhAFqREffK7eWdKgFH', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe('sovrinBuilder') - }) - - it('should return the production pool if the did was found on one production and one non production ledger and both DIDs are not self certifying', async () => { - const did = 'V6ty6ttM3EjuCtosH6sGtW' - // Found on one production and one non production ledger - const responses = getDidResponsesForDid(did, pools, { - indicioMain: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', - sovrinBuilder: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe('indicioMain') - }) - - it('should return the pool with the self certified did if the did was found on two production ledgers where one did is self certified', async () => { - const did = 'VsKV7grR1BUE29mG2Fm2kX' - // Found on two production ledgers. Sovrin is self certified - const responses = getDidResponsesForDid(did, pools, { - sovrinMain: '~43X4NhAFqREffK7eWdKgFH', - indicioMain: 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe('sovrinMain') - }) - - it('should return the first pool with a self certified did if the did was found on three non production ledgers where two DIDs are self certified', async () => { - const did = 'HEi9QViXNThGQaDsQ3ptcw' - // Found on two non production ledgers. Sovrin is self certified - const responses = getDidResponsesForDid(did, pools, { - sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', - sovrinStaging: '~M9kv2Ez61cur7X39DXWh8W', - bcovrinTest: '3SeuRm3uYuQDYmHeuMLu1xNHozNTtzS3kbZRFMMCWrX4', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe('sovrinBuilder') - }) - - it('should return the pool from the cache if the did was found in the cache', async () => { - const did = 'HEi9QViXNThGQaDsQ3ptcw' - - const expectedPool = pools[3] - - const didResponse: CachedDidResponse = { - nymResponse: { - did, - role: 'ENDORSER', - verkey: '~M9kv2Ez61cur7X39DXWh8W', - }, - poolId: expectedPool.id, - } - - await cache.set(agentContext, `IndySdkPoolService:${did}`, didResponse) - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe(pool.id) - }) - - it('should set the poolId in the cache if the did was not found in the cache, but resolved later on', async () => { - const did = 'HEi9QViXNThGQaDsQ3ptcw' - // Found on one ledger - const responses = getDidResponsesForDid(did, pools, { - sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', - }) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'submitReadRequest') - spy.mockImplementationOnce(responses[index]) - }) - - const { pool } = await poolService.getPoolForDid(agentContext, did) - - expect(pool.config.id).toBe('sovrinBuilder') - expect(pool.config.indyNamespace).toBe('sovrin:builder') - - expect(await cache.get(agentContext, `IndySdkPoolService:${did}`)).toEqual({ - nymResponse: { - did, - verkey: '~M9kv2Ez61cur7X39DXWh8W', - role: '0', - }, - poolId: 'sovrinBuilder', - }) - }) - }) - - describe('getPoolForNamespace', () => { - it('should throw a LedgerNotConfiguredError error if no pools are configured on the pool service', async () => { - poolService.setPools([]) - - expect(() => poolService.getPoolForNamespace()).toThrow(LedgerNotConfiguredError) - }) - - it('should return the first pool if indyNamespace is not provided', async () => { - const expectedPool = pools[0] - - expect(poolService.getPoolForNamespace().id).toEqual(expectedPool.id) - }) - - it('should throw a LedgerNotFoundError error if any of the pools did not have the provided indyNamespace', async () => { - const indyNameSpace = 'test' - const responses = pools.map((pool) => pool.indyNamespace) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'didIndyNamespace', 'get') - spy.mockReturnValueOnce(responses[index]) - }) - - expect(() => poolService.getPoolForNamespace(indyNameSpace)).toThrow(LedgerNotFoundError) - }) - - it('should return the first pool that indyNamespace matches', async () => { - const expectedPool = pools[3] - const indyNameSpace = 'indicio' - const responses = pools.map((pool) => pool.indyNamespace) - - poolService.pools.forEach((pool, index) => { - const spy = jest.spyOn(pool, 'didIndyNamespace', 'get') - spy.mockReturnValueOnce(responses[index]) - }) - - const pool = poolService.getPoolForNamespace(indyNameSpace) - - expect(pool.id).toEqual(expectedPool.id) - }) - }) - - describe('submitWriteRequest', () => { - it('should throw an error if the config version does not match', async () => { - const pool = poolService.getPoolForNamespace() - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ - digest: 'abcde', - version: '2.0', - text: 'jhsdhbv', - ratification_ts: 12345678, - acceptanceMechanisms: { - aml: { accept: 'accept' }, - amlContext: 'accept', - version: '3', - }, - } as never) - await expect( - poolService.submitWriteRequest( - agentContext, - pool, - { - reqId: 1668174449192969000, - identifier: 'BBPoJqRKatdcfLEAFL7exC', - operation: { - type: '1', - dest: 'N8NQHLtCKfPmWMgCSdfa7h', - verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', - alias: 'Heinz57', - }, - protocolVersion: 2, - }, - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' - ) - ).rejects.toThrowError( - 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["accept"] and version 2.0 in pool.' - ) - }) - - it('should throw an error if the config acceptance mechanism does not match', async () => { - const pool = poolService.getPoolForNamespace() - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ - digest: 'abcde', - version: '1.0', - text: 'jhsdhbv', - ratification_ts: 12345678, - acceptanceMechanisms: { - aml: { decline: 'accept' }, - amlContext: 'accept', - version: '1', - }, - } as never) - await expect( - poolService.submitWriteRequest( - agentContext, - pool, - { - reqId: 1668174449192969000, - identifier: 'BBPoJqRKatdcfLEAFL7exC', - operation: { - type: '1', - dest: 'N8NQHLtCKfPmWMgCSdfa7h', - verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', - alias: 'Heinz57', - }, - protocolVersion: 2, - }, - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' - ) - ).rejects.toThrowError( - 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["decline"] and version 1.0 in pool.' - ) - }) - - it('should throw an error if no config is present', async () => { - const pool = poolService.getPoolForNamespace() - pool.authorAgreement = undefined - pool.config.transactionAuthorAgreement = undefined - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - jest.spyOn(poolService, 'getTransactionAuthorAgreement').mockResolvedValue({ - digest: 'abcde', - version: '1.0', - text: 'jhsdhbv', - ratification_ts: 12345678, - acceptanceMechanisms: { - aml: { accept: 'accept' }, - amlContext: 'accept', - version: '3', - }, - } as never) - await expect( - poolService.submitWriteRequest( - agentContext, - pool, - { - reqId: 1668174449192969000, - identifier: 'BBPoJqRKatdcfLEAFL7exC', - operation: { - type: '1', - dest: 'N8NQHLtCKfPmWMgCSdfa7h', - verkey: 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', - alias: 'Heinz57', - }, - protocolVersion: 2, - }, - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' - ) - ).rejects.toThrowError(/Please, specify a transaction author agreement with version and acceptance mechanism/) - }) - }) -}) diff --git a/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts b/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts deleted file mode 100644 index cf7b35bbc5..0000000000 --- a/packages/core/src/modules/ledger/__tests__/LedgerApi.test.ts +++ /dev/null @@ -1,399 +0,0 @@ -import type { AgentContext } from '../../../agent/context/AgentContext' -import type { IndyPoolConfig } from '../IndyPool' -import type { CredentialDefinitionTemplate } from '../services/IndyLedgerService' -import type * as Indy from 'indy-sdk' - -import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../tests/helpers' -import { SigningProviderRegistry } from '../../../crypto/signing-provider' -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' -import { getLegacySchemaId, getLegacyCredentialDefinitionId } from '../../../utils' -import { IndyWallet } from '../../../wallet/IndyWallet' -import { AnonCredsCredentialDefinitionRecord } from '../../indy/repository/AnonCredsCredentialDefinitionRecord' -import { AnonCredsCredentialDefinitionRepository } from '../../indy/repository/AnonCredsCredentialDefinitionRepository' -import { AnonCredsSchemaRecord } from '../../indy/repository/AnonCredsSchemaRecord' -import { AnonCredsSchemaRepository } from '../../indy/repository/AnonCredsSchemaRepository' -import { LedgerApi } from '../LedgerApi' -import { LedgerModuleConfig } from '../LedgerModuleConfig' -import { IndyLedgerService } from '../services/IndyLedgerService' - -jest.mock('../services/IndyLedgerService') -const IndyLedgerServiceMock = IndyLedgerService as jest.Mock - -jest.mock('../../indy/repository/AnonCredsCredentialDefinitionRepository') -const AnonCredsCredentialDefinitionRepositoryMock = - AnonCredsCredentialDefinitionRepository as jest.Mock -jest.mock('../../indy/repository/AnonCredsSchemaRepository') -const AnonCredsSchemaRepositoryMock = AnonCredsSchemaRepository as jest.Mock - -const did = 'Y5bj4SjCiTM9PgeheKAiXx' - -const schemaId = 'Y5bj4SjCiTM9PgeheKAiXx:2:awesomeSchema:1' - -const schema: Indy.Schema = { - id: schemaId, - attrNames: ['hello', 'world'], - name: 'awesomeSchema', - version: '1', - ver: '1', - seqNo: 99, -} - -const credentialDefinition = { - schema: schema, - tag: 'someTag', - signatureType: 'CL', - supportRevocation: true, -} - -const schemaIdQualified = 'did:indy:sovrin:Y5bj4SjCiTM9PgeheKAiXx/anoncreds/v0/SCHEMA/awesomeSchema/1' -const schemaIdGenerated = getLegacySchemaId(did, schema.name, schema.version) -const qualifiedDidCred = 'did:indy:sovrin:Y5bj4SjCiTM9PgeheKAiXx/anoncreds/v0/CLAIM_DEF/99/someTag' - -const credDef: Indy.CredDef = { - id: qualifiedDidCred, - schemaId: schemaIdQualified, - type: 'CL', - tag: 'someTag', - value: { - primary: credentialDefinition as Record, - revocation: true, - }, - ver: '1', -} - -const credentialDefinitionTemplate: Omit = { - schema: { ...schema, id: schemaIdQualified }, - tag: 'someTag', - supportRevocation: true, -} - -const revocRegDef: Indy.RevocRegDef = { - id: 'abcde', - revocDefType: 'CL_ACCUM', - tag: 'someTag', - credDefId: 'abcde', - value: { - issuanceType: 'ISSUANCE_BY_DEFAULT', - maxCredNum: 3, - tailsHash: 'abcde', - tailsLocation: 'xyz', - publicKeys: { - accumKey: { - z: 'z', - }, - }, - }, - ver: 'abcde', -} - -const credentialDefinitionId = getLegacyCredentialDefinitionId( - did, - credentialDefinitionTemplate.schema.seqNo, - credentialDefinitionTemplate.tag -) - -const pools: IndyPoolConfig[] = [ - { - id: '7Tqg6BwSSWapxgUDm9KKgg', - indyNamespace: 'sovrin', - isProduction: true, - genesisTransactions: 'xxx', - transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, - }, -] - -describe('LedgerApi', () => { - let wallet: IndyWallet - let ledgerService: IndyLedgerService - let anonCredsCredentialDefinitionRepository: AnonCredsCredentialDefinitionRepository - let anonCredsSchemaRepository: AnonCredsSchemaRepository - let ledgerApi: LedgerApi - let agentContext: AgentContext - - const contextCorrelationId = 'mock' - const agentConfig = getAgentConfig('LedgerApiTest', { - indyLedgers: pools, - }) - - beforeEach(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) - }) - - afterEach(async () => { - await wallet.delete() - }) - - beforeEach(async () => { - ledgerService = new IndyLedgerServiceMock() - - agentContext = getAgentContext({ - wallet, - agentConfig, - contextCorrelationId, - }) - - anonCredsCredentialDefinitionRepository = new AnonCredsCredentialDefinitionRepositoryMock() - anonCredsSchemaRepository = new AnonCredsSchemaRepositoryMock() - - ledgerApi = new LedgerApi( - ledgerService, - agentContext, - anonCredsCredentialDefinitionRepository, - anonCredsSchemaRepository, - new LedgerModuleConfig() - ) - }) - - describe('LedgerApi', () => { - // Connect to pools - describe('connectToPools', () => { - it('should connect to all pools', async () => { - mockFunction(ledgerService.connectToPools).mockResolvedValue([1, 2, 4]) - await expect(ledgerApi.connectToPools()).resolves.toBeUndefined() - expect(ledgerService.connectToPools).toHaveBeenCalled() - }) - }) - - // Register public did - describe('registerPublicDid', () => { - it('should register a public DID', async () => { - mockFunction(ledgerService.registerPublicDid).mockResolvedValueOnce(did) - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - await expect(ledgerApi.registerPublicDid(did, 'abcde', 'someAlias')).resolves.toEqual(did) - expect(ledgerService.registerPublicDid).toHaveBeenCalledWith( - agentContext, - did, - did, - 'abcde', - 'someAlias', - undefined - ) - }) - - it('should throw an error if the DID cannot be registered because there is no public did', async () => { - const did = 'Y5bj4SjCiTM9PgeheKAiXx' - mockProperty(wallet, 'publicDid', undefined) - await expect(ledgerApi.registerPublicDid(did, 'abcde', 'someAlias')).rejects.toThrowError(AriesFrameworkError) - }) - }) - - // Get public DID - describe('getPublicDid', () => { - it('should return the public DID if there is one', async () => { - const nymResponse: Indy.GetNymResponse = { did: 'Y5bj4SjCiTM9PgeheKAiXx', verkey: 'abcde', role: 'STEWARD' } - mockProperty(wallet, 'publicDid', { did: nymResponse.did, verkey: nymResponse.verkey }) - mockFunction(ledgerService.getPublicDid).mockResolvedValueOnce(nymResponse) - await expect(ledgerApi.getPublicDid(nymResponse.did)).resolves.toEqual(nymResponse) - expect(ledgerService.getPublicDid).toHaveBeenCalledWith(agentContext, nymResponse.did) - }) - }) - - // Get schema - describe('getSchema', () => { - it('should return the schema by id if there is one', async () => { - mockFunction(ledgerService.getSchema).mockResolvedValueOnce(schema) - await expect(ledgerApi.getSchema(schemaId)).resolves.toEqual(schema) - expect(ledgerService.getSchema).toHaveBeenCalledWith(agentContext, schemaId) - }) - - it('should throw an error if no schema for the id exists', async () => { - mockFunction(ledgerService.getSchema).mockRejectedValueOnce( - new AriesFrameworkError('Error retrieving schema abcd from ledger 1') - ) - await expect(ledgerApi.getSchema(schemaId)).rejects.toThrowError(AriesFrameworkError) - expect(ledgerService.getSchema).toHaveBeenCalledWith(agentContext, schemaId) - }) - }) - - describe('registerSchema', () => { - it('should throw an error if there is no public DID', async () => { - mockProperty(wallet, 'publicDid', undefined) - await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).rejects.toThrowError( - AriesFrameworkError - ) - }) - - it('should return the schema from anonCreds when it already exists', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(anonCredsSchemaRepository.findById).mockResolvedValueOnce( - new AnonCredsSchemaRecord({ schema: { ...schema, id: schemaIdQualified } }) - ) - mockFunction(ledgerService.getDidIndyWriteNamespace).mockReturnValueOnce(pools[0].indyNamespace) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { id, ...schemaWithoutId } = schema - await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toMatchObject({ - ...schema, - id: schema.id, - }) - expect(anonCredsSchemaRepository.findById).toHaveBeenCalledWith(agentContext, schemaIdQualified) - }) - - it('should return the schema from the ledger when it already exists', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - jest - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .spyOn(LedgerApi.prototype as any, 'findBySchemaIdOnLedger') - .mockResolvedValueOnce(new AnonCredsSchemaRecord({ schema: schema })) - mockProperty(ledgerApi, 'config', { - connectToIndyLedgersOnStartup: true, - indyLedgers: pools, - } as LedgerModuleConfig) - await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toHaveProperty( - 'schema', - { ...schema } - ) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(jest.spyOn(LedgerApi.prototype as any, 'findBySchemaIdOnLedger')).toHaveBeenCalledWith(schemaIdGenerated) - }) - - it('should return the schema after registering it', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(ledgerService.registerSchema).mockResolvedValueOnce(schema) - mockProperty(ledgerApi, 'config', { - connectToIndyLedgersOnStartup: true, - indyLedgers: pools, - } as LedgerModuleConfig) - await expect(ledgerApi.registerSchema({ ...schema, attributes: ['hello', 'world'] })).resolves.toEqual(schema) - expect(ledgerService.registerSchema).toHaveBeenCalledWith(agentContext, did, { - ...schema, - attributes: ['hello', 'world'], - }) - }) - }) - - describe('registerCredentialDefinition', () => { - it('should throw an error if there si no public DID', async () => { - mockProperty(wallet, 'publicDid', undefined) - await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).rejects.toThrowError( - AriesFrameworkError - ) - }) - - it('should return the credential definition from the wallet if it already exists', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - const anonCredsCredentialDefinitionRecord: AnonCredsCredentialDefinitionRecord = - new AnonCredsCredentialDefinitionRecord({ - credentialDefinition: credDef, - }) - mockFunction(anonCredsCredentialDefinitionRepository.findById).mockResolvedValueOnce( - anonCredsCredentialDefinitionRecord - ) - mockProperty(ledgerApi, 'config', { - connectToIndyLedgersOnStartup: true, - indyLedgers: pools, - } as LedgerModuleConfig) - mockFunction(ledgerService.getDidIndyWriteNamespace).mockReturnValueOnce(pools[0].indyNamespace) - await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).resolves.toHaveProperty( - 'value.primary', - credentialDefinition - ) - expect(anonCredsCredentialDefinitionRepository.findById).toHaveBeenCalledWith(agentContext, qualifiedDidCred) - }) - - it('should throw an exception if the definition already exists on the ledger', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - jest - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .spyOn(LedgerApi.prototype as any, 'findByCredentialDefinitionIdOnLedger') - .mockResolvedValueOnce({ credentialDefinition: credentialDefinition }) - mockProperty(ledgerApi, 'config', { - connectToIndyLedgersOnStartup: true, - indyLedgers: pools, - } as LedgerModuleConfig) - await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).rejects.toThrowError( - AriesFrameworkError - ) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(jest.spyOn(LedgerApi.prototype as any, 'findByCredentialDefinitionIdOnLedger')).toHaveBeenCalledWith( - credentialDefinitionId - ) - }) - - it('should register the credential successfully if it is neither in the wallet and neither on the ledger', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(ledgerService.registerCredentialDefinition).mockResolvedValueOnce(credDef) - mockProperty(ledgerApi, 'config', { - connectToIndyLedgersOnStartup: true, - indyLedgers: pools, - } as LedgerModuleConfig) - await expect(ledgerApi.registerCredentialDefinition(credentialDefinitionTemplate)).resolves.toEqual(credDef) - expect(ledgerService.registerCredentialDefinition).toHaveBeenCalledWith(agentContext, did, { - ...credentialDefinitionTemplate, - signatureType: 'CL', - }) - }) - }) - - describe('getCredentialDefinition', () => { - it('should return the credential definition given the id', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(ledgerService.getCredentialDefinition).mockResolvedValue(credDef) - await expect(ledgerApi.getCredentialDefinition(credDef.id)).resolves.toEqual(credDef) - expect(ledgerService.getCredentialDefinition).toHaveBeenCalledWith(agentContext, credDef.id) - }) - - it('should throw an error if there is no credential definition for the given id', async () => { - mockProperty(wallet, 'publicDid', { did: did, verkey: 'abcde' }) - mockFunction(ledgerService.getCredentialDefinition).mockRejectedValueOnce(new AriesFrameworkError('')) - await expect(ledgerApi.getCredentialDefinition(credDef.id)).rejects.toThrowError(AriesFrameworkError) - expect(ledgerService.getCredentialDefinition).toHaveBeenCalledWith(agentContext, credDef.id) - }) - }) - - describe('getRevocationRegistryDefinition', () => { - it('should return the ParseRevocationRegistryDefinitionTemplate for a valid revocationRegistryDefinitionId', async () => { - const parseRevocationRegistryDefinitionTemplate = { - revocationRegistryDefinition: revocRegDef, - revocationRegistryDefinitionTxnTime: 12345678, - } - mockFunction(ledgerService.getRevocationRegistryDefinition).mockResolvedValue( - parseRevocationRegistryDefinitionTemplate - ) - await expect(ledgerApi.getRevocationRegistryDefinition(revocRegDef.id)).resolves.toBe( - parseRevocationRegistryDefinitionTemplate - ) - expect(ledgerService.getRevocationRegistryDefinition).toHaveBeenLastCalledWith(agentContext, revocRegDef.id) - }) - - it('should throw an error if the ParseRevocationRegistryDefinitionTemplate does not exists', async () => { - mockFunction(ledgerService.getRevocationRegistryDefinition).mockRejectedValueOnce(new AriesFrameworkError('')) - await expect(ledgerApi.getRevocationRegistryDefinition('abcde')).rejects.toThrowError(AriesFrameworkError) - expect(ledgerService.getRevocationRegistryDefinition).toHaveBeenCalledWith(agentContext, revocRegDef.id) - }) - }) - - describe('getRevocationRegistryDelta', () => { - it('should return the ParseRevocationRegistryDeltaTemplate', async () => { - const revocRegDelta = { - value: { - prevAccum: 'prev', - accum: 'accum', - issued: [1, 2, 3], - revoked: [4, 5, 6], - }, - ver: 'ver', - } - const parseRevocationRegistryDeltaTemplate = { - revocationRegistryDelta: revocRegDelta, - deltaTimestamp: 12345678, - } - - mockFunction(ledgerService.getRevocationRegistryDelta).mockResolvedValueOnce( - parseRevocationRegistryDeltaTemplate - ) - await expect(ledgerApi.getRevocationRegistryDelta('12345')).resolves.toEqual( - parseRevocationRegistryDeltaTemplate - ) - expect(ledgerService.getRevocationRegistryDelta).toHaveBeenCalledTimes(1) - }) - - it('should throw an error if the delta cannot be obtained', async () => { - mockFunction(ledgerService.getRevocationRegistryDelta).mockRejectedValueOnce(new AriesFrameworkError('')) - await expect(ledgerApi.getRevocationRegistryDelta('abcde1234')).rejects.toThrowError(AriesFrameworkError) - expect(ledgerService.getRevocationRegistryDelta).toHaveBeenCalledTimes(1) - }) - }) - }) -}) diff --git a/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts b/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts deleted file mode 100644 index b258bd5416..0000000000 --- a/packages/core/src/modules/ledger/__tests__/LedgerModule.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { DependencyManager } from '../../../plugins/DependencyManager' -import { AnonCredsCredentialDefinitionRepository } from '../../indy/repository/AnonCredsCredentialDefinitionRepository' -import { AnonCredsSchemaRepository } from '../../indy/repository/AnonCredsSchemaRepository' -import { LedgerApi } from '../LedgerApi' -import { LedgerModule } from '../LedgerModule' -import { IndyLedgerService, IndyPoolService } from '../services' - -jest.mock('../../../plugins/DependencyManager') -const DependencyManagerMock = DependencyManager as jest.Mock - -const dependencyManager = new DependencyManagerMock() - -describe('LedgerModule', () => { - test('registers dependencies on the dependency manager', () => { - new LedgerModule().register(dependencyManager) - - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(LedgerApi) - - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(4) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyLedgerService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(IndyPoolService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsCredentialDefinitionRepository) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(AnonCredsSchemaRepository) - }) -}) diff --git a/packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts b/packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts deleted file mode 100644 index ec33976a63..0000000000 --- a/packages/core/src/modules/ledger/__tests__/ledgerUtils.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { LedgerRejectResponse, LedgerReqnackResponse } from 'indy-sdk' - -import * as LedgerUtil from '../ledgerUtil' - -describe('LedgerUtils', () => { - // IsLedgerRejectResponse - it('Should return true if the response op is: REJECT', () => { - const ledgerResponse: LedgerRejectResponse = { - op: 'REJECT', - reqId: 1, - reason: 'Why not', - identifier: '123456', - } - expect(LedgerUtil.isLedgerRejectResponse(ledgerResponse)).toEqual(true) - }) - it('Should return false if the response op is not: REJECT', () => { - const ledgerResponse: LedgerReqnackResponse = { - op: 'REQNACK', - reqId: 1, - reason: 'Why not', - identifier: '123456', - } - expect(LedgerUtil.isLedgerRejectResponse(ledgerResponse)).toEqual(false) - }) - - // isLedgerReqnackResponse - it('Should return true if the response op is: REQNACK', () => { - const ledgerResponse: LedgerReqnackResponse = { - op: 'REQNACK', - reqId: 1, - reason: 'Why not', - identifier: '123456', - } - expect(LedgerUtil.isLedgerReqnackResponse(ledgerResponse)).toEqual(true) - }) - it('Should return false if the response op is NOT: REQNACK', () => { - const ledgerResponse: LedgerRejectResponse = { - op: 'REJECT', - reqId: 1, - reason: 'Why not', - identifier: '123456', - } - expect(LedgerUtil.isLedgerReqnackResponse(ledgerResponse)).toEqual(false) - }) -}) diff --git a/packages/core/src/modules/ledger/error/LedgerError.ts b/packages/core/src/modules/ledger/error/LedgerError.ts deleted file mode 100644 index 1ee8589cf9..0000000000 --- a/packages/core/src/modules/ledger/error/LedgerError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { AriesFrameworkError } from '../../../error/AriesFrameworkError' - -export class LedgerError extends AriesFrameworkError { - public constructor(message: string, { cause }: { cause?: Error } = {}) { - super(message, { cause }) - } -} diff --git a/packages/core/src/modules/ledger/error/LedgerNotConfiguredError.ts b/packages/core/src/modules/ledger/error/LedgerNotConfiguredError.ts deleted file mode 100644 index 0cee3914dc..0000000000 --- a/packages/core/src/modules/ledger/error/LedgerNotConfiguredError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { LedgerError } from './LedgerError' - -export class LedgerNotConfiguredError extends LedgerError { - public constructor(message: string, { cause }: { cause?: Error } = {}) { - super(message, { cause }) - } -} diff --git a/packages/core/src/modules/ledger/error/LedgerNotFoundError.ts b/packages/core/src/modules/ledger/error/LedgerNotFoundError.ts deleted file mode 100644 index 09355964d6..0000000000 --- a/packages/core/src/modules/ledger/error/LedgerNotFoundError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { LedgerError } from './LedgerError' - -export class LedgerNotFoundError extends LedgerError { - public constructor(message: string, { cause }: { cause?: Error } = {}) { - super(message, { cause }) - } -} diff --git a/packages/core/src/modules/ledger/error/index.ts b/packages/core/src/modules/ledger/error/index.ts deleted file mode 100644 index 79c42fc2b6..0000000000 --- a/packages/core/src/modules/ledger/error/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './LedgerError' -export * from './LedgerNotConfiguredError' -export * from './LedgerNotFoundError' diff --git a/packages/core/src/modules/ledger/index.ts b/packages/core/src/modules/ledger/index.ts deleted file mode 100644 index fc65f390db..0000000000 --- a/packages/core/src/modules/ledger/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './services' -export * from './LedgerApi' -export * from './IndyPool' -export * from './LedgerModule' diff --git a/packages/core/src/modules/ledger/ledgerUtil.ts b/packages/core/src/modules/ledger/ledgerUtil.ts deleted file mode 100644 index 62e75f1e72..0000000000 --- a/packages/core/src/modules/ledger/ledgerUtil.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type * as Indy from 'indy-sdk' - -export function isLedgerRejectResponse(response: Indy.LedgerResponse): response is Indy.LedgerRejectResponse { - return response.op === 'REJECT' -} - -export function isLedgerReqnackResponse(response: Indy.LedgerResponse): response is Indy.LedgerReqnackResponse { - return response.op === 'REQNACK' -} diff --git a/packages/core/src/modules/ledger/services/IndyLedgerService.ts b/packages/core/src/modules/ledger/services/IndyLedgerService.ts deleted file mode 100644 index df5e535d6d..0000000000 --- a/packages/core/src/modules/ledger/services/IndyLedgerService.ts +++ /dev/null @@ -1,503 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { IndyPoolConfig } from '../IndyPool' -import type { CredDef, default as Indy, NymRole, Schema } from 'indy-sdk' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { IndySdkError } from '../../../error/IndySdkError' -import { Logger } from '../../../logger' -import { injectable, inject } from '../../../plugins' -import { - didFromCredentialDefinitionId, - didFromRevocationRegistryDefinitionId, - didFromSchemaId, -} from '../../../utils/did' -import { isIndyError } from '../../../utils/indyError' -import { IndyIssuerService } from '../../indy/services/IndyIssuerService' - -import { IndyPoolService } from './IndyPoolService' - -@injectable() -export class IndyLedgerService { - private indy: typeof Indy - private logger: Logger - - private indyIssuer: IndyIssuerService - private indyPoolService: IndyPoolService - - public constructor( - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, - @inject(InjectionSymbols.Logger) logger: Logger, - indyIssuer: IndyIssuerService, - indyPoolService: IndyPoolService - ) { - this.indy = agentDependencies.indy - this.logger = logger - this.indyIssuer = indyIssuer - this.indyPoolService = indyPoolService - } - - public setPools(poolConfigs: IndyPoolConfig[]) { - return this.indyPoolService.setPools(poolConfigs) - } - - /** - * @deprecated - */ - public getDidIndyWriteNamespace(): string { - return this.indyPoolService.ledgerWritePool.config.indyNamespace - } - - public async connectToPools() { - return this.indyPoolService.connectToPools() - } - - /** - * @deprecated - */ - public async registerPublicDid( - agentContext: AgentContext, - submitterDid: string, - targetDid: string, - verkey: string, - alias: string, - role?: NymRole - ) { - const pool = this.indyPoolService.getPoolForNamespace() - - try { - this.logger.debug(`Register public did '${targetDid}' on ledger '${pool.id}'`) - - const request = await this.indy.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) - - const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) - - this.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.id}'`, { - response, - }) - - return targetDid - } catch (error) { - this.logger.error(`Error registering public did '${targetDid}' on ledger '${pool.id}'`, { - error, - submitterDid, - targetDid, - verkey, - alias, - role, - pool: pool.id, - }) - - throw error - } - } - - /** - * @deprecated - */ - public async getPublicDid(agentContext: AgentContext, did: string) { - // Getting the pool for a did also retrieves the DID. We can just use that - const { did: didResponse } = await this.indyPoolService.getPoolForDid(agentContext, did) - - return didResponse - } - - /** - * @deprecated - */ - public async setEndpointsForDid( - agentContext: AgentContext, - did: string, - endpoints: IndyEndpointAttrib - ): Promise { - const pool = this.indyPoolService.getPoolForNamespace() - - try { - this.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.id}'`, endpoints) - - const request = await this.indy.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) - - const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, did) - this.logger.debug(`Successfully set endpoints for did '${did}' on ledger '${pool.id}'`, { - response, - endpoints, - }) - } catch (error) { - this.logger.error(`Error setting endpoints for did '${did}' on ledger '${pool.id}'`, { - error, - did, - endpoints, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** - * @deprecated - */ - public async getEndpointsForDid(agentContext: AgentContext, did: string) { - const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) - - try { - this.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.id}'`) - - const request = await this.indy.buildGetAttribRequest(null, did, 'endpoint', null, null) - - this.logger.debug(`Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.id}'`) - const response = await this.indyPoolService.submitReadRequest(pool, request) - - if (!response.result.data) return {} - - const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib - this.logger.debug(`Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.id}'`, { - response, - endpoints, - }) - - return endpoints ?? {} - } catch (error) { - this.logger.error(`Error retrieving endpoints for did '${did}' from ledger '${pool.id}'`, { - error, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async registerSchema( - agentContext: AgentContext, - did: string, - schemaTemplate: SchemaTemplate - ): Promise { - const pool = this.indyPoolService.getPoolForNamespace() - - try { - this.logger.debug(`Register schema on ledger '${pool.id}' with did '${did}'`, schemaTemplate) - const { name, attributes, version } = schemaTemplate - const schema = await this.indyIssuer.createSchema(agentContext, { originDid: did, name, version, attributes }) - - const request = await this.indy.buildSchemaRequest(did, schema) - - const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, did) - this.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.id}'`, { - response, - schema, - }) - - schema.seqNo = response.result.txnMetadata.seqNo - - return schema - } catch (error) { - this.logger.error(`Error registering schema for did '${did}' on ledger '${pool.id}'`, { - error, - did, - schemaTemplate, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async getSchema(agentContext: AgentContext, schemaId: string) { - const did = didFromSchemaId(schemaId) - const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) - - try { - this.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.id}'`) - - const request = await this.indy.buildGetSchemaRequest(null, schemaId) - - this.logger.trace(`Submitting get schema request for schema '${schemaId}' to ledger '${pool.id}'`) - const response = await this.indyPoolService.submitReadRequest(pool, request) - - this.logger.trace(`Got un-parsed schema '${schemaId}' from ledger '${pool.id}'`, { - response, - }) - - const [, schema] = await this.indy.parseGetSchemaResponse(response) - this.logger.debug(`Got schema '${schemaId}' from ledger '${pool.id}'`, { - schema, - }) - - return schema - } catch (error) { - this.logger.error(`Error retrieving schema '${schemaId}' from ledger '${pool.id}'`, { - error, - schemaId, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async registerCredentialDefinition( - agentContext: AgentContext, - did: string, - credentialDefinitionTemplate: CredentialDefinitionTemplate - ): Promise { - const pool = this.indyPoolService.getPoolForNamespace() - - try { - this.logger.debug( - `Registering credential definition on ledger '${pool.id}' with did '${did}'`, - credentialDefinitionTemplate - ) - const { schema, tag, signatureType, supportRevocation } = credentialDefinitionTemplate - - const credentialDefinition = await this.indyIssuer.createCredentialDefinition(agentContext, { - issuerDid: did, - schema, - tag, - signatureType, - supportRevocation, - }) - - const request = await this.indy.buildCredDefRequest(did, credentialDefinition) - - const response = await this.indyPoolService.submitWriteRequest(agentContext, pool, request, did) - - this.logger.debug(`Registered credential definition '${credentialDefinition.id}' on ledger '${pool.id}'`, { - response, - credentialDefinition: credentialDefinition, - }) - - return credentialDefinition - } catch (error) { - this.logger.error( - `Error registering credential definition for schema '${credentialDefinitionTemplate.schema.id}' on ledger '${pool.id}'`, - { - error, - did, - credentialDefinitionTemplate, - } - ) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async getCredentialDefinition(agentContext: AgentContext, credentialDefinitionId: string) { - const did = didFromCredentialDefinitionId(credentialDefinitionId) - const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) - - this.logger.debug(`Using ledger '${pool.id}' to retrieve credential definition '${credentialDefinitionId}'`) - - try { - const request = await this.indy.buildGetCredDefRequest(null, credentialDefinitionId) - - this.logger.trace( - `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.id}'` - ) - - const response = await this.indyPoolService.submitReadRequest(pool, request) - this.logger.trace(`Got un-parsed credential definition '${credentialDefinitionId}' from ledger '${pool.id}'`, { - response, - }) - - const [, credentialDefinition] = await this.indy.parseGetCredDefResponse(response) - this.logger.debug(`Got credential definition '${credentialDefinitionId}' from ledger '${pool.id}'`, { - credentialDefinition, - }) - - return credentialDefinition - } catch (error) { - this.logger.error(`Error retrieving credential definition '${credentialDefinitionId}' from ledger '${pool.id}'`, { - error, - credentialDefinitionId, - pool: pool.id, - }) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async getRevocationRegistryDefinition( - agentContext: AgentContext, - revocationRegistryDefinitionId: string - ): Promise { - const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) - const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) - - this.logger.debug( - `Using ledger '${pool.id}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` - ) - try { - //TODO - implement a cache - this.logger.trace( - `Revocation Registry Definition '${revocationRegistryDefinitionId}' not cached, retrieving from ledger` - ) - - const request = await this.indy.buildGetRevocRegDefRequest(null, revocationRegistryDefinitionId) - - this.logger.trace( - `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` - ) - const response = await this.indyPoolService.submitReadRequest(pool, request) - this.logger.trace( - `Got un-parsed revocation registry definition '${revocationRegistryDefinitionId}' from ledger '${pool.id}'`, - { - response, - } - ) - - const [, revocationRegistryDefinition] = await this.indy.parseGetRevocRegDefResponse(response) - - this.logger.debug(`Got revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, { - revocationRegistryDefinition, - }) - - return { revocationRegistryDefinition, revocationRegistryDefinitionTxnTime: response.result.txnTime } - } catch (error) { - this.logger.error( - `Error retrieving revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, - { - error, - revocationRegistryDefinitionId: revocationRegistryDefinitionId, - pool: pool.id, - } - ) - throw error - } - } - - // Retrieves the accumulated state of a revocation registry by id given a revocation interval from & to (used primarily for proof creation) - public async getRevocationRegistryDelta( - agentContext: AgentContext, - revocationRegistryDefinitionId: string, - to: number = new Date().getTime(), - from = 0 - ): Promise { - //TODO - implement a cache - const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) - const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) - - this.logger.debug( - `Using ledger '${pool.id}' to retrieve revocation registry delta with revocation registry definition id: '${revocationRegistryDefinitionId}'`, - { - to, - from, - } - ) - - try { - const request = await this.indy.buildGetRevocRegDeltaRequest(null, revocationRegistryDefinitionId, from, to) - - this.logger.trace( - `Submitting get revocation registry delta request for revocation registry '${revocationRegistryDefinitionId}' to ledger` - ) - - const response = await this.indyPoolService.submitReadRequest(pool, request) - this.logger.trace( - `Got revocation registry delta unparsed-response '${revocationRegistryDefinitionId}' from ledger`, - { - response, - } - ) - - const [, revocationRegistryDelta, deltaTimestamp] = await this.indy.parseGetRevocRegDeltaResponse(response) - - this.logger.debug(`Got revocation registry delta '${revocationRegistryDefinitionId}' from ledger`, { - revocationRegistryDelta, - deltaTimestamp, - to, - from, - }) - - return { revocationRegistryDelta, deltaTimestamp } - } catch (error) { - this.logger.error( - `Error retrieving revocation registry delta '${revocationRegistryDefinitionId}' from ledger, potentially revocation interval ends before revocation registry creation?"`, - { - error, - revocationRegistryId: revocationRegistryDefinitionId, - pool: pool.id, - } - ) - throw error - } - } - - // Retrieves the accumulated state of a revocation registry by id given a timestamp (used primarily for verification) - public async getRevocationRegistry( - agentContext: AgentContext, - revocationRegistryDefinitionId: string, - timestamp: number - ): Promise { - //TODO - implement a cache - const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) - const { pool } = await this.indyPoolService.getPoolForDid(agentContext, did) - - this.logger.debug( - `Using ledger '${pool.id}' to retrieve revocation registry accumulated state with revocation registry definition id: '${revocationRegistryDefinitionId}'`, - { - timestamp, - } - ) - - try { - const request = await this.indy.buildGetRevocRegRequest(null, revocationRegistryDefinitionId, timestamp) - - this.logger.trace( - `Submitting get revocation registry request for revocation registry '${revocationRegistryDefinitionId}' to ledger` - ) - const response = await this.indyPoolService.submitReadRequest(pool, request) - this.logger.trace( - `Got un-parsed revocation registry '${revocationRegistryDefinitionId}' from ledger '${pool.id}'`, - { - response, - } - ) - - const [, revocationRegistry, ledgerTimestamp] = await this.indy.parseGetRevocRegResponse(response) - this.logger.debug(`Got revocation registry '${revocationRegistryDefinitionId}' from ledger`, { - ledgerTimestamp, - revocationRegistry, - }) - - return { revocationRegistry, ledgerTimestamp } - } catch (error) { - this.logger.error(`Error retrieving revocation registry '${revocationRegistryDefinitionId}' from ledger`, { - error, - revocationRegistryId: revocationRegistryDefinitionId, - pool: pool.id, - }) - throw error - } - } -} - -export interface SchemaTemplate { - name: string - version: string - attributes: string[] -} - -export interface CredentialDefinitionTemplate { - schema: Schema - tag: string - signatureType: 'CL' - supportRevocation: boolean -} - -export interface ParseRevocationRegistryDefinitionTemplate { - revocationRegistryDefinition: Indy.RevocRegDef - revocationRegistryDefinitionTxnTime: number -} - -export interface ParseRevocationRegistryDeltaTemplate { - revocationRegistryDelta: Indy.RevocRegDelta - deltaTimestamp: number -} - -export interface ParseRevocationRegistryTemplate { - revocationRegistry: Indy.RevocReg - ledgerTimestamp: number -} - -export interface IndyEndpointAttrib { - endpoint?: string - types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> - routingKeys?: string[] - [key: string]: unknown -} diff --git a/packages/core/src/modules/ledger/services/IndyPoolService.ts b/packages/core/src/modules/ledger/services/IndyPoolService.ts deleted file mode 100644 index 172d1febd1..0000000000 --- a/packages/core/src/modules/ledger/services/IndyPoolService.ts +++ /dev/null @@ -1,349 +0,0 @@ -import type { AgentContext } from '../../../agent' -import type { AcceptanceMechanisms, AuthorAgreement, IndyPoolConfig } from '../IndyPool' -import type { default as Indy, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' - -import { Subject } from 'rxjs' - -import { AgentDependencies } from '../../../agent/AgentDependencies' -import { InjectionSymbols } from '../../../constants' -import { IndySdkError } from '../../../error/IndySdkError' -import { Logger } from '../../../logger/Logger' -import { injectable, inject } from '../../../plugins' -import { FileSystem } from '../../../storage/FileSystem' -import { isSelfCertifiedDid } from '../../../utils/did' -import { isIndyError } from '../../../utils/indyError' -import { allSettled, onlyFulfilled, onlyRejected } from '../../../utils/promises' -import { assertIndyWallet } from '../../../wallet/util/assertIndyWallet' -import { CacheModuleConfig } from '../../cache' -import { IndyPool } from '../IndyPool' -import { LedgerError } from '../error/LedgerError' -import { LedgerNotConfiguredError } from '../error/LedgerNotConfiguredError' -import { LedgerNotFoundError } from '../error/LedgerNotFoundError' - -export interface CachedDidResponse { - nymResponse: Indy.GetNymResponse - poolId: string -} - -@injectable() -export class IndyPoolService { - public pools: IndyPool[] = [] - private logger: Logger - private indy: typeof Indy - private agentDependencies: AgentDependencies - private stop$: Subject - private fileSystem: FileSystem - - public constructor( - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, - @inject(InjectionSymbols.Logger) logger: Logger, - @inject(InjectionSymbols.Stop$) stop$: Subject, - @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem - ) { - this.logger = logger - this.indy = agentDependencies.indy - this.agentDependencies = agentDependencies - this.fileSystem = fileSystem - this.stop$ = stop$ - } - - public setPools(poolConfigs: IndyPoolConfig[]) { - this.pools = poolConfigs.map( - (poolConfig) => new IndyPool(poolConfig, this.agentDependencies, this.logger, this.stop$, this.fileSystem) - ) - } - - /** - * Create connections to all ledger pools - */ - public async connectToPools() { - const handleArray: number[] = [] - // Sequentially connect to pools so we don't use up too many resources connecting in parallel - for (const pool of this.pools) { - this.logger.debug(`Connecting to pool: ${pool.id}`) - const poolHandle = await pool.connect() - this.logger.debug(`Finished connection to pool: ${pool.id}`) - handleArray.push(poolHandle) - } - return handleArray - } - - /** - * @deprecated use instead getPoolForNamespace - * Get the pool used for writing to the ledger. For now we always use the first pool - * as the pool that writes to the ledger - */ - public get ledgerWritePool() { - if (this.pools.length === 0) { - throw new LedgerNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" - ) - } - - return this.pools[0] - } - - /** - * Get the most appropriate pool for the given did. The algorithm is based on the approach as described in this document: - * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit - */ - public async getPoolForDid( - agentContext: AgentContext, - did: string - ): Promise<{ pool: IndyPool; did: Indy.GetNymResponse }> { - const pools = this.pools - - if (pools.length === 0) { - throw new LedgerNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" - ) - } - - const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache - - const cachedNymResponse = await cache.get(agentContext, `IndySdkPoolService:${did}`) - const pool = this.pools.find((pool) => pool.id === cachedNymResponse?.poolId) - - // If we have the nym response with associated pool in the cache, we'll use that - if (cachedNymResponse && pool) { - this.logger.trace(`Found ledger id '${pool.id}' for did '${did}' in cache`) - return { did: cachedNymResponse.nymResponse, pool } - } - - const { successful, rejected } = await this.getSettledDidResponsesFromPools(did, pools) - - if (successful.length === 0) { - const allNotFound = rejected.every((e) => e.reason instanceof LedgerNotFoundError) - const rejectedOtherThanNotFound = rejected.filter((e) => !(e.reason instanceof LedgerNotFoundError)) - - // All ledgers returned response that the did was not found - if (allNotFound) { - throw new LedgerNotFoundError(`Did '${did}' not found on any of the ledgers (total ${this.pools.length}).`) - } - - // one or more of the ledgers returned an unknown error - throw new LedgerError( - `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers`, - { cause: rejectedOtherThanNotFound[0].reason } - ) - } - - // If there are self certified DIDs we always prefer it over non self certified DIDs - // We take the first self certifying DID as we take the order in the - // indyLedgers config as the order of preference of ledgers - let value = successful.find((response) => - isSelfCertifiedDid(response.value.did.did, response.value.did.verkey) - )?.value - - if (!value) { - // Split between production and nonProduction ledgers. If there is at least one - // successful response from a production ledger, only keep production ledgers - // otherwise we only keep the non production ledgers. - const production = successful.filter((s) => s.value.pool.config.isProduction) - const nonProduction = successful.filter((s) => !s.value.pool.config.isProduction) - const productionOrNonProduction = production.length >= 1 ? production : nonProduction - - // We take the first value as we take the order in the indyLedgers config as - // the order of preference of ledgers - value = productionOrNonProduction[0].value - } - - await cache.set(agentContext, `IndySdkPoolService:${did}`, { - nymResponse: value.did, - poolId: value.pool.id, - }) - return { pool: value.pool, did: value.did } - } - - private async getSettledDidResponsesFromPools(did: string, pools: IndyPool[]) { - this.logger.trace(`Retrieving did '${did}' from ${pools.length} ledgers`) - const didResponses = await allSettled(pools.map((pool) => this.getDidFromPool(did, pool))) - - const successful = onlyFulfilled(didResponses) - this.logger.trace(`Retrieved ${successful.length} responses from ledgers for did '${did}'`) - - const rejected = onlyRejected(didResponses) - - return { - rejected, - successful, - } - } - - /** - * Get the most appropriate pool for the given indyNamespace - */ - public getPoolForNamespace(indyNamespace?: string) { - if (this.pools.length === 0) { - throw new LedgerNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" - ) - } - - if (!indyNamespace) { - this.logger.warn('Not passing the indyNamespace is deprecated and will be removed in the future version.') - return this.pools[0] - } - - const pool = this.pools.find((pool) => pool.didIndyNamespace === indyNamespace) - - if (!pool) { - throw new LedgerNotFoundError(`No ledgers found for IndyNamespace '${indyNamespace}'.`) - } - - return pool - } - - public async submitWriteRequest( - agentContext: AgentContext, - pool: IndyPool, - request: LedgerRequest, - signDid: string - ): Promise { - try { - const requestWithTaa = await this.appendTaa(pool, request) - const signedRequestWithTaa = await this.signRequest(agentContext, signDid, requestWithTaa) - - const response = await pool.submitWriteRequest(signedRequestWithTaa) - - return response - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async submitReadRequest(pool: IndyPool, request: LedgerRequest): Promise { - try { - const response = await pool.submitReadRequest(request) - - return response - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async signRequest(agentContext: AgentContext, did: string, request: LedgerRequest): Promise { - assertIndyWallet(agentContext.wallet) - - try { - return this.indy.signRequest(agentContext.wallet.handle, did, request) - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async appendTaa(pool: IndyPool, request: Indy.LedgerRequest) { - try { - const authorAgreement = await this.getTransactionAuthorAgreement(pool) - const taa = pool.config.transactionAuthorAgreement - - // If ledger does not have TAA, we can just send request - if (authorAgreement == null) { - return request - } - // Ledger has taa but user has not specified which one to use - if (!taa) { - throw new LedgerError( - `Please, specify a transaction author agreement with version and acceptance mechanism. ${JSON.stringify( - authorAgreement - )}` - ) - } - - // Throw an error if the pool doesn't have the specified version and acceptance mechanism - if ( - authorAgreement.version !== taa.version || - !(taa.acceptanceMechanism in authorAgreement.acceptanceMechanisms.aml) - ) { - // Throw an error with a helpful message - const errMessage = `Unable to satisfy matching TAA with mechanism ${JSON.stringify( - taa.acceptanceMechanism - )} and version ${JSON.stringify(taa.version)} in pool.\n Found ${JSON.stringify( - Object.keys(authorAgreement.acceptanceMechanisms.aml) - )} and version ${authorAgreement.version} in pool.` - throw new LedgerError(errMessage) - } - - const requestWithTaa = await this.indy.appendTxnAuthorAgreementAcceptanceToRequest( - request, - authorAgreement.text, - taa.version, - authorAgreement.digest, - taa.acceptanceMechanism, - // Current time since epoch - // We can't use ratification_ts, as it must be greater than 1499906902 - Math.floor(new Date().getTime() / 1000) - ) - - return requestWithTaa - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async getTransactionAuthorAgreement(pool: IndyPool): Promise { - try { - // TODO Replace this condition with memoization - if (pool.authorAgreement !== undefined) { - return pool.authorAgreement - } - - const taaRequest = await this.indy.buildGetTxnAuthorAgreementRequest(null) - const taaResponse = await this.submitReadRequest(pool, taaRequest) - const acceptanceMechanismRequest = await this.indy.buildGetAcceptanceMechanismsRequest(null) - const acceptanceMechanismResponse = await this.submitReadRequest(pool, acceptanceMechanismRequest) - - // TAA can be null - if (taaResponse.result.data == null) { - pool.authorAgreement = null - return null - } - - // If TAA is not null, we can be sure AcceptanceMechanisms is also not null - const authorAgreement = taaResponse.result.data as AuthorAgreement - const acceptanceMechanisms = acceptanceMechanismResponse.result.data as AcceptanceMechanisms - pool.authorAgreement = { - ...authorAgreement, - acceptanceMechanisms, - } - return pool.authorAgreement - } catch (error) { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async getDidFromPool(did: string, pool: IndyPool): Promise { - try { - this.logger.trace(`Get public did '${did}' from ledger '${pool.id}'`) - const request = await this.indy.buildGetNymRequest(null, did) - - this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.id}'`) - const response = await pool.submitReadRequest(request) - - const result = await this.indy.parseGetNymResponse(response) - this.logger.trace(`Retrieved did '${did}' from ledger '${pool.id}'`, result) - - return { - did: result, - pool, - response, - } - } catch (error) { - this.logger.trace(`Error retrieving did '${did}' from ledger '${pool.id}'`, { - error, - did, - }) - if (isIndyError(error, 'LedgerNotFound')) { - throw new LedgerNotFoundError(`Did '${did}' not found on ledger ${pool.id}`) - } else { - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - } -} - -export interface PublicDidRequest { - did: Indy.GetNymResponse - pool: IndyPool - response: Indy.LedgerReadReplyResponse -} diff --git a/packages/core/src/modules/ledger/services/index.ts b/packages/core/src/modules/ledger/services/index.ts deleted file mode 100644 index e0399c9afe..0000000000 --- a/packages/core/src/modules/ledger/services/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './IndyLedgerService' -export * from './IndyPoolService' diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts index 70a08b4d55..4d89be6eaf 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts @@ -1,11 +1,7 @@ -import type { AgentContext } from '../../../agent' -import type { Wallet } from '../../../wallet/Wallet' - import { Subject } from 'rxjs' import { agentDependencies, - getAgentConfig, getAgentContext, getMockConnection, getMockOutOfBand, @@ -14,9 +10,7 @@ import { import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { KeyType, Key } from '../../../crypto' -import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { AriesFrameworkError } from '../../../error' -import { IndyWallet } from '../../../wallet/IndyWallet' import { DidExchangeState } from '../../connections/models' import { OutOfBandService } from '../OutOfBandService' import { OutOfBandEventTypes } from '../domain/OutOfBandEvents' @@ -31,24 +25,12 @@ const OutOfBandRepositoryMock = OutOfBandRepository as jest.Mock { - const agentConfig = getAgentConfig('OutOfBandServiceTest') - let wallet: Wallet let outOfBandRepository: OutOfBandRepository let outOfBandService: OutOfBandService let eventEmitter: EventEmitter - let agentContext: AgentContext - - beforeAll(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) - agentContext = getAgentContext() - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) - }) - - afterAll(async () => { - await wallet.delete() - }) beforeEach(async () => { eventEmitter = new EventEmitter(agentDependencies, new Subject()) diff --git a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts index d135c0e9ea..5807e13d70 100644 --- a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts @@ -5,15 +5,21 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions } from '../../../../tests/helpers' import { HandshakeProtocol, DidExchangeState } from '../../connections' -import { OutOfBandState } from '../domain/OutOfBandState' import { Agent } from '@aries-framework/core' -const faberAgentOptions = getAgentOptions('Faber Agent OOB Connect to Self', { - endpoints: ['rxjs:faber'], -}) +import { OutOfBandState } from '../domain/OutOfBandState' + +const faberAgentOptions = getAgentOptions( + 'Faber Agent OOB Connect to Self', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() +) describe('out of band', () => { let faberAgent: Agent diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index a3d3cf3b4d..b154e6bef3 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -7,14 +7,13 @@ import type { Constructor } from '../../utils/mixins' import { ProofsApi } from './ProofsApi' import { ProofsModuleConfig } from './ProofsModuleConfig' -import { IndyProofFormatService } from './formats/indy/IndyProofFormatService' -import { V1ProofProtocol, V2ProofProtocol } from './protocol' +import { V2ProofProtocol } from './protocol' import { ProofRepository } from './repository' /** * Default proofProtocols that will be registered if the `proofProtocols` property is not configured. */ -export type DefaultProofProtocols = [V1ProofProtocol, V2ProofProtocol] +export type DefaultProofProtocols = [V2ProofProtocol<[]>] // ProofsModuleOptions makes the proofProtocols property optional from the config, as it will set it when not provided. export type ProofsModuleOptions = Optional< @@ -32,26 +31,10 @@ export class ProofsModule } - /** - * Get the default proof protocols that will be registered if the `proofProtocols` property is not configured. - */ - private getDefaultProofProtocols(): DefaultProofProtocols { - // Instantiate proof formats - const indyProofFormat = new IndyProofFormatService() - - // Instantiate proof protocols - const v1ProofProtocol = new V1ProofProtocol({ indyProofFormat }) - const v2ProofProtocol = new V2ProofProtocol({ - proofFormats: [indyProofFormat], - }) - - return [v1ProofProtocol, v2ProofProtocol] - } - /** * Registers the dependencies of the proofs module on the dependency manager. */ diff --git a/packages/core/src/modules/proofs/ProofsModuleConfig.ts b/packages/core/src/modules/proofs/ProofsModuleConfig.ts index 3526e4eb7d..e87966ef27 100644 --- a/packages/core/src/modules/proofs/ProofsModuleConfig.ts +++ b/packages/core/src/modules/proofs/ProofsModuleConfig.ts @@ -18,11 +18,11 @@ export interface ProofsModuleConfigOptions { return this.options.autoAcceptProofs ?? AutoAcceptProof.Never } - /** See {@link CredentialsModuleConfigOptions.proofProtocols} */ + /** See {@link ProofsModuleConfigOptions.proofProtocols} */ public get proofProtocols() { return this.options.proofProtocols } diff --git a/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts index c2012ed566..1126aeda52 100644 --- a/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts +++ b/packages/core/src/modules/proofs/__tests__/ProofsModule.test.ts @@ -5,7 +5,6 @@ import { DependencyManager } from '../../../plugins/DependencyManager' import { ProofsApi } from '../ProofsApi' import { ProofsModule } from '../ProofsModule' import { ProofsModuleConfig } from '../ProofsModuleConfig' -import { V1ProofProtocol } from '../protocol/v1/V1ProofProtocol' import { V2ProofProtocol } from '../protocol/v2/V2ProofProtocol' import { ProofRepository } from '../repository' @@ -34,10 +33,10 @@ describe('ProofsModule', () => { expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(ProofRepository) }) - test('registers V1ProofProtocol and V2ProofProtocol if no proofProtocols are configured', () => { + test('registers V2ProofProtocol if no proofProtocols are configured', () => { const proofsModule = new ProofsModule() - expect(proofsModule.config.proofProtocols).toEqual([expect.any(V1ProofProtocol), expect.any(V2ProofProtocol)]) + expect(proofsModule.config.proofProtocols).toEqual([expect.any(V2ProofProtocol)]) }) test('calls register on the provided ProofProtocols', () => { diff --git a/packages/core/src/modules/proofs/__tests__/fixtures.ts b/packages/core/src/modules/proofs/__tests__/fixtures.ts deleted file mode 100644 index 2045f6f0f8..0000000000 --- a/packages/core/src/modules/proofs/__tests__/fixtures.ts +++ /dev/null @@ -1,47 +0,0 @@ -export const credDef = { - ver: '1.0', - id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:16:TAG', - schemaId: '16', - type: 'CL', - tag: 'TAG', - value: { - primary: { - n: '92498022445845202032348897620554299694896009176315493627722439892023558526259875239808280186111059586069456394012963552956574651629517633396592827947162983189649269173220440607665417484696688946624963596710652063849006738050417440697782608643095591808084344059908523401576738321329706597491345875134180790935098782801918369980296355919072827164363500681884641551147645504164254206270541724042784184712124576190438261715948768681331862924634233043594086219221089373455065715714369325926959533971768008691000560918594972006312159600845441063618991760512232714992293187779673708252226326233136573974603552763615191259713', - s: '10526250116244590830801226936689232818708299684432892622156345407187391699799320507237066062806731083222465421809988887959680863378202697458984451550048737847231343182195679453915452156726746705017249911605739136361885518044604626564286545453132948801604882107628140153824106426249153436206037648809856342458324897885659120708767794055147846459394129610878181859361616754832462886951623882371283575513182530118220334228417923423365966593298195040550255217053655606887026300020680355874881473255854564974899509540795154002250551880061649183753819902391970912501350100175974791776321455551753882483918632271326727061054', - r: [Object], - rctxt: - '46370806529776888197599056685386177334629311939451963919411093310852010284763705864375085256873240323432329015015526097014834809926159013231804170844321552080493355339505872140068998254185756917091385820365193200970156007391350745837300010513687490459142965515562285631984769068796922482977754955668569724352923519618227464510753980134744424528043503232724934196990461197793822566137436901258663918660818511283047475389958180983391173176526879694302021471636017119966755980327241734084462963412467297412455580500138233383229217300797768907396564522366006433982511590491966618857814545264741708965590546773466047139517', - z: '84153935869396527029518633753040092509512111365149323230260584738724940130382637900926220255597132853379358675015222072417404334537543844616589463419189203852221375511010886284448841979468767444910003114007224993233448170299654815710399828255375084265247114471334540928216537567325499206413940771681156686116516158907421215752364889506967984343660576422672840921988126699885304325384925457260272972771547695861942114712679509318179363715259460727275178310181122162544785290813713205047589943947592273130618286905125194410421355167030389500160371886870735704712739886223342214864760968555566496288314800410716250791012', - }, - }, -} - -export const TEST_INPUT_DESCRIPTORS_CITIZENSHIP = { - constraints: { - fields: [ - { - path: ['$.credentialSubject.familyName'], - purpose: 'The claim must be from one of the specified issuers', - id: '1f44d55f-f161-4938-a659-f8026467f126', - }, - { - path: ['$.credentialSubject.givenName'], - purpose: 'The claim must be from one of the specified issuers', - }, - ], - }, - schema: [ - { - uri: 'https://www.w3.org/2018/credentials#VerifiableCredential', - }, - { - uri: 'https://w3id.org/citizenship#PermanentResident', - }, - { - uri: 'https://w3id.org/citizenship/v1', - }, - ], - name: "EU Driver's License", - group: ['A'], - id: 'citizenship_input_1', -} diff --git a/packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts b/packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts deleted file mode 100644 index a81e4e5553..0000000000 --- a/packages/core/src/modules/proofs/formats/errors/InvalidEncodedValueError.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' - -export class InvalidEncodedValueError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts b/packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts deleted file mode 100644 index a00abc40cb..0000000000 --- a/packages/core/src/modules/proofs/formats/errors/MissingIndyProofMessageError.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' - -export class MissingIndyProofMessageError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/formats/index.ts b/packages/core/src/modules/proofs/formats/index.ts index 08ece3aa21..a28e77d623 100644 --- a/packages/core/src/modules/proofs/formats/index.ts +++ b/packages/core/src/modules/proofs/formats/index.ts @@ -2,8 +2,6 @@ export * from './ProofFormat' export * from './ProofFormatService' export * from './ProofFormatServiceOptions' -export * from './indy' - import * as ProofFormatServiceOptions from './ProofFormatServiceOptions' export { ProofFormatServiceOptions } diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts deleted file mode 100644 index a6afce160a..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormat.ts +++ /dev/null @@ -1,80 +0,0 @@ -import type { ProofAttributeInfoOptions, ProofPredicateInfoOptions } from './models' -import type { RequestedAttributeOptions } from './models/RequestedAttribute' -import type { RequestedPredicateOptions } from './models/RequestedPredicate' -import type { V1PresentationPreviewAttributeOptions, V1PresentationPreviewPredicateOptions } from '../../protocol/v1' -import type { ProofFormat } from '../ProofFormat' -import type { IndyProof, IndyProofRequest } from 'indy-sdk' - -/** - * Interface for creating an indy proof proposal. - */ -export interface IndyProposeProofFormat { - name?: string - version?: string - attributes?: V1PresentationPreviewAttributeOptions[] - predicates?: V1PresentationPreviewPredicateOptions[] -} - -/** - * Interface for creating an indy proof request. - */ -export interface IndyRequestProofFormat { - name: string - version: string - // TODO: update to AnonCredsNonRevokedInterval when moving to AnonCreds package - nonRevoked?: { from?: number; to?: number } - requestedAttributes?: Record - requestedPredicates?: Record -} - -/** - * Interface for accepting an indy proof request. - */ -export type IndyAcceptProofRequestFormat = Partial - -export interface IndySelectedCredentialsForProofRequest { - requestedAttributes: Record - requestedPredicates: Record - selfAttestedAttributes: Record -} - -/** - * Interface for getting credentials for an indy proof request. - */ -export interface IndyCredentialsForProofRequest { - attributes: Record - predicates: Record -} - -export interface IndyGetCredentialsForProofRequestOptions { - filterByNonRevocationRequirements?: boolean -} - -export interface IndyProofFormat extends ProofFormat { - formatKey: 'indy' - - proofFormats: { - createProposal: IndyProposeProofFormat - acceptProposal: { - name?: string - version?: string - } - createRequest: IndyRequestProofFormat - acceptRequest: IndyAcceptProofRequestFormat - - getCredentialsForRequest: { - input: IndyGetCredentialsForProofRequestOptions - output: IndyCredentialsForProofRequest - } - selectCredentialsForRequest: { - input: IndyGetCredentialsForProofRequestOptions - output: IndySelectedCredentialsForProofRequest - } - } - - formatData: { - proposal: IndyProofRequest - request: IndyProofRequest - presentation: IndyProof - } -} diff --git a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts b/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts deleted file mode 100644 index 924f9dcb62..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/IndyProofFormatService.ts +++ /dev/null @@ -1,584 +0,0 @@ -import type { - IndyCredentialsForProofRequest, - IndyGetCredentialsForProofRequestOptions, - IndyProofFormat, - IndySelectedCredentialsForProofRequest, -} from './IndyProofFormat' -import type { ProofAttributeInfo, ProofPredicateInfo } from './models' -import type { AgentContext } from '../../../../agent' -import type { ProofFormatService } from '../ProofFormatService' -import type { - ProofFormatCreateProposalOptions, - ProofFormatCreateReturn, - ProofFormatAcceptProposalOptions, - ProofFormatAcceptRequestOptions, - ProofFormatAutoRespondProposalOptions, - ProofFormatAutoRespondRequestOptions, - ProofFormatGetCredentialsForRequestOptions, - ProofFormatGetCredentialsForRequestReturn, - ProofFormatSelectCredentialsForRequestOptions, - ProofFormatSelectCredentialsForRequestReturn, - ProofFormatProcessOptions, - FormatCreateRequestOptions, - ProofFormatProcessPresentationOptions, -} from '../ProofFormatServiceOptions' -import type { CredDef, IndyProof, IndyProofRequest, Schema } from 'indy-sdk' - -import { Attachment, AttachmentData } from '../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { JsonEncoder } from '../../../../utils/JsonEncoder' -import { JsonTransformer } from '../../../../utils/JsonTransformer' -import { MessageValidator } from '../../../../utils/MessageValidator' -import { IndyCredential, IndyCredentialInfo } from '../../../credentials' -import { IndyCredentialUtils } from '../../../credentials/formats/indy/IndyCredentialUtils' -import { IndyVerifierService, IndyHolderService, IndyRevocationService } from '../../../indy' -import { IndyLedgerService } from '../../../ledger' -import { ProofFormatSpec } from '../../models/ProofFormatSpec' - -import { InvalidEncodedValueError } from './errors/InvalidEncodedValueError' -import { RequestedAttribute, RequestedPredicate } from './models' -import { PartialProof } from './models/PartialProof' -import { ProofRequest } from './models/ProofRequest' -import { RequestedCredentials } from './models/RequestedCredentials' -import { areIndyProofRequestsEqual, assertNoDuplicateGroupsNamesInProofRequest, createRequestFromPreview } from './util' -import { sortRequestedCredentials } from './util/sortRequestedCredentials' - -const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' -const V2_INDY_PRESENTATION_REQUEST = 'hlindy/proof-req@v2.0' -const V2_INDY_PRESENTATION = 'hlindy/proof@v2.0' - -export class IndyProofFormatService implements ProofFormatService { - public readonly formatKey = 'indy' as const - - public async createProposal( - agentContext: AgentContext, - { attachmentId, proofFormats }: ProofFormatCreateProposalOptions - ): Promise { - const format = new ProofFormatSpec({ - format: V2_INDY_PRESENTATION_PROPOSAL, - attachmentId, - }) - - const indyFormat = proofFormats.indy - if (!indyFormat) { - throw Error('Missing indy format to create proposal attachment format') - } - - const proofRequest = createRequestFromPreview({ - attributes: indyFormat.attributes ?? [], - predicates: indyFormat.predicates ?? [], - name: indyFormat.name ?? 'Proof request', - version: indyFormat.version ?? '1.0', - nonce: await agentContext.wallet.generateNonce(), - }) - const attachment = this.getFormatData(proofRequest.toJSON(), format.attachmentId) - - return { attachment, format } - } - - public async processProposal(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { - const proposalJson = attachment.getDataAsJson() - - // fromJSON also validates - const proposal = JsonTransformer.fromJSON(proposalJson, ProofRequest) - - // Assert attribute and predicate (group) names do not match - assertNoDuplicateGroupsNamesInProofRequest(proposal) - } - - public async acceptProposal( - agentContext: AgentContext, - { proposalAttachment, attachmentId }: ProofFormatAcceptProposalOptions - ): Promise { - const format = new ProofFormatSpec({ - format: V2_INDY_PRESENTATION_REQUEST, - attachmentId, - }) - - const proposalJson = proposalAttachment.getDataAsJson() - - // The proposal and request formats are the same, so we can just use the proposal - const request = JsonTransformer.fromJSON(proposalJson, ProofRequest) - - // We never want to reuse the nonce from the proposal, as this will allow replay attacks - request.nonce = await agentContext.wallet.generateNonce() - - const attachment = this.getFormatData(request.toJSON(), format.attachmentId) - - return { attachment, format } - } - - public async createRequest( - agentContext: AgentContext, - { attachmentId, proofFormats }: FormatCreateRequestOptions - ): Promise { - const format = new ProofFormatSpec({ - format: V2_INDY_PRESENTATION_REQUEST, - attachmentId, - }) - - const indyFormat = proofFormats.indy - if (!indyFormat) { - throw Error('Missing indy format in create request attachment format') - } - - const request = new ProofRequest({ - name: indyFormat.name, - version: indyFormat.version, - nonce: await agentContext.wallet.generateNonce(), - requestedAttributes: indyFormat.requestedAttributes, - requestedPredicates: indyFormat.requestedPredicates, - nonRevoked: indyFormat.nonRevoked, - }) - - // Validate to make sure user provided correct input - MessageValidator.validateSync(request) - assertNoDuplicateGroupsNamesInProofRequest(request) - - const attachment = this.getFormatData(request.toJSON(), format.attachmentId) - - return { attachment, format } - } - - public async processRequest(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { - const requestJson = attachment.getDataAsJson() - - // fromJSON also validates - const proofRequest = JsonTransformer.fromJSON(requestJson, ProofRequest) - - // Assert attribute and predicate (group) names do not match - assertNoDuplicateGroupsNamesInProofRequest(proofRequest) - } - - public async acceptRequest( - agentContext: AgentContext, - { proofFormats, requestAttachment, attachmentId }: ProofFormatAcceptRequestOptions - ): Promise { - const format = new ProofFormatSpec({ - format: V2_INDY_PRESENTATION, - attachmentId, - }) - - const indyFormat = proofFormats?.indy - - const requestJson = requestAttachment.getDataAsJson() - const proofRequest = JsonTransformer.fromJSON(requestJson, ProofRequest) - - let requestedCredentials: RequestedCredentials - - if (indyFormat) { - requestedCredentials = new RequestedCredentials({ - requestedAttributes: indyFormat.requestedAttributes, - requestedPredicates: indyFormat.requestedPredicates, - selfAttestedAttributes: indyFormat.selfAttestedAttributes, - }) - - // Validate to make sure user provided correct input - MessageValidator.validateSync(requestedCredentials) - } else { - const selectedCredentials = await this._selectCredentialsForRequest(agentContext, proofRequest, { - filterByNonRevocationRequirements: true, - }) - - requestedCredentials = new RequestedCredentials({ - requestedAttributes: selectedCredentials.requestedAttributes, - requestedPredicates: selectedCredentials.requestedPredicates, - selfAttestedAttributes: selectedCredentials.selfAttestedAttributes, - }) - } - - const proof = await this.createProof(agentContext, proofRequest, requestedCredentials) - const attachment = this.getFormatData(proof, format.attachmentId) - - return { - attachment, - format, - } - } - - public async processPresentation( - agentContext: AgentContext, - { requestAttachment, attachment }: ProofFormatProcessPresentationOptions - ): Promise { - const indyVerifierService = agentContext.dependencyManager.resolve(IndyVerifierService) - - const proofRequestJson = requestAttachment.getDataAsJson() - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - const proofJson = attachment.getDataAsJson() - const proof = JsonTransformer.fromJSON(proofJson, PartialProof) - - for (const [referent, attribute] of proof.requestedProof.revealedAttributes.entries()) { - if (!IndyCredentialUtils.checkValidEncoding(attribute.raw, attribute.encoded)) { - throw new InvalidEncodedValueError( - `The encoded value for '${referent}' is invalid. ` + - `Expected '${IndyCredentialUtils.encode(attribute.raw)}'. ` + - `Actual '${attribute.encoded}'` - ) - } - } - - // TODO: pre verify proof json - // I'm not 100% sure how much indy does. Also if it checks whether the proof requests matches the proof - // @see https://github.com/hyperledger/aries-cloudagent-python/blob/master/aries_cloudagent/indy/sdk/verifier.py#L79-L164 - - const schemas = await this.getSchemas(agentContext, new Set(proof.identifiers.map((i) => i.schemaId))) - const credentialDefinitions = await this.getCredentialDefinitions( - agentContext, - new Set(proof.identifiers.map((i) => i.credentialDefinitionId)) - ) - - return await indyVerifierService.verifyProof(agentContext, { - proofRequest: proofRequest.toJSON(), - proof: proofJson, - schemas, - credentialDefinitions, - }) - } - - public async getCredentialsForRequest( - agentContext: AgentContext, - { requestAttachment, proofFormats }: ProofFormatGetCredentialsForRequestOptions - ): Promise> { - const proofRequestJson = requestAttachment.getDataAsJson() - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - // Set default values - const { filterByNonRevocationRequirements = true } = proofFormats?.indy ?? {} - - const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, { - filterByNonRevocationRequirements, - }) - - return credentialsForRequest - } - - public async selectCredentialsForRequest( - agentContext: AgentContext, - { requestAttachment, proofFormats }: ProofFormatSelectCredentialsForRequestOptions - ): Promise> { - const proofRequestJson = requestAttachment.getDataAsJson() - const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) - - // Set default values - const { filterByNonRevocationRequirements = true } = proofFormats?.indy ?? {} - - const selectedCredentials = this._selectCredentialsForRequest(agentContext, proofRequest, { - filterByNonRevocationRequirements, - }) - - return selectedCredentials - } - - public async shouldAutoRespondToProposal( - agentContext: AgentContext, - { proposalAttachment, requestAttachment }: ProofFormatAutoRespondProposalOptions - ): Promise { - const proposalJson = proposalAttachment.getDataAsJson() - const requestJson = requestAttachment.getDataAsJson() - - const areRequestsEqual = areIndyProofRequestsEqual(proposalJson, requestJson) - agentContext.config.logger.debug(`Indy request and proposal are are equal: ${areRequestsEqual}`, { - proposalJson, - requestJson, - }) - - return areRequestsEqual - } - - public async shouldAutoRespondToRequest( - agentContext: AgentContext, - { proposalAttachment, requestAttachment }: ProofFormatAutoRespondRequestOptions - ): Promise { - const proposalJson = proposalAttachment.getDataAsJson() - const requestJson = requestAttachment.getDataAsJson() - - return areIndyProofRequestsEqual(proposalJson, requestJson) - } - - public async shouldAutoRespondToPresentation(): Promise { - // The presentation is already verified in processPresentation, so we can just return true here. - // It's only an ack, so it's just that we received the presentation. - return true - } - - public supportsFormat(formatIdentifier: string): boolean { - const supportedFormats = [V2_INDY_PRESENTATION_PROPOSAL, V2_INDY_PRESENTATION_REQUEST, V2_INDY_PRESENTATION] - return supportedFormats.includes(formatIdentifier) - } - - private async _getCredentialsForRequest( - agentContext: AgentContext, - proofRequest: ProofRequest, - options: IndyGetCredentialsForProofRequestOptions - ): Promise { - const credentialsForProofRequest: IndyCredentialsForProofRequest = { - attributes: {}, - predicates: {}, - } - - const proofRequestJson = proofRequest.toJSON() - - for (const [referent, requestedAttribute] of proofRequest.requestedAttributes.entries()) { - const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequestJson, referent) - - credentialsForProofRequest.attributes[referent] = sortRequestedCredentials( - await Promise.all( - credentials.map(async (credential: IndyCredential) => { - const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { - proofRequest, - requestedItem: requestedAttribute, - credential, - }) - - return new RequestedAttribute({ - credentialId: credential.credentialInfo.referent, - revealed: true, - credentialInfo: credential.credentialInfo, - timestamp: deltaTimestamp, - revoked, - }) - }) - ) - ) - - // We only attach revoked state if non-revocation is requested. So if revoked is true it means - // the credential is not applicable to the proof request - if (options.filterByNonRevocationRequirements) { - credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter( - (r) => !r.revoked - ) - } - } - - for (const [referent, requestedPredicate] of proofRequest.requestedPredicates.entries()) { - const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequestJson, referent) - - credentialsForProofRequest.predicates[referent] = sortRequestedCredentials( - await Promise.all( - credentials.map(async (credential) => { - const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem(agentContext, { - proofRequest, - requestedItem: requestedPredicate, - credential, - }) - - return new RequestedPredicate({ - credentialId: credential.credentialInfo.referent, - credentialInfo: credential.credentialInfo, - timestamp: deltaTimestamp, - revoked, - }) - }) - ) - ) - - // We only attach revoked state if non-revocation is requested. So if revoked is true it means - // the credential is not applicable to the proof request - if (options.filterByNonRevocationRequirements) { - credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter( - (r) => !r.revoked - ) - } - } - - return credentialsForProofRequest - } - - private async _selectCredentialsForRequest( - agentContext: AgentContext, - proofRequest: ProofRequest, - options: IndyGetCredentialsForProofRequestOptions - ): Promise { - const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, options) - - const selectedCredentials: IndySelectedCredentialsForProofRequest = { - requestedAttributes: {}, - requestedPredicates: {}, - selfAttestedAttributes: {}, - } - - Object.keys(credentialsForRequest.attributes).forEach((attributeName) => { - const attributeArray = credentialsForRequest.attributes[attributeName] - - if (attributeArray.length === 0) { - throw new AriesFrameworkError('Unable to automatically select requested attributes.') - } - - selectedCredentials.requestedAttributes[attributeName] = attributeArray[0] - }) - - Object.keys(credentialsForRequest.predicates).forEach((attributeName) => { - if (credentialsForRequest.predicates[attributeName].length === 0) { - throw new AriesFrameworkError('Unable to automatically select requested predicates.') - } else { - selectedCredentials.requestedPredicates[attributeName] = credentialsForRequest.predicates[attributeName][0] - } - }) - - return selectedCredentials - } - - private async getCredentialsForProofRequestReferent( - agentContext: AgentContext, - // pass as json to prevent having to transform to json on every call - proofRequestJson: IndyProofRequest, - attributeReferent: string - ): Promise { - const holderService = agentContext.dependencyManager.resolve(IndyHolderService) - - const credentialsJson = await holderService.getCredentialsForProofRequest(agentContext, { - proofRequest: proofRequestJson, - attributeReferent, - }) - - return JsonTransformer.fromJSON(credentialsJson, IndyCredential) as unknown as IndyCredential[] - } - - /** - * Build schemas object needed to create and verify proof objects. - * - * Creates object with `{ schemaId: Schema }` mapping - * - * @param schemaIds List of schema ids - * @returns Object containing schemas for specified schema ids - * - */ - private async getSchemas(agentContext: AgentContext, schemaIds: Set) { - const ledgerService = agentContext.dependencyManager.resolve(IndyLedgerService) - - const schemas: { [key: string]: Schema } = {} - - for (const schemaId of schemaIds) { - const schema = await ledgerService.getSchema(agentContext, schemaId) - schemas[schemaId] = schema - } - - return schemas - } - - /** - * Build credential definitions object needed to create and verify proof objects. - * - * Creates object with `{ credentialDefinitionId: CredentialDefinition }` mapping - * - * @param credentialDefinitionIds List of credential definition ids - * @returns Object containing credential definitions for specified credential definition ids - * - */ - private async getCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { - const ledgerService = agentContext.dependencyManager.resolve(IndyLedgerService) - - const credentialDefinitions: { [key: string]: CredDef } = {} - - for (const credDefId of credentialDefinitionIds) { - const credDef = await ledgerService.getCredentialDefinition(agentContext, credDefId) - credentialDefinitions[credDefId] = credDef - } - - return credentialDefinitions - } - - /** - * Create indy proof from a given proof request and requested credential object. - * - * @param proofRequest The proof request to create the proof for - * @param requestedCredentials The requested credentials object specifying which credentials to use for the proof - * @returns indy proof object - */ - private async createProof( - agentContext: AgentContext, - proofRequest: ProofRequest, - requestedCredentials: RequestedCredentials - ): Promise { - const indyHolderService = agentContext.dependencyManager.resolve(IndyHolderService) - - const credentialObjects = await Promise.all( - [ - ...Object.values(requestedCredentials.requestedAttributes), - ...Object.values(requestedCredentials.requestedPredicates), - ].map(async (c) => { - if (c.credentialInfo) { - return c.credentialInfo - } - const credentialInfo = await indyHolderService.getCredential(agentContext, c.credentialId) - return JsonTransformer.fromJSON(credentialInfo, IndyCredentialInfo) - }) - ) - - const schemas = await this.getSchemas(agentContext, new Set(credentialObjects.map((c) => c.schemaId))) - const credentialDefinitions = await this.getCredentialDefinitions( - agentContext, - new Set(credentialObjects.map((c) => c.credentialDefinitionId)) - ) - - return await indyHolderService.createProof(agentContext, { - proofRequest: proofRequest.toJSON(), - requestedCredentials: requestedCredentials, - schemas, - credentialDefinitions, - }) - } - - private async getRevocationStatusForRequestedItem( - agentContext: AgentContext, - { - proofRequest, - requestedItem, - credential, - }: { - proofRequest: ProofRequest - requestedItem: ProofAttributeInfo | ProofPredicateInfo - credential: IndyCredential - } - ) { - const indyRevocationService = agentContext.dependencyManager.resolve(IndyRevocationService) - - const requestNonRevoked = requestedItem.nonRevoked ?? proofRequest.nonRevoked - const credentialRevocationId = credential.credentialInfo.credentialRevocationId - const revocationRegistryId = credential.credentialInfo.revocationRegistryId - - // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display - if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { - agentContext.config.logger.trace( - `Presentation is requesting proof of non revocation, getting revocation status for credential`, - { - requestNonRevoked, - credentialRevocationId, - revocationRegistryId, - } - ) - - // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals - const status = await indyRevocationService.getRevocationStatus( - agentContext, - credentialRevocationId, - revocationRegistryId, - requestNonRevoked - ) - - return status - } - - return { revoked: undefined, deltaTimestamp: undefined } - } - - /** - * Returns an object of type {@link Attachment} for use in credential exchange messages. - * It looks up the correct format identifier and encodes the data as a base64 attachment. - * - * @param data The data to include in the attach object - * @param id the attach id from the formats component of the message - */ - private getFormatData(data: unknown, id: string): Attachment { - const attachment = new Attachment({ - id, - mimeType: 'application/json', - data: new AttachmentData({ - base64: JsonEncoder.toBase64(data), - }), - }) - - return attachment - } -} diff --git a/packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts b/packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts deleted file mode 100644 index 3d62914aca..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/__tests__/groupKeys.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { GetProofFormatDataReturn } from '../../../protocol/ProofProtocolOptions' -import type { IndyProofFormat } from '../IndyProofFormat' - -import { AriesFrameworkError } from '../../../../../error' - -export function getGroupKeysFromIndyProofFormatData(formatData: GetProofFormatDataReturn<[IndyProofFormat]>): { - proposeKey1: string - proposeKey2: string - requestKey1: string - requestKey2: string -} { - const proofRequest = formatData.request?.indy - const proofProposal = formatData.proposal?.indy - if (!proofProposal) { - throw new AriesFrameworkError('missing indy proof proposal') - } - if (!proofRequest) { - throw new AriesFrameworkError('missing indy proof request') - } - const proposeAttributes = proofProposal.requested_attributes - const proposePredicates = proofProposal.requested_predicates - const requestAttributes = proofRequest.requested_attributes - const requestPredicates = proofRequest.requested_predicates - - const proposeKey1 = Object.keys(proposeAttributes)[1] - const proposeKey2 = Object.keys(proposePredicates)[0] - const requestKey1 = Object.keys(requestAttributes)[1] - const requestKey2 = Object.keys(requestPredicates)[0] - - return { proposeKey1, proposeKey2, requestKey1, requestKey2 } -} diff --git a/packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts b/packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts deleted file mode 100644 index db6435670c..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/__tests__/util.test.ts +++ /dev/null @@ -1,541 +0,0 @@ -import type { default as Indy } from 'indy-sdk' - -import { AriesFrameworkError } from '../../../../../error' -import { AttributeFilter, PredicateType, ProofAttributeInfo, ProofPredicateInfo, ProofRequest } from '../models' -import { areIndyProofRequestsEqual, assertNoDuplicateGroupsNamesInProofRequest } from '../util' - -const proofRequest = { - name: 'Proof Request', - version: '1.0.0', - nonce: 'nonce', - ver: '1.0', - non_revoked: {}, - requested_attributes: { - a: { - names: ['name1', 'name2'], - restrictions: [ - { - cred_def_id: 'cred_def_id1', - }, - { - schema_id: 'schema_id', - }, - ], - }, - }, - requested_predicates: { - p: { - name: 'Hello', - p_type: '<', - p_value: 10, - restrictions: [ - { - cred_def_id: 'string2', - }, - { - cred_def_id: 'string', - }, - ], - }, - }, -} satisfies Indy.IndyProofRequest - -const credDefId = '9vPXgSpQJPkJEALbLXueBp:3:CL:57753:tag1' -const nonce = 'testtesttest12345' - -describe('IndyProofFormat | util', () => { - describe('assertNoDuplicateGroupsNamesInProofRequest', () => { - test('attribute names match', () => { - const attributes = { - age1: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - age2: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const proofRequest = new ProofRequest({ - name: 'proof-request', - version: '1.0', - nonce, - requestedAttributes: attributes, - }) - - expect(() => assertNoDuplicateGroupsNamesInProofRequest(proofRequest)).not.toThrow() - }) - - test('attribute names match with predicates name', () => { - const attributes = { - attrib: new ProofAttributeInfo({ - name: 'age', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - predicate: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const proofRequest = new ProofRequest({ - name: 'proof-request', - version: '1.0', - nonce, - requestedAttributes: attributes, - requestedPredicates: predicates, - }) - - expect(() => assertNoDuplicateGroupsNamesInProofRequest(proofRequest)).toThrowError(AriesFrameworkError) - }) - }) - describe('areIndyProofRequestsEqual', () => { - test('does not compare name, ver, version and nonce', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - name: 'Proof Request 2', - version: '2.0.0', - nonce: 'nonce2', - ver: '2.0', - }) - ).toBe(true) - }) - - test('check top level non_revocation interval', () => { - // empty object is semantically equal to undefined - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - non_revoked: {}, - }) - ).toBe(true) - - // properties inside object are different - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - non_revoked: { - to: 5, - }, - }, - { - ...proofRequest, - non_revoked: { - from: 5, - }, - } - ) - ).toBe(false) - - // One has non_revoked, other doesn't - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - non_revoked: { - from: 5, - }, - }) - ).toBe(false) - }) - - test('ignores attribute group name differences', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - b: proofRequest.requested_attributes.a, - }, - }) - ).toBe(true) - }) - - test('ignores attribute restriction order', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - restrictions: [...proofRequest.requested_attributes.a.restrictions].reverse(), - }, - }, - }) - ).toBe(true) - }) - - test('ignores attribute restriction undefined vs empty array', () => { - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - restrictions: undefined, - }, - }, - }, - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - restrictions: [], - }, - }, - } - ) - ).toBe(true) - }) - - test('ignores attribute names order', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - names: ['name2', 'name1'], - }, - }, - }) - ).toBe(true) - }) - - test('checks attribute non_revocation interval', () => { - // empty object is semantically equal to undefined - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - non_revoked: {}, - }, - }, - }) - ).toBe(true) - - // properties inside object are different - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - non_revoked: { - to: 5, - }, - }, - }, - }, - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - non_revoked: { - from: 5, - }, - }, - }, - } - ) - ).toBe(false) - - // One has non_revoked, other doesn't - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - non_revoked: { - from: 5, - }, - }, - }, - }) - ).toBe(false) - }) - - test('checks attribute restriction differences', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - restrictions: [ - { - cred_def_id: 'cred_def_id1', - }, - { - cred_def_id: 'cred_def_id2', - }, - ], - }, - }, - }) - ).toBe(false) - }) - - test('checks attribute name differences', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - names: ['name3'], - }, - }, - }) - ).toBe(false) - - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - name: 'name3', - names: undefined, - }, - }, - }) - ).toBe(false) - }) - - test('allows names with one value to be same as name property', () => { - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - name: 'name1', - }, - }, - }, - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - names: ['name1'], - }, - }, - } - ) - ).toBe(true) - - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - name: 'name1', - }, - }, - }, - { - ...proofRequest, - requested_attributes: { - a: { - ...proofRequest.requested_attributes.a, - names: ['name2'], - }, - }, - } - ) - ).toBe(false) - }) - - test('ignores predicate group name differences', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_predicates: { - a: proofRequest.requested_predicates.p, - }, - }) - ).toBe(true) - }) - - test('ignores predicate restriction order', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - restrictions: [...proofRequest.requested_predicates.p.restrictions].reverse(), - }, - }, - }) - ).toBe(true) - }) - - test('ignores predicate restriction undefined vs empty array', () => { - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - restrictions: undefined, - }, - }, - }, - { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - restrictions: [], - }, - }, - } - ) - ).toBe(true) - }) - - test('checks predicate restriction differences', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_attributes: { - p: { - ...proofRequest.requested_predicates.p, - restrictions: [ - { - cred_def_id: 'cred_def_id1', - }, - { - cred_def_id: 'cred_def_id2', - }, - ], - }, - }, - }) - ).toBe(false) - }) - - test('checks predicate name differences', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - name: 'name3', - }, - }, - }) - ).toBe(false) - }) - - test('checks predicate non_revocation interval', () => { - // empty object is semantically equal to undefined - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - non_revoked: {}, - }, - }, - }) - ).toBe(true) - - // properties inside object are different - expect( - areIndyProofRequestsEqual( - { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - non_revoked: { - to: 5, - }, - }, - }, - }, - { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - non_revoked: { - from: 5, - }, - }, - }, - } - ) - ).toBe(false) - - // One has non_revoked, other doesn't - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - non_revoked: { - from: 5, - }, - }, - }, - }) - ).toBe(false) - }) - - test('checks predicate p_type and p_value', () => { - expect( - areIndyProofRequestsEqual(proofRequest, { - ...proofRequest, - requested_predicates: { - p: { - ...proofRequest.requested_predicates.p, - p_type: '<', - p_value: 134134, - }, - }, - }) - ).toBe(false) - }) - }) -}) diff --git a/packages/core/src/modules/proofs/formats/indy/errors/InvalidEncodedValueError.ts b/packages/core/src/modules/proofs/formats/indy/errors/InvalidEncodedValueError.ts deleted file mode 100644 index 84ac6e1385..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/errors/InvalidEncodedValueError.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' - -export class InvalidEncodedValueError extends AriesFrameworkError {} diff --git a/packages/core/src/modules/proofs/formats/indy/errors/index.ts b/packages/core/src/modules/proofs/formats/indy/errors/index.ts deleted file mode 100644 index 7b2373bb66..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/errors/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './InvalidEncodedValueError' diff --git a/packages/core/src/modules/proofs/formats/indy/index.ts b/packages/core/src/modules/proofs/formats/indy/index.ts deleted file mode 100644 index 185c2f8afc..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './IndyProofFormat' -export * from './IndyProofFormatService' diff --git a/packages/core/src/modules/proofs/formats/indy/models/AttributeFilter.ts b/packages/core/src/modules/proofs/formats/indy/models/AttributeFilter.ts deleted file mode 100644 index b2a804ab2d..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/AttributeFilter.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { Expose, Transform, TransformationType, Type } from 'class-transformer' -import { IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' - -import { credDefIdRegex, indyDidRegex, schemaIdRegex, schemaVersionRegex } from '../../../../../utils/regex' - -export class AttributeValue { - public constructor(options: AttributeValue) { - if (options) { - this.name = options.name - this.value = options.value - } - } - - @IsString() - public name!: string - - @IsString() - public value!: string -} - -export class AttributeFilter { - public constructor(options: AttributeFilter) { - if (options) { - this.schemaId = options.schemaId - this.schemaIssuerDid = options.schemaIssuerDid - this.schemaName = options.schemaName - this.schemaVersion = options.schemaVersion - this.issuerDid = options.issuerDid - this.credentialDefinitionId = options.credentialDefinitionId - this.attributeValue = options.attributeValue - } - } - - @Expose({ name: 'schema_id' }) - @IsOptional() - @IsString() - @Matches(schemaIdRegex) - public schemaId?: string - - @Expose({ name: 'schema_issuer_did' }) - @IsOptional() - @IsString() - @Matches(indyDidRegex) - public schemaIssuerDid?: string - - @Expose({ name: 'schema_name' }) - @IsOptional() - @IsString() - public schemaName?: string - - @Expose({ name: 'schema_version' }) - @IsOptional() - @IsString() - @Matches(schemaVersionRegex, { - message: 'Version must be X.X or X.X.X', - }) - public schemaVersion?: string - - @Expose({ name: 'issuer_did' }) - @IsOptional() - @IsString() - @Matches(indyDidRegex) - public issuerDid?: string - - @Expose({ name: 'cred_def_id' }) - @IsOptional() - @IsString() - @Matches(credDefIdRegex) - public credentialDefinitionId?: string - - @IsOptional() - @Type(() => AttributeValue) - @ValidateNested() - @IsInstance(AttributeValue) - public attributeValue?: AttributeValue -} - -/** - * Decorator that transforms attribute filter to corresponding class instances. - * Needed for transformation of attribute value filter. - * - * Transforms attribute value between these formats: - * - * JSON: - * ```json - * { - * "attr::test_prop::value": "test_value" - * } - * ``` - * - * Class: - * ```json - * { - * "attributeValue": { - * "name": "test_props", - * "value": "test_value" - * } - * } - * ``` - * - * @example - * class Example { - * AttributeFilterTransformer() - * public attributeFilter?: AttributeFilter; - * } - * - * @see https://github.com/hyperledger/aries-framework-dotnet/blob/a18bef91e5b9e4a1892818df7408e2383c642dfa/src/Hyperledger.Aries/Features/PresentProof/Models/AttributeFilterConverter.cs - */ -export function AttributeFilterTransformer() { - return Transform(({ value: attributeFilter, type: transformationType }) => { - switch (transformationType) { - case TransformationType.CLASS_TO_PLAIN: - if (attributeFilter.attributeValue) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - attributeFilter[`attr::${attributeFilter.attributeValue.name}::value`] = attributeFilter.attributeValue.value - delete attributeFilter.attributeValue - } - - return attributeFilter - - case TransformationType.PLAIN_TO_CLASS: - for (const [key, value] of Object.entries(attributeFilter)) { - const match = new RegExp('^attr::([^:]+)::(value)$').exec(key) - - if (match) { - const attributeValue = new AttributeValue({ - name: match[1], - value: value as string, - }) - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - delete attributeFilter[key] - attributeFilter.attributeValue = attributeValue - - return attributeFilter - } - } - return attributeFilter - default: - return attributeFilter - } - }) -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/PartialProof.ts b/packages/core/src/modules/proofs/formats/indy/models/PartialProof.ts deleted file mode 100644 index c33627c99a..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/PartialProof.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Expose, Type } from 'class-transformer' -import { IsInstance, ValidateNested } from 'class-validator' - -import { ProofIdentifier } from './ProofIdentifier' -import { RequestedProof } from './RequestedProof' - -export class PartialProof { - public constructor(options: PartialProof) { - if (options) { - this.identifiers = options.identifiers - } - } - - @Type(() => ProofIdentifier) - @ValidateNested({ each: true }) - @IsInstance(ProofIdentifier, { each: true }) - public identifiers!: ProofIdentifier[] - - @Expose({ name: 'requested_proof' }) - @Type(() => RequestedProof) - @ValidateNested() - @IsInstance(RequestedProof) - public requestedProof!: RequestedProof -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/PredicateType.ts b/packages/core/src/modules/proofs/formats/indy/models/PredicateType.ts deleted file mode 100644 index f5dda2fc14..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/PredicateType.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum PredicateType { - LessThan = '<', - LessThanOrEqualTo = '<=', - GreaterThan = '>', - GreaterThanOrEqualTo = '>=', -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofAttribute.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofAttribute.ts deleted file mode 100644 index f307f92da6..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofAttribute.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Expose } from 'class-transformer' -import { IsInt, IsString } from 'class-validator' - -export class ProofAttribute { - public constructor(options: ProofAttribute) { - if (options) { - this.subProofIndex = options.subProofIndex - this.raw = options.raw - this.encoded = options.encoded - } - } - - @Expose({ name: 'sub_proof_index' }) - @IsInt() - public subProofIndex!: number - - @IsString() - public raw!: string - - @IsString() - public encoded!: string -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts deleted file mode 100644 index a67c8425ae..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofAttributeInfo.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Expose, Type } from 'class-transformer' -import { ArrayNotEmpty, IsArray, IsInstance, IsOptional, IsString, ValidateIf, ValidateNested } from 'class-validator' - -import { IndyRevocationInterval } from '../../../../credentials' - -import { AttributeFilter } from './AttributeFilter' - -export type ProofAttributeInfoOptions = ProofAttributeInfo - -export class ProofAttributeInfo { - public constructor(options: ProofAttributeInfoOptions) { - if (options) { - this.name = options.name - this.names = options.names - this.nonRevoked = options.nonRevoked ? new IndyRevocationInterval(options.nonRevoked) : undefined - this.restrictions = options.restrictions?.map((r) => new AttributeFilter(r)) - } - } - - @IsString() - @ValidateIf((o: ProofAttributeInfo) => o.names === undefined) - public name?: string - - @IsArray() - @IsString({ each: true }) - @ValidateIf((o: ProofAttributeInfo) => o.name === undefined) - @ArrayNotEmpty() - public names?: string[] - - @Expose({ name: 'non_revoked' }) - @ValidateNested() - @IsInstance(IndyRevocationInterval) - @Type(() => IndyRevocationInterval) - @IsOptional() - public nonRevoked?: IndyRevocationInterval - - @ValidateNested({ each: true }) - @Type(() => AttributeFilter) - @IsOptional() - @IsInstance(AttributeFilter, { each: true }) - public restrictions?: AttributeFilter[] -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofIdentifier.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofIdentifier.ts deleted file mode 100644 index 241ac74aaa..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofIdentifier.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Expose } from 'class-transformer' -import { IsNumber, IsOptional, IsString, Matches } from 'class-validator' - -import { credDefIdRegex } from '../../../../../utils/regex' - -export class ProofIdentifier { - public constructor(options: ProofIdentifier) { - if (options) { - this.schemaId = options.schemaId - this.credentialDefinitionId = options.credentialDefinitionId - this.revocationRegistryId = options.revocationRegistryId - this.timestamp = options.timestamp - } - } - - @Expose({ name: 'schema_id' }) - @IsString() - public schemaId!: string - - @Expose({ name: 'cred_def_id' }) - @IsString() - @Matches(credDefIdRegex) - public credentialDefinitionId!: string - - @Expose({ name: 'rev_reg_id' }) - @IsOptional() - @IsString() - public revocationRegistryId?: string - - @IsOptional() - @IsNumber() - public timestamp?: number -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts deleted file mode 100644 index 48083fc54d..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofPredicateInfo.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Expose, Type } from 'class-transformer' -import { IsArray, IsEnum, IsInstance, IsInt, IsOptional, IsString, ValidateNested } from 'class-validator' - -import { IndyRevocationInterval } from '../../../../credentials' - -import { AttributeFilter } from './AttributeFilter' -import { PredicateType } from './PredicateType' - -export interface ProofPredicateInfoOptions { - name: string - // Also allow string value of the enum as input, to make it easier to use in the API - predicateType: PredicateType | `${PredicateType}` - predicateValue: number - nonRevoked?: IndyRevocationInterval - restrictions?: AttributeFilter[] -} - -export class ProofPredicateInfo { - public constructor(options: ProofPredicateInfoOptions) { - if (options) { - this.name = options.name - this.nonRevoked = options.nonRevoked ? new IndyRevocationInterval(options.nonRevoked) : undefined - this.restrictions = options.restrictions?.map((r) => new AttributeFilter(r)) - this.predicateType = options.predicateType as PredicateType - this.predicateValue = options.predicateValue - } - } - - @IsString() - public name!: string - - @Expose({ name: 'p_type' }) - @IsEnum(PredicateType) - public predicateType!: PredicateType - - @Expose({ name: 'p_value' }) - @IsInt() - public predicateValue!: number - - @Expose({ name: 'non_revoked' }) - @ValidateNested() - @Type(() => IndyRevocationInterval) - @IsOptional() - @IsInstance(IndyRevocationInterval) - public nonRevoked?: IndyRevocationInterval - - @ValidateNested({ each: true }) - @Type(() => AttributeFilter) - @IsOptional() - @IsInstance(AttributeFilter, { each: true }) - @IsArray() - public restrictions?: AttributeFilter[] -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts b/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts deleted file mode 100644 index b2d5cf83cc..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/ProofRequest.ts +++ /dev/null @@ -1,94 +0,0 @@ -import type { ProofAttributeInfoOptions } from './ProofAttributeInfo' -import type { ProofPredicateInfoOptions } from './ProofPredicateInfo' -import type { IndyProofRequest } from 'indy-sdk' - -import { Expose, Type } from 'class-transformer' -import { IsIn, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' - -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { IsMap } from '../../../../../utils/transformers' -import { IndyRevocationInterval } from '../../../../credentials' - -import { ProofAttributeInfo } from './ProofAttributeInfo' -import { ProofPredicateInfo } from './ProofPredicateInfo' - -export interface ProofRequestOptions { - name: string - version: string - nonce: string - nonRevoked?: IndyRevocationInterval - ver?: '1.0' | '2.0' - requestedAttributes?: Record - requestedPredicates?: Record -} - -/** - * Proof Request for Indy based proof format - * - * @see https://github.com/hyperledger/indy-sdk/blob/57dcdae74164d1c7aa06f2cccecaae121cefac25/libindy/src/api/anoncreds.rs#L1222-L1239 - */ -export class ProofRequest { - public constructor(options: ProofRequestOptions) { - if (options) { - this.name = options.name - this.version = options.version - this.nonce = options.nonce - - this.requestedAttributes = new Map( - Object.entries(options.requestedAttributes ?? {}).map(([key, attribute]) => [ - key, - new ProofAttributeInfo(attribute), - ]) - ) - - this.requestedPredicates = new Map( - Object.entries(options.requestedPredicates ?? {}).map(([key, predicate]) => [ - key, - new ProofPredicateInfo(predicate), - ]) - ) - - this.nonRevoked = options.nonRevoked ? new IndyRevocationInterval(options.nonRevoked) : undefined - this.ver = options.ver - } - } - - @IsString() - public name!: string - - @IsString() - public version!: string - - @IsString() - public nonce!: string - - @Expose({ name: 'requested_attributes' }) - @IsMap() - @ValidateNested({ each: true }) - @Type(() => ProofAttributeInfo) - @IsInstance(ProofAttributeInfo, { each: true }) - public requestedAttributes!: Map - - @Expose({ name: 'requested_predicates' }) - @IsMap() - @ValidateNested({ each: true }) - @Type(() => ProofPredicateInfo) - @IsInstance(ProofPredicateInfo, { each: true }) - public requestedPredicates!: Map - - @Expose({ name: 'non_revoked' }) - @ValidateNested() - @Type(() => IndyRevocationInterval) - @IsOptional() - @IsInstance(IndyRevocationInterval) - public nonRevoked?: IndyRevocationInterval - - @IsIn(['1.0', '2.0']) - @IsOptional() - public ver?: '1.0' | '2.0' - - public toJSON() { - // IndyProofRequest is indy-sdk json type - return JsonTransformer.toJSON(this) as unknown as IndyProofRequest - } -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts deleted file mode 100644 index 21a1e9a1c3..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedAttribute.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { IndyCredentialInfoOptions } from '../../../../credentials/formats/indy/models/IndyCredentialInfo' - -import { Exclude, Expose } from 'class-transformer' -import { IsBoolean, IsInt, IsOptional, IsString } from 'class-validator' - -import { IndyCredentialInfo } from '../../../../credentials/formats/indy/models/IndyCredentialInfo' - -export interface RequestedAttributeOptions { - credentialId: string - timestamp?: number - revealed: boolean - credentialInfo?: IndyCredentialInfoOptions - revoked?: boolean -} - -/** - * Requested Attribute for Indy proof creation - */ -export class RequestedAttribute { - public constructor(options: RequestedAttributeOptions) { - if (options) { - this.credentialId = options.credentialId - this.timestamp = options.timestamp - this.revealed = options.revealed - this.credentialInfo = options.credentialInfo ? new IndyCredentialInfo(options.credentialInfo) : undefined - this.revoked = options.revoked - } - } - - @Expose({ name: 'cred_id' }) - @IsString() - public credentialId!: string - - @Expose({ name: 'timestamp' }) - @IsInt() - @IsOptional() - public timestamp?: number - - @IsBoolean() - public revealed!: boolean - - @Exclude({ toPlainOnly: true }) - public credentialInfo?: IndyCredentialInfo - - @Exclude({ toPlainOnly: true }) - public revoked?: boolean -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts deleted file mode 100644 index f515a82dee..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedCredentials.ts +++ /dev/null @@ -1,81 +0,0 @@ -import type { RequestedAttributeOptions } from './RequestedAttribute' -import type { RequestedPredicateOptions } from './RequestedPredicate' -import type { IndyRequestedCredentials } from 'indy-sdk' - -import { Expose } from 'class-transformer' -import { ValidateNested } from 'class-validator' - -import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { RecordTransformer } from '../../../../../utils/transformers' - -import { RequestedAttribute } from './RequestedAttribute' -import { RequestedPredicate } from './RequestedPredicate' - -export interface IndyRequestedCredentialsOptions { - requestedAttributes?: Record - requestedPredicates?: Record - selfAttestedAttributes?: Record -} - -/** - * Requested Credentials for Indy proof creation - * - * @see https://github.com/hyperledger/indy-sdk/blob/57dcdae74164d1c7aa06f2cccecaae121cefac25/libindy/src/api/anoncreds.rs#L1433-L1445 - */ -export class RequestedCredentials { - public constructor(options: IndyRequestedCredentialsOptions = {}) { - if (options) { - const { requestedAttributes, requestedPredicates } = options - - // Create RequestedAttribute objects from options - this.requestedAttributes = {} - if (requestedAttributes) { - Object.keys(requestedAttributes).forEach((key) => { - this.requestedAttributes[key] = new RequestedAttribute(requestedAttributes[key]) - }) - } - - // Create RequestedPredicate objects from options - this.requestedPredicates = {} - if (requestedPredicates) { - Object.keys(requestedPredicates).forEach((key) => { - this.requestedPredicates[key] = new RequestedPredicate(requestedPredicates[key]) - }) - } - - this.selfAttestedAttributes = options.selfAttestedAttributes ?? {} - } - } - - @Expose({ name: 'requested_attributes' }) - @ValidateNested({ each: true }) - @RecordTransformer(RequestedAttribute) - public requestedAttributes!: Record - - @Expose({ name: 'requested_predicates' }) - @ValidateNested({ each: true }) - @RecordTransformer(RequestedPredicate) - public requestedPredicates!: Record - - @Expose({ name: 'self_attested_attributes' }) - public selfAttestedAttributes!: Record - - public toJSON() { - // IndyRequestedCredentials is indy-sdk json type - return JsonTransformer.toJSON(this) as unknown as IndyRequestedCredentials - } - - public getCredentialIdentifiers(): string[] { - const credIds = new Set() - - Object.values(this.requestedAttributes).forEach((attr) => { - credIds.add(attr.credentialId) - }) - - Object.values(this.requestedPredicates).forEach((pred) => { - credIds.add(pred.credentialId) - }) - - return Array.from(credIds) - } -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts deleted file mode 100644 index d8f5e2d9d2..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedPredicate.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { IndyCredentialInfoOptions } from '../../../../credentials' - -import { Exclude, Expose } from 'class-transformer' -import { IsInt, IsOptional, IsString } from 'class-validator' - -import { IndyCredentialInfo } from '../../../../credentials' - -export interface RequestedPredicateOptions { - credentialId: string - timestamp?: number - credentialInfo?: IndyCredentialInfoOptions - revoked?: boolean -} - -/** - * Requested Predicate for Indy proof creation - */ -export class RequestedPredicate { - public constructor(options: RequestedPredicateOptions) { - if (options) { - this.credentialId = options.credentialId - this.timestamp = options.timestamp - this.credentialInfo = options.credentialInfo ? new IndyCredentialInfo(options.credentialInfo) : undefined - this.revoked = options.revoked - } - } - - @Expose({ name: 'cred_id' }) - @IsString() - public credentialId!: string - - @Expose({ name: 'timestamp' }) - @IsInt() - @IsOptional() - public timestamp?: number - - @Exclude({ toPlainOnly: true }) - public credentialInfo?: IndyCredentialInfo - - @Exclude({ toPlainOnly: true }) - public revoked?: boolean -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/RequestedProof.ts b/packages/core/src/modules/proofs/formats/indy/models/RequestedProof.ts deleted file mode 100644 index a2f2a5cf85..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/RequestedProof.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Expose, Type } from 'class-transformer' -import { IsInstance, IsOptional, ValidateNested } from 'class-validator' - -import { ProofAttribute } from './ProofAttribute' - -export class RequestedProof { - public constructor(options: RequestedProof) { - if (options) { - this.revealedAttributes = options.revealedAttributes - this.selfAttestedAttributes = options.selfAttestedAttributes - } - } - - @Expose({ name: 'revealed_attrs' }) - @ValidateNested({ each: true }) - @Type(() => ProofAttribute) - @IsInstance(ProofAttribute, { each: true }) - public revealedAttributes!: Map - - @Expose({ name: 'self_attested_attrs' }) - @IsOptional() - // Validation is relaxed/skipped because empty Map validation will fail on JSON transform validation - public selfAttestedAttributes: Map = new Map() -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/RetrievedCredentials.ts b/packages/core/src/modules/proofs/formats/indy/models/RetrievedCredentials.ts deleted file mode 100644 index e529b24065..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/RetrievedCredentials.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { RequestedAttribute } from './RequestedAttribute' -import type { RequestedPredicate } from './RequestedPredicate' - -export interface RetrievedCredentialsOptions { - requestedAttributes?: Record - requestedPredicates?: Record -} - -/** - * Lists of requested credentials for Indy proof creation - */ -export class RetrievedCredentials { - public requestedAttributes: Record - public requestedPredicates: Record - - public constructor(options: RetrievedCredentialsOptions = {}) { - this.requestedAttributes = options.requestedAttributes ?? {} - this.requestedPredicates = options.requestedPredicates ?? {} - } -} diff --git a/packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts b/packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts deleted file mode 100644 index 9d52625ece..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/__tests__/ProofRequest.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { ClassValidationError } from '../../../../../../error/ClassValidationError' -import { JsonTransformer } from '../../../../../../utils/JsonTransformer' -import { MessageValidator } from '../../../../../../utils/MessageValidator' -import { ProofRequest } from '../ProofRequest' - -describe('ProofRequest', () => { - it('should successfully validate if the proof request JSON contains a valid structure', async () => { - const proofRequest = JsonTransformer.fromJSON( - { - name: 'ProofRequest', - version: '1.0', - nonce: '947121108704767252195123', - requested_attributes: { - First: { - name: 'Timo', - restrictions: [ - { - schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - }, - ], - }, - }, - requested_predicates: { - Second: { - name: 'Timo', - p_type: '<=', - p_value: 10, - restrictions: [ - { - schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - }, - ], - }, - }, - }, - ProofRequest - ) - - expect(() => MessageValidator.validateSync(proofRequest)).not.toThrow() - }) - - it('should throw an error if the proof request json contains an invalid structure', async () => { - const proofRequest = { - name: 'ProofRequest', - version: '1.0', - nonce: '947121108704767252195123', - requested_attributes: { - First: { - names: [], - restrictions: [ - { - schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - }, - ], - }, - }, - requested_predicates: [ - { - name: 'Timo', - p_type: '<=', - p_value: 10, - restrictions: [ - { - schema_id: 'q7ATwTYbQDgiigVijUAej:2:test:1.0', - }, - ], - }, - ], - } - - expect(() => JsonTransformer.fromJSON(proofRequest, ProofRequest)).toThrowError(ClassValidationError) - try { - JsonTransformer.fromJSON(proofRequest, ProofRequest) - } catch (e) { - const caughtError = e as ClassValidationError - expect(caughtError.validationErrors).toHaveLength(2) - } - }) -}) diff --git a/packages/core/src/modules/proofs/formats/indy/models/index.ts b/packages/core/src/modules/proofs/formats/indy/models/index.ts deleted file mode 100644 index 978b3ee89f..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/models/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export * from './AttributeFilter' -export * from './PredicateType' -export * from './ProofAttributeInfo' -export * from './ProofPredicateInfo' -export * from './ProofRequest' -export * from './RequestedAttribute' -export * from './RequestedCredentials' -export * from './RequestedPredicate' -export * from './RetrievedCredentials' diff --git a/packages/core/src/modules/proofs/formats/indy/util.ts b/packages/core/src/modules/proofs/formats/indy/util.ts deleted file mode 100644 index f1c3df2a16..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/util.ts +++ /dev/null @@ -1,266 +0,0 @@ -import type { V1PresentationPreviewAttributeOptions, V1PresentationPreviewPredicateOptions } from '../../protocol' -import type { default as Indy } from 'indy-sdk' - -import { AriesFrameworkError } from '../../../../error' -import { areObjectsEqual } from '../../../../utils' -import { uuid } from '../../../../utils/uuid' - -import { ProofAttributeInfo, ProofPredicateInfo, ProofRequest } from './models' - -export function createRequestFromPreview({ - name, - version, - nonce, - attributes, - predicates, -}: { - name: string - version: string - nonce: string - attributes: V1PresentationPreviewAttributeOptions[] - predicates: V1PresentationPreviewPredicateOptions[] -}): ProofRequest { - const proofRequest = new ProofRequest({ - name, - version, - nonce, - }) - - /** - * Create mapping of attributes by referent. This required the - * attributes to come from the same credential. - * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#referent - * - * { - * "referent1": [Attribute1, Attribute2], - * "referent2": [Attribute3] - * } - */ - const attributesByReferent: Record = {} - for (const proposedAttributes of attributes ?? []) { - if (!proposedAttributes.referent) proposedAttributes.referent = uuid() - - const referentAttributes = attributesByReferent[proposedAttributes.referent] - - // Referent key already exist, add to list - if (referentAttributes) { - referentAttributes.push(proposedAttributes) - } - - // Referent key does not exist yet, create new entry - else { - attributesByReferent[proposedAttributes.referent] = [proposedAttributes] - } - } - - // Transform attributes by referent to requested attributes - for (const [referent, proposedAttributes] of Object.entries(attributesByReferent)) { - // Either attributeName or attributeNames will be undefined - const attributeName = proposedAttributes.length === 1 ? proposedAttributes[0].name : undefined - const attributeNames = proposedAttributes.length > 1 ? proposedAttributes.map((a) => a.name) : undefined - - const requestedAttribute = new ProofAttributeInfo({ - name: attributeName, - names: attributeNames, - restrictions: [ - { - credentialDefinitionId: proposedAttributes[0].credentialDefinitionId, - }, - ], - }) - - proofRequest.requestedAttributes.set(referent, requestedAttribute) - } - - // Transform proposed predicates to requested predicates - for (const proposedPredicate of predicates ?? []) { - const requestedPredicate = new ProofPredicateInfo({ - name: proposedPredicate.name, - predicateType: proposedPredicate.predicate, - predicateValue: proposedPredicate.threshold, - restrictions: [ - { - credentialDefinitionId: proposedPredicate.credentialDefinitionId, - }, - ], - }) - - proofRequest.requestedPredicates.set(uuid(), requestedPredicate) - } - - return proofRequest -} - -/** - * Checks whether two `names` arrays are equal. The order of the names doesn't matter. - */ -function areNamesEqual({ - nameA, - namesA, - nameB, - namesB, -}: { - namesA?: string[] - nameA?: string - namesB?: string[] - nameB?: string -}) { - const namesACombined = nameA ? [nameA] : namesA - const namesBCombined = nameB ? [nameB] : namesB - - // Filter out case where both are not set (invalid) - if (!namesACombined || !namesBCombined) return false - - // Check if there are any duplicates - if (new Set(namesACombined).size !== namesACombined.length || new Set(namesBCombined).size !== namesBCombined.length) - return false - - // Check if the number of names is equal between A & B - if (namesACombined.length !== namesBCombined.length) return false - - return namesACombined.every((a) => namesBCombined.includes(a)) -} - -/** - * Checks whether two proof requests are semantically equal. The `name`, `version` and `nonce`, `ver` fields are ignored. - * In addition the group names don't have to be the same between the different requests. - */ -export function areIndyProofRequestsEqual(requestA: Indy.IndyProofRequest, requestB: Indy.IndyProofRequest): boolean { - // Check if the top-level non-revocation interval is equal - if (!isNonRevokedEqual(requestA.non_revoked, requestB.non_revoked)) return false - - const attributeAList = Object.values(requestA.requested_attributes) - const attributeBList = Object.values(requestB.requested_attributes) - - // Check if the number of attribute groups is equal in both requests - if (attributeAList.length !== attributeBList.length) return false - - const predicatesA = Object.values(requestA.requested_predicates) - const predicatesB = Object.values(requestB.requested_predicates) - - if (predicatesA.length !== predicatesB.length) return false - - // Check if all attribute groups in A are also in B - const attributesMatch = attributeAList.every((a) => { - // find an attribute in B that matches this attribute - const bIndex = attributeBList.findIndex((b) => { - return ( - // Check if name and names are equal - areNamesEqual({ - nameB: b.name, - namesB: b.names, - nameA: a.name, - namesA: a.names, - }) && - isNonRevokedEqual(a.non_revoked, b.non_revoked) && - areRestrictionsEqual(a.restrictions, b.restrictions) - ) - }) - - // Match found - if (bIndex !== -1) { - attributeBList.splice(bIndex, 1) - return true - } - - // Match not found - return false - }) - - if (!attributesMatch) return false - - const predicatesMatch = predicatesA.every((a) => { - // find a predicate in B that matches this predicate - const bIndex = predicatesB.findIndex((b) => { - return ( - a.name === b.name && - a.p_type === b.p_type && - a.p_value === b.p_value && - isNonRevokedEqual(a.non_revoked, b.non_revoked) && - areRestrictionsEqual(a.restrictions, b.restrictions) - ) - }) - - if (bIndex !== -1) { - predicatesB.splice(bIndex, 1) - return true - } - - return false - }) - - if (!predicatesMatch) return false - - return true -} - -/** - * Checks whether two non-revocation intervals are semantically equal. They are considered equal if: - * - Both are undefined - * - Both are empty objects - * - One if undefined and the other is an empty object - * - Both have the same from and to values - */ -function isNonRevokedEqual(nonRevokedA?: Indy.NonRevokedInterval, nonRevokedB?: Indy.NonRevokedInterval) { - // Having an empty non-revoked object is the same as not having one - if (nonRevokedA === undefined) - return nonRevokedB === undefined || (nonRevokedB.from === undefined && nonRevokedB.to === undefined) - if (nonRevokedB === undefined) return nonRevokedA.from === undefined && nonRevokedA.to === undefined - - return nonRevokedA.from === nonRevokedB.from && nonRevokedA.to === nonRevokedB.to -} - -/** - * Check if two restriction lists are equal. The order of the restrictions does not matter. - */ -function areRestrictionsEqual(restrictionsA?: Indy.WalletQuery[], restrictionsB?: Indy.WalletQuery[]) { - // Having an undefined restrictions property or an empty array is the same - if (restrictionsA === undefined) return restrictionsB === undefined || restrictionsB.length === 0 - if (restrictionsB === undefined) return restrictionsA.length === 0 - - // Clone array to not modify input object - const bList = [...restrictionsB] - - // Check if all restrictions in A are also in B - return restrictionsA.every((a) => { - const bIndex = restrictionsB.findIndex((b) => areObjectsEqual(a, b)) - - // Match found - if (bIndex !== -1) { - bList.splice(bIndex, 1) - return true - } - - // Match not found - return false - }) -} - -function attributeNamesToArray(proofRequest: ProofRequest) { - // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array - // containing all attribute names from the requested attributes. - return Array.from(proofRequest.requestedAttributes.values()).reduce( - (names, a) => [...names, ...(a.name ? [a.name] : a.names ? a.names : [])], - [] - ) -} - -function predicateNamesToArray(proofRequest: ProofRequest) { - return Array.from(new Set(Array.from(proofRequest.requestedPredicates.values()).map((a) => a.name))) -} - -function assertNoDuplicates(predicates: string[], attributeNames: string[]) { - const duplicates = predicates.filter((item) => attributeNames.indexOf(item) !== -1) - if (duplicates.length > 0) { - throw new AriesFrameworkError( - `The proof request contains duplicate predicates and attributes: ${duplicates.toString()}` - ) - } -} - -// TODO: This is still not ideal. The requested groups can specify different credentials using restrictions. -export function assertNoDuplicateGroupsNamesInProofRequest(proofRequest: ProofRequest) { - const attributes = attributeNamesToArray(proofRequest) - const predicates = predicateNamesToArray(proofRequest) - assertNoDuplicates(predicates, attributes) -} diff --git a/packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts b/packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts deleted file mode 100644 index 117fe2b898..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/util/__tests__/sortRequestedCredentials.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { RequestedAttribute } from '../../models' -import { sortRequestedCredentials } from '../sortRequestedCredentials' - -const credentials = [ - new RequestedAttribute({ - credentialId: '1', - revealed: true, - revoked: true, - }), - new RequestedAttribute({ - credentialId: '2', - revealed: true, - revoked: undefined, - }), - new RequestedAttribute({ - credentialId: '3', - revealed: true, - revoked: false, - }), - new RequestedAttribute({ - credentialId: '4', - revealed: true, - revoked: false, - }), - new RequestedAttribute({ - credentialId: '5', - revealed: true, - revoked: true, - }), - new RequestedAttribute({ - credentialId: '6', - revealed: true, - revoked: undefined, - }), -] - -describe('sortRequestedCredentials', () => { - test('sorts the credentials', () => { - expect(sortRequestedCredentials(credentials)).toEqual([ - credentials[1], - credentials[5], - credentials[2], - credentials[3], - credentials[0], - credentials[4], - ]) - }) -}) diff --git a/packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts b/packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts deleted file mode 100644 index 2db1deb0b9..0000000000 --- a/packages/core/src/modules/proofs/formats/indy/util/sortRequestedCredentials.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { RequestedAttribute, RequestedPredicate } from '../models' - -/** - * Sort requested attributes and predicates by `revoked` status. The order is: - * - first credentials with `revoked` set to undefined, this means no revocation status is needed for the credentials - * - then credentials with `revoked` set to false, this means the credentials are not revoked - * - then credentials with `revoked` set to true, this means the credentials are revoked - */ -export function sortRequestedCredentials | Array>( - credentials: Requested -) { - const staySame = 0 - const credentialGoUp = -1 - const credentialGoDown = 1 - - // Clone as sort is in place - const credentialsClone = [...credentials] - - return credentialsClone.sort((credential, compareTo) => { - // Nothing needs to happen if values are the same - if (credential.revoked === compareTo.revoked) return staySame - - // Undefined always is at the top - if (credential.revoked === undefined) return credentialGoUp - if (compareTo.revoked === undefined) return credentialGoDown - - // Then revoked - if (credential.revoked === false) return credentialGoUp - - // It means that compareTo is false and credential is true - return credentialGoDown - }) -} diff --git a/packages/core/src/modules/proofs/protocol/index.ts b/packages/core/src/modules/proofs/protocol/index.ts index db72d7287c..71799a5c45 100644 --- a/packages/core/src/modules/proofs/protocol/index.ts +++ b/packages/core/src/modules/proofs/protocol/index.ts @@ -1,6 +1,10 @@ -export * from './v1' export * from './v2' -export { ProofProtocol } from './ProofProtocol' import * as ProofProtocolOptions from './ProofProtocolOptions' +export { ProofProtocol } from './ProofProtocol' +// NOTE: ideally we don't export the BaseProofProtocol, but as the V1ProofProtocol is defined in the +// anoncreds package, we need to export it. We should at some point look at creating a core package which can be used for +// sharing internal types, and when you want to build you own modules, and an agent package, which is the one you use when +// consuming the framework +export { BaseProofProtocol } from './BaseProofProtocol' export { ProofProtocolOptions } diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts deleted file mode 100644 index f69048fece..0000000000 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/indy-proof-proposal.test.e2e.ts +++ /dev/null @@ -1,103 +0,0 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V1PresentationPreview } from '../models/V1PresentationPreview' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage' -import { ProofState } from '../../../models/ProofState' -import { V1ProposePresentationMessage } from '../messages' - -describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository - - beforeAll(async () => { - testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' - )) - }) - - afterAll(async () => { - testLogger.test('Shutting down both agents') - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - - test(`Alice Creates and sends Proof Proposal to Faber`, async () => { - testLogger.test('Alice sends proof proposal to Faber') - - const faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - - await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, - protocolVersion: 'v1', - proofFormats: { - indy: { - name: 'ProofRequest', - version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, - }, - }, - comment: 'V1 propose proof test', - }) - - testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V1ProposePresentationMessage, - }) - - expect(proposal).toMatchObject({ - type: 'https://didcomm.org/present-proof/1.0/propose-presentation', - id: expect.any(String), - comment: 'V1 propose proof test', - presentationProposal: { - type: 'https://didcomm.org/present-proof/1.0/presentation-preview', - attributes: [ - { - name: 'name', - credentialDefinitionId: presentationPreview.attributes[0].credentialDefinitionId, - value: 'John', - referent: '0', - }, - { - name: 'image_0', - credentialDefinitionId: presentationPreview.attributes[1].credentialDefinitionId, - }, - ], - predicates: [ - { - name: 'age', - credentialDefinitionId: presentationPreview.predicates[0].credentialDefinitionId, - predicate: '>=', - threshold: 50, - }, - ], - }, - }) - - expect(faberProofExchangeRecord).toMatchObject({ - id: expect.anything(), - threadId: faberProofExchangeRecord.threadId, - state: ProofState.ProposalReceived, - protocolVersion: 'v1', - }) - }) -}) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts deleted file mode 100644 index fcfaaaebf1..0000000000 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ /dev/null @@ -1,379 +0,0 @@ -import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import type { ProofStateChangedEvent } from '../../../ProofEvents' - -import { Subject, ReplaySubject } from 'rxjs' - -import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' -import { - setupProofsTest, - waitForProofExchangeRecordSubject, - getAgentOptions, - prepareForIssuance, - makeConnection, - issueCredential, -} from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { Agent } from '../../../../../agent/Agent' -import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' -import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' -import { uuid } from '../../../../../utils/uuid' -import { HandshakeProtocol } from '../../../../connections' -import { V1CredentialPreview } from '../../../../credentials' -import { MediatorPickupStrategy } from '../../../../routing' -import { ProofEventTypes } from '../../../ProofEvents' -import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' -import { AutoAcceptProof, ProofState } from '../../../models' - -describe('Present Proof', () => { - let agents: Agent[] - - afterEach(async () => { - for (const agent of agents) { - await agent.shutdown() - await agent.wallet.delete() - } - }) - - test('Faber starts with connection-less proof requests to Alice', async () => { - const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( - 'Faber connection-less Proofs', - 'Alice connection-less Proofs', - AutoAcceptProof.Never - ) - agents = [aliceAgent, faberAgent] - testLogger.test('Faber sends presentation request to Alice') - - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - let aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.RequestReceived, - }) - - // eslint-disable-next-line prefer-const - let { proofRecord: faberProofExchangeRecord, message } = await faberAgent.proofs.createRequest({ - protocolVersion: 'v1', - proofFormats: { - indy: { - name: 'test-proof-request', - version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, - }, - }, - }) - - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofExchangeRecord.id, - message, - domain: 'https://a-domain.com', - }) - await aliceAgent.receiveMessage(requestMessage.toJSON()) - - testLogger.test('Alice waits for presentation request from Faber') - let aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - - testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ - proofRecordId: aliceProofExchangeRecord.id, - }) - - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.PresentationReceived, - }) - - await aliceAgent.proofs.acceptRequest({ - proofRecordId: aliceProofExchangeRecord.id, - proofFormats: { indy: requestedCredentials.proofFormats.indy }, - }) - - testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise - - // assert presentation is valid - expect(faberProofExchangeRecord.isVerified).toBe(true) - - aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.Done, - }) - - // Faber accepts presentation - await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) - - // Alice waits till it receives presentation ack - aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - }) - - test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { - testLogger.test('Faber sends presentation request to Alice') - - const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( - 'Faber connection-less Proofs - Auto Accept', - 'Alice connection-less Proofs - Auto Accept', - AutoAcceptProof.Always - ) - - agents = [aliceAgent, faberAgent] - - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.Done, - }) - - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - state: ProofState.Done, - }) - - // eslint-disable-next-line prefer-const - let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ - protocolVersion: 'v1', - proofFormats: { - indy: { - name: 'test-proof-request', - version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, - }, - }, - autoAcceptProof: AutoAcceptProof.ContentApproved, - }) - - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofExchangeRecord.id, - message, - domain: 'https://a-domain.com', - }) - - await aliceAgent.receiveMessage(requestMessage.toJSON()) - - await aliceProofExchangeRecordPromise - - await faberProofExchangeRecordPromise - }) - - test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and both agents having a mediator', async () => { - testLogger.test('Faber sends presentation request to Alice') - - const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', - }) - - const unique = uuid().substring(0, 4) - - const mediatorAgentOptions = getAgentOptions(`Connectionless proofs with mediator Mediator-${unique}`, { - autoAcceptMediationRequests: true, - endpoints: ['rxjs:mediator'], - }) - - const faberMessages = new Subject() - const aliceMessages = new Subject() - const mediatorMessages = new Subject() - - const subjectMap = { - 'rxjs:mediator': mediatorMessages, - } - - // Initialize mediator - const mediatorAgent = new Agent(mediatorAgentOptions) - mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) - await mediatorAgent.initialize() - - const faberMediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ - label: 'faber invitation', - handshakeProtocols: [HandshakeProtocol.Connections], - }) - - const aliceMediationOutOfBandRecord = await mediatorAgent.oob.createInvitation({ - label: 'alice invitation', - handshakeProtocols: [HandshakeProtocol.Connections], - }) - - const faberAgentOptions = getAgentOptions(`Connectionless proofs with mediator Faber-${unique}`, { - autoAcceptProofs: AutoAcceptProof.Always, - mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', - }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }) - - const aliceAgentOptions = getAgentOptions(`Connectionless proofs with mediator Alice-${unique}`, { - autoAcceptProofs: AutoAcceptProof.Always, - mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', - }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }) - - const faberAgent = new Agent(faberAgentOptions) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - await faberAgent.initialize() - - const aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - await aliceAgent.initialize() - - agents = [aliceAgent, faberAgent, mediatorAgent] - - const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) - - const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) - expect(faberConnection.isReady).toBe(true) - expect(aliceConnection.isReady).toBe(true) - - await issueCredential({ - issuerAgent: faberAgent, - issuerConnectionId: faberConnection.id, - holderAgent: aliceAgent, - credentialTemplate: { - credentialDefinitionId: definition.id, - attributes: credentialPreview.attributes, - linkedAttachments: [ - new LinkedAttachment({ - name: 'image_0', - attachment: new Attachment({ - filename: 'picture-of-a-cat.png', - data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), - }), - }), - new LinkedAttachment({ - name: 'image_1', - attachment: new Attachment({ - filename: 'picture-of-a-dog.png', - data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), - }), - }), - ], - }, - }) - const faberReplay = new ReplaySubject() - const aliceReplay = new ReplaySubject() - - faberAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(faberReplay) - aliceAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(aliceReplay) - - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: definition.id, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: definition.id, - }), - ], - }), - } - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.Done, - }) - - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - state: ProofState.Done, - }) - - // eslint-disable-next-line prefer-const - let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ - protocolVersion: 'v1', - proofFormats: { - indy: { - name: 'test-proof-request', - version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, - }, - }, - autoAcceptProof: AutoAcceptProof.ContentApproved, - }) - - const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ - recordId: faberProofExchangeRecord.id, - message, - domain: 'https://a-domain.com', - }) - - const mediationRecord = await faberAgent.mediationRecipient.findDefaultMediator() - if (!mediationRecord) { - throw new Error('Faber agent has no default mediator') - } - - expect(requestMessage).toMatchObject({ - service: { - recipientKeys: [expect.any(String)], - routingKeys: mediationRecord.routingKeys, - serviceEndpoint: mediationRecord.endpoint, - }, - }) - - await aliceAgent.receiveMessage(requestMessage.toJSON()) - - await aliceProofExchangeRecordPromise - - await faberProofExchangeRecordPromise - - await aliceAgent.mediationRecipient.stopMessagePickup() - await faberAgent.mediationRecipient.stopMessagePickup() - }) -}) diff --git a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts deleted file mode 100644 index c8a116e8ed..0000000000 --- a/packages/core/src/modules/proofs/protocol/v1/__tests__/v1-proofs-auto-accept.e2e.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { V1PresentationPreview } from '../models' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' -import { AutoAcceptProof, ProofState } from '../../../models' - -describe('Auto accept present proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - - describe("Auto accept on 'always'", () => { - beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest( - 'Faber Auto Accept Always Proofs', - 'Alice Auto Accept Always Proofs', - AutoAcceptProof.Always - )) - }) - afterAll(async () => { - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - - test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'always'", async () => { - testLogger.test('Alice sends presentation proposal to Faber') - - await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, - protocolVersion: 'v1', - proofFormats: { - indy: { - name: 'abc', - version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, - }, - }, - }) - - testLogger.test('Faber waits for presentation from Alice') - testLogger.test('Alice waits till it receives presentation ack') - await Promise.all([ - waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), - waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), - ]) - }) - - test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'always'", async () => { - testLogger.test('Faber sends presentation request to Alice') - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - await faberAgent.proofs.requestProof({ - protocolVersion: 'v1', - connectionId: faberConnection.id, - proofFormats: { - indy: { - name: 'proof-request', - version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, - }, - }, - }) - - testLogger.test('Faber waits for presentation from Alice') - await Promise.all([ - waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), - waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), - ]) - }) - }) - - describe("Auto accept on 'contentApproved'", () => { - beforeAll(async () => { - testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest( - 'Faber Auto Accept Content Approved Proofs', - 'Alice Auto Accept Content Approved Proofs', - AutoAcceptProof.ContentApproved - )) - }) - afterAll(async () => { - testLogger.test('Shutting down both agents') - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - - test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'contentApproved'", async () => { - testLogger.test('Alice sends presentation proposal to Faber') - - const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, - protocolVersion: 'v1', - proofFormats: { - indy: { - name: 'abc', - version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, - }, - }, - }) - - testLogger.test('Faber waits for presentation proposal from Alice') - const faberProofExchangeRecord = await waitForProofExchangeRecord(faberAgent, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.ProposalReceived, - }) - - testLogger.test('Faber accepts presentation proposal from Alice') - await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofExchangeRecord.id }) - - await Promise.all([ - waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), - waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), - ]) - }) - - test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'contentApproved'", async () => { - testLogger.test('Faber sends presentation request to Alice') - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - await faberAgent.proofs.requestProof({ - protocolVersion: 'v1', - connectionId: faberConnection.id, - proofFormats: { - indy: { - name: 'proof-request', - version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, - }, - }, - }) - - testLogger.test('Alice waits for request from Faber') - const { id: proofRecordId } = await waitForProofExchangeRecord(aliceAgent, { - state: ProofState.RequestReceived, - }) - - const { proofFormats } = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId }) - await aliceAgent.proofs.acceptRequest({ proofRecordId, proofFormats }) - - await Promise.all([ - waitForProofExchangeRecord(aliceAgent, { state: ProofState.Done }), - waitForProofExchangeRecord(faberAgent, { state: ProofState.Done }), - ]) - }) - }) -}) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts index 397e0b8866..3c140bd867 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts @@ -1,4 +1,5 @@ import type { ProofStateChangedEvent } from '../../../ProofEvents' +import type { ProofFormatService } from '../../../formats' import type { CustomProofTags } from '../../../repository/ProofExchangeRecord' import { Subject } from 'rxjs' @@ -12,7 +13,6 @@ import { uuid } from '../../../../../utils/uuid' import { ConnectionService, DidExchangeState } from '../../../../connections' import { ProofEventTypes } from '../../../ProofEvents' import { PresentationProblemReportReason } from '../../../errors/PresentationProblemReportReason' -import { IndyProofFormatService } from '../../../formats/indy/IndyProofFormatService' import { ProofFormatSpec } from '../../../models/ProofFormatSpec' import { ProofState } from '../../../models/ProofState' import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' @@ -29,12 +29,14 @@ jest.mock('../../../../../storage/Repository') const ProofRepositoryMock = ProofRepository as jest.Mock const connectionServiceMock = ConnectionService as jest.Mock const didCommMessageRepositoryMock = DidCommMessageRepository as jest.Mock -const IndyProofFormatServiceMock = IndyProofFormatService as jest.Mock const proofRepository = new ProofRepositoryMock() const connectionService = new connectionServiceMock() const didCommMessageRepository = new didCommMessageRepositoryMock() -const indyProofFormatService = new IndyProofFormatServiceMock() +const proofFormatService = { + supportsFormat: () => true, + processRequest: jest.fn(), +} as unknown as ProofFormatService const agentConfig = getAgentConfig('V2ProofProtocolTest') const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) @@ -49,7 +51,7 @@ const agentContext = getAgentContext({ agentConfig, }) -const proofProtocol = new V2ProofProtocol({ proofFormats: [indyProofFormatService] }) +const proofProtocol = new V2ProofProtocol({ proofFormats: [proofFormatService] }) const connection = getMockConnection({ id: '123', diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index f924869f3f..0482ed4a86 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -1,31 +1,34 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' -import type { ProofStateChangedEvent } from '../../../ProofEvents' -import { Subject, ReplaySubject } from 'rxjs' +import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../../../tests/transport/SubjectOutboundTransport' +import { V1CredentialPreview } from '../../../../../../../anoncreds/src' +import { + getLegacyAnonCredsModules, + issueLegacyAnonCredsCredential, + prepareForAnonCredsIssuance, + setupAnonCredsTests, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import { - setupProofsTest, waitForProofExchangeRecordSubject, getAgentOptions, - prepareForIssuance, makeConnection, - issueCredential, -} from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' + testLogger, + setupEventReplaySubjects, +} from '../../../../../../tests' import { Agent } from '../../../../../agent/Agent' import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' import { uuid } from '../../../../../utils/uuid' import { HandshakeProtocol } from '../../../../connections' -import { V1CredentialPreview } from '../../../../credentials' +import { CredentialEventTypes } from '../../../../credentials' import { MediatorPickupStrategy } from '../../../../routing' import { ProofEventTypes } from '../../../ProofEvents' -import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' import { AutoAcceptProof, ProofState } from '../../../models' -describe('Present Proof', () => { +describe('V2 Connectionless Proofs - Indy', () => { let agents: Agent[] afterEach(async () => { @@ -36,42 +39,44 @@ describe('Present Proof', () => { }) test('Faber starts with connection-less proof requests to Alice', async () => { - const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( - 'Faber connection-less Proofs v2', - 'Alice connection-less Proofs v2', - AutoAcceptProof.Never - ) - agents = [aliceAgent, faberAgent] - testLogger.test('Faber sends presentation request to Alice') - - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } + const { + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber connection-less Proofs v2', + holderName: 'Alice connection-less Proofs v2', + autoAcceptProofs: AutoAcceptProof.Never, + attributeNames: ['name', 'age'], + }) - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, ], - }), - } - - let aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.RequestReceived, + }, }) + agents = [aliceAgent, faberAgent] + testLogger.test('Faber sends presentation request to Alice') + // eslint-disable-next-line prefer-const let { proofRecord: faberProofExchangeRecord, message } = await faberAgent.proofs.createRequest({ protocolVersion: 'v2', @@ -79,8 +84,28 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) @@ -94,84 +119,78 @@ describe('Present Proof', () => { await aliceAgent.receiveMessage(requestMessage.toJSON()) testLogger.test('Alice waits for presentation request from Faber') - let aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + let aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.RequestReceived, + }) testLogger.test('Alice accepts presentation request from Faber') - const requestedCredentials = await aliceAgent.proofs.selectCredentialsForRequest({ proofRecordId: aliceProofExchangeRecord.id, }) - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.PresentationReceived, - }) - await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise + faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) // assert presentation is valid expect(faberProofExchangeRecord.isVerified).toBe(true) - aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.Done, - }) - // Faber accepts presentation await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) // Alice waits till it receives presentation ack - aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.Done, + }) }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { testLogger.test('Faber sends presentation request to Alice') - const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( - 'Faber connection-less Proofs - Auto Accept', - 'Alice connection-less Proofs - Auto Accept', - AutoAcceptProof.Always - ) - - agents = [aliceAgent, faberAgent] - - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } + const { + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber connection-less Proofs v2 - Auto Accept', + holderName: 'Alice connection-less Proofs v2 - Auto Accept', + autoAcceptProofs: AutoAcceptProof.Always, + attributeNames: ['name', 'age'], + }) - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, ], - }), - } - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.Done, + }, }) - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - state: ProofState.Done, - }) + agents = [aliceAgent, faberAgent] // eslint-disable-next-line prefer-const let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ @@ -180,8 +199,28 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, autoAcceptProof: AutoAcceptProof.ContentApproved, @@ -194,9 +233,13 @@ describe('Present Proof', () => { }) await aliceAgent.receiveMessage(requestMessage.toJSON()) - await aliceProofExchangeRecordPromise + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + }) - await faberProofExchangeRecordPromise + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + }) }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and both agents having a mediator', async () => { @@ -209,18 +252,19 @@ describe('Present Proof', () => { const unique = uuid().substring(0, 4) - const mediatorOptions = getAgentOptions(`Connectionless proofs with mediator Mediator-${unique}`, { - autoAcceptMediationRequests: true, - endpoints: ['rxjs:mediator'], - }) + const mediatorOptions = getAgentOptions( + `Connectionless proofs with mediator Mediator-${unique}`, + { + autoAcceptMediationRequests: true, + endpoints: ['rxjs:mediator'], + }, + getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, + }) + ) - const faberMessages = new Subject() - const aliceMessages = new Subject() const mediatorMessages = new Subject() - - const subjectMap = { - 'rxjs:mediator': mediatorMessages, - } + const subjectMap = { 'rxjs:mediator': mediatorMessages } // Initialize mediator const mediatorAgent = new Agent(mediatorOptions) @@ -238,47 +282,62 @@ describe('Present Proof', () => { handshakeProtocols: [HandshakeProtocol.Connections], }) - const faberOptions = getAgentOptions(`Connectionless proofs with mediator Faber-${unique}`, { - autoAcceptProofs: AutoAcceptProof.Always, - mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', - }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }) + const faberOptions = getAgentOptions( + `Connectionless proofs with mediator Faber-${unique}`, + { + mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, + }) + ) - const aliceOptions = getAgentOptions(`Connectionless proofs with mediator Alice-${unique}`, { - autoAcceptProofs: AutoAcceptProof.Always, - mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', - }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }) + const aliceOptions = getAgentOptions( + `Connectionless proofs with mediator Alice-${unique}`, + { + mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, + }) + ) const faberAgent = new Agent(faberOptions) faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) await faberAgent.initialize() const aliceAgent = new Agent(aliceOptions) aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) await aliceAgent.initialize() + const [faberReplay, aliceReplay] = setupEventReplaySubjects( + [faberAgent, aliceAgent], + [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + ) agents = [aliceAgent, faberAgent, mediatorAgent] - const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { + attributeNames: ['name', 'age', 'image_0', 'image_1'], + issuerId: faberAgent.publicDid?.did as string, + }) - const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) - expect(faberConnection.isReady).toBe(true) - expect(aliceConnection.isReady).toBe(true) + const [faberConnection] = await makeConnection(faberAgent, aliceAgent) // issue credential with two linked attachments - await issueCredential({ + await issueLegacyAnonCredsCredential({ issuerAgent: faberAgent, - issuerConnectionId: faberConnection.id, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnection.id, holderAgent: aliceAgent, - credentialTemplate: { - credentialDefinitionId: definition.id, + holderReplay: aliceReplay, + offer: { + credentialDefinitionId: credentialDefinition.credentialDefinitionId, attributes: credentialPreview.attributes, linkedAttachments: [ new LinkedAttachment({ @@ -298,43 +357,6 @@ describe('Present Proof', () => { ], }, }) - const faberReplay = new ReplaySubject() - const aliceReplay = new ReplaySubject() - - faberAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(faberReplay) - aliceAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(aliceReplay) - - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: definition.id, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: definition.id, - }), - ], - }), - } - - const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.Done, - }) - - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - state: ProofState.Done, - }) // eslint-disable-next-line prefer-const let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ @@ -343,8 +365,28 @@ describe('Present Proof', () => { indy: { name: 'test-proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinition.credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinition.credentialDefinitionId, + }, + ], + }, + }, }, }, autoAcceptProof: AutoAcceptProof.ContentApproved, @@ -357,9 +399,7 @@ describe('Present Proof', () => { }) const mediationRecord = await faberAgent.mediationRecipient.findDefaultMediator() - if (!mediationRecord) { - throw new Error('Faber agent has no default mediator') - } + if (!mediationRecord) throw new Error('Faber agent has no default mediator') expect(requestMessage).toMatchObject({ service: { @@ -371,8 +411,12 @@ describe('Present Proof', () => { await aliceAgent.receiveMessage(requestMessage.toJSON()) - await aliceProofExchangeRecordPromise + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + }) - await faberProofExchangeRecordPromise + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + }) }) }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts index f35b4da5d3..6bed06c5ab 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-negotiation.test.ts @@ -1,38 +1,63 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' -import type { CredDefId } from 'indy-sdk' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage/didcomm' +import type { AnonCredsProofRequest } from '../../../../../../../anoncreds/src/models/exchange' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' +import type { V2ProposePresentationMessage, V2RequestPresentationMessage } from '../messages' + +import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../../../../../../../anoncreds/src/models/AnonCredsProofRequest' +import { + issueLegacyAnonCredsCredential, + setupAnonCredsTests, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForProofExchangeRecordSubject, testLogger } from '../../../../../../tests' import { JsonTransformer } from '../../../../../utils/JsonTransformer' -import { AttributeFilter } from '../../../formats/indy/models/AttributeFilter' -import { PredicateType } from '../../../formats/indy/models/PredicateType' -import { ProofAttributeInfo } from '../../../formats/indy/models/ProofAttributeInfo' -import { ProofPredicateInfo } from '../../../formats/indy/models/ProofPredicateInfo' -import { ProofRequest } from '../../../formats/indy/models/ProofRequest' import { ProofState } from '../../../models/ProofState' -import { V2ProposePresentationMessage, V2RequestPresentationMessage } from '../messages' -describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: CredDefId - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository +describe('V2 Proofs Negotiation - Indy', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let faberConnectionId: string + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent', - 'Alice agent' - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + + credentialDefinitionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber agent v2', + holderName: 'Alice agent v2', + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) }) afterAll(async () => { @@ -46,34 +71,34 @@ describe('Present Proof', () => { test(`Proof negotiation between Alice and Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - let faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v2', proofFormats: { indy: { name: 'proof-request', version: '1.0', - attributes: presentationPreview.attributes.filter((attribute) => attribute.name !== 'name'), - predicates: presentationPreview.predicates, + attributes: [], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 50, + }, + ], }, }, comment: 'V2 propose proof test 1', }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - let proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposePresentationMessage, + let faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.ProposalReceived, + threadId: aliceProofExchangeRecord.threadId, }) + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ @@ -94,29 +119,22 @@ describe('Present Proof', () => { id: expect.any(String), comment: 'V2 propose proof test 1', }) + // eslint-disable-next-line @typescript-eslint/no-explicit-any - let proposalAttach = proposal?.proposalAttachments[0].getDataAsJson() as any - let attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] - let predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] + const proposalAttach = ( + proposal as V2ProposePresentationMessage + )?.proposalAttachments?.[0].getDataAsJson() + expect(proposalAttach).toMatchObject({ - requested_attributes: { - [attributesGroup]: { - name: 'image_0', - restrictions: [ - { - cred_def_id: presentationPreview.attributes[1].credentialDefinitionId, - }, - ], - }, - }, + requested_attributes: {}, requested_predicates: { - [predicatesGroup]: { + [Object.keys(proposalAttach.requested_predicates)[0]]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: presentationPreview.predicates[0].credentialDefinitionId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -129,44 +147,6 @@ describe('Present Proof', () => { protocolVersion: 'v2', }) - // Negotiate Proposal - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - threadId: faberProofExchangeRecord.threadId, - state: ProofState.RequestReceived, - }) - testLogger.test('Faber sends new proof request to Alice') faberProofExchangeRecord = await faberAgent.proofs.negotiateProposal({ proofRecordId: faberProofExchangeRecord.id, @@ -174,22 +154,39 @@ describe('Present Proof', () => { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) testLogger.test('Alice waits for proof request from Faber') - aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - let request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, + aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, }) + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', id: expect.any(String), @@ -215,34 +212,36 @@ describe('Present Proof', () => { testLogger.test('Alice sends proof proposal to Faber') - faberProofExchangeRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - aliceProofExchangeRecord = await aliceAgent.proofs.negotiateRequest({ proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: { name: 'proof-request', version: '1.0', - attributes: presentationPreview.attributes.filter((attribute) => attribute.name === 'name'), - predicates: presentationPreview.predicates, + attributes: [], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 50, + }, + ], }, }, comment: 'V2 propose proof test 2', }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposePresentationMessage, + faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.ProposalReceived, + threadId: aliceProofExchangeRecord.threadId, + // Negotiation so this will be the second proposal + count: 2, }) - expect(proposal).toMatchObject({ + const proposal2 = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) + expect(proposal2).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ { @@ -262,29 +261,20 @@ describe('Present Proof', () => { id: expect.any(String), comment: 'V2 propose proof test 2', }) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - proposalAttach = proposal?.proposalAttachments[0].getDataAsJson() as any - attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] - predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] - expect(proposalAttach).toMatchObject({ - requested_attributes: { - [attributesGroup]: { - name: 'name', - restrictions: [ - { - cred_def_id: presentationPreview.attributes[1].credentialDefinitionId, - }, - ], - }, - }, + + const proposalAttach2 = ( + proposal as V2ProposePresentationMessage + )?.proposalAttachments[0].getDataAsJson() + expect(proposalAttach2).toMatchObject({ + requested_attributes: {}, requested_predicates: { - [predicatesGroup]: { + [Object.keys(proposalAttach2.requested_predicates)[0]]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: presentationPreview.predicates[0].credentialDefinitionId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -298,29 +288,21 @@ describe('Present Proof', () => { }) // Accept Proposal - const acceptProposalOptions: AcceptProofProposalOptions = { + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofExchangeRecord.id, - } - - aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { - threadId: faberProofExchangeRecord.threadId, - state: ProofState.RequestReceived, }) - testLogger.test('Faber accepts presentation proposal from Alice') - faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) - testLogger.test('Alice waits for proof request from Faber') - aliceProofExchangeRecord = await aliceProofExchangeRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, + aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, + // Negotiation so this will be the second request + count: 2, }) - expect(request).toMatchObject({ + const request2 = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) + expect(request2).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', formats: [ { @@ -350,7 +332,6 @@ describe('Present Proof', () => { }) const proposalMessage = await aliceAgent.proofs.findProposalMessage(aliceProofExchangeRecord.id) - expect(proposalMessage).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ @@ -372,29 +353,19 @@ describe('Present Proof', () => { comment: 'V2 propose proof test 2', }) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - proposalAttach = proposal?.proposalAttachments[0].getDataAsJson() as any - attributesGroup = Object.keys(proposalAttach.requested_attributes ?? {})[0] - predicatesGroup = Object.keys(proposalAttach.requested_predicates ?? {})[0] - expect(proposalAttach).toMatchObject({ - requested_attributes: { - [attributesGroup]: { - name: 'name', - restrictions: [ - { - cred_def_id: presentationPreview.attributes[1].credentialDefinitionId, - }, - ], - }, - }, + const proposalAttach3 = ( + proposal as V2ProposePresentationMessage + )?.proposalAttachments[0].getDataAsJson() + expect(proposalAttach3).toMatchObject({ + requested_attributes: {}, requested_predicates: { - [predicatesGroup]: { + [Object.keys(proposalAttach3.requested_predicates ?? {})[0]]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: presentationPreview.predicates[0].credentialDefinitionId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -407,24 +378,15 @@ describe('Present Proof', () => { const proofRequest = JsonTransformer.fromJSON( proofRequestMessage.requestAttachments[0].getDataAsJson(), - ProofRequest + AnonCredsProofRequestClass ) const predicateKey = proofRequest.requestedPredicates?.keys().next().value - expect(proofRequest.toJSON()).toMatchObject({ + expect(JsonTransformer.toJSON(proofRequest)).toMatchObject({ name: 'proof-request', nonce: expect.any(String), version: '1.0', - requested_attributes: { - '0': { - name: 'name', - restrictions: [ - { - cred_def_id: credDefId, - }, - ], - }, - }, + requested_attributes: {}, requested_predicates: { [predicateKey]: { name: 'age', @@ -432,7 +394,7 @@ describe('Present Proof', () => { p_value: 50, restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts index 2fccef1f98..ee4d3cd44b 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-presentation.e2e.test.ts @@ -1,31 +1,60 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' -import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' + +import { + setupAnonCredsTests, + issueLegacyAnonCredsCredential, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForProofExchangeRecordSubject, testLogger } from '../../../../../../tests' import { ProofState } from '../../../models/ProofState' import { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' -import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' - -describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository + +describe('V2 Proofs - Indy', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let faberConnectionId: string + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent v2 present proof', - 'Alice agent v2 present proof' - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + + credentialDefinitionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber agent v2', + holderName: 'Alice agent v2', + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) }) afterAll(async () => { @@ -39,34 +68,39 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v2', proofFormats: { indy: { name: 'ProofRequest', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + value: 'Alice', + credentialDefinitionId, + }, + ], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], }, }, comment: 'V2 propose proof test', }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberPresentationRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposePresentationMessage, + let faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.ProposalReceived, }) + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ @@ -93,32 +127,20 @@ describe('Present Proof', () => { state: ProofState.ProposalReceived, protocolVersion: 'v2', }) - }) - test(`Faber accepts the Proposal send by Alice`, async () => { // Accept Proposal - const acceptProposalOptions: AcceptProofProposalOptions = { + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofExchangeRecord.id, - } - - const alicePresentationRecordPromise = waitForProofExchangeRecord(aliceAgent, { - threadId: faberProofExchangeRecord.threadId, - state: ProofState.RequestReceived, }) - testLogger.test('Faber accepts presentation proposal from Alice') - faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) - testLogger.test('Alice waits for proof request from Faber') - aliceProofExchangeRecord = await alicePresentationRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, + aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, }) + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', formats: [ @@ -147,9 +169,7 @@ describe('Present Proof', () => { state: ProofState.RequestReceived, protocolVersion: 'v2', }) - }) - test(`Alice accepts presentation request from Faber`, async () => { // Alice retrieves the requested credentials and accepts the presentation request testLogger.test('Alice accepts presentation request from Faber') @@ -157,25 +177,20 @@ describe('Present Proof', () => { proofRecordId: aliceProofExchangeRecord.id, }) - const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { - threadId: aliceProofExchangeRecord.threadId, - state: ProofState.PresentationReceived, - }) - await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) + faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + threadId: aliceProofExchangeRecord.threadId, + state: ProofState.PresentationReceived, + }) + // Faber waits for the presentation from Alice testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberPresentationRecordPromise - - const presentation = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2PresentationMessage, - }) + const presentation = await faberAgent.proofs.findPresentationMessage(faberProofExchangeRecord.id) expect(presentation).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/presentation', formats: [ @@ -204,10 +219,8 @@ describe('Present Proof', () => { state: ProofState.PresentationReceived, protocolVersion: 'v2', }) - }) - test(`Faber accepts the presentation provided by Alice`, async () => { - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts deleted file mode 100644 index 68c09d5717..0000000000 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-proposal.e2e.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { ProofExchangeRecord } from '../../../repository' -import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage' -import { ProofState } from '../../../models/ProofState' -import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' - -describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberPresentationRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository - - beforeAll(async () => { - testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent v2', - 'Alice agent v2' - )) - }) - - afterAll(async () => { - testLogger.test('Shutting down both agents') - await faberAgent.shutdown() - await faberAgent.wallet.delete() - await aliceAgent.shutdown() - await aliceAgent.wallet.delete() - }) - - test(`Alice Creates and sends Proof Proposal to Faber`, async () => { - testLogger.test('Alice sends proof proposal to Faber') - - const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - - await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, - protocolVersion: 'v2', - proofFormats: { - indy: { - name: 'ProofRequest', - version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, - }, - }, - comment: 'V2 propose proof test', - }) - - testLogger.test('Faber waits for presentation from Alice') - faberPresentationRecord = await faberPresentationRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberPresentationRecord.id, - messageClass: V2ProposePresentationMessage, - }) - - expect(proposal).toMatchObject({ - type: 'https://didcomm.org/present-proof/2.0/propose-presentation', - formats: [ - { - attachmentId: expect.any(String), - format: 'hlindy/proof-req@v2.0', - }, - ], - proposalAttachments: [ - { - id: expect.any(String), - mimeType: 'application/json', - data: { - base64: expect.any(String), - }, - }, - ], - id: expect.any(String), - comment: 'V2 propose proof test', - }) - expect(faberPresentationRecord).toMatchObject({ - id: expect.anything(), - threadId: faberPresentationRecord.threadId, - state: ProofState.ProposalReceived, - protocolVersion: 'v2', - }) - }) -}) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts index 827555ec65..afe41e9417 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proof-request.e2e.test.ts @@ -1,31 +1,60 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections/repository/ConnectionRecord' -import type { AcceptProofProposalOptions } from '../../../ProofsApiOptions' -import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' -import type { V1PresentationPreview } from '../../v1/models/V1PresentationPreview' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' + +import { + issueLegacyAnonCredsCredential, + setupAnonCredsTests, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForProofExchangeRecordSubject } from '../../../../../../tests' import testLogger from '../../../../../../tests/logger' -import { DidCommMessageRepository } from '../../../../../storage' import { ProofState } from '../../../models/ProofState' -import { V2RequestPresentationMessage } from '../messages' -import { V2ProposePresentationMessage } from '../messages/V2ProposePresentationMessage' -describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - let faberProofExchangeRecord: ProofExchangeRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let didCommMessageRepository: DidCommMessageRepository +describe('V2 Proofs - Indy', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let faberConnectionId: string + let aliceConnectionId: string + let credentialDefinitionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, aliceConnection, presentationPreview } = await setupProofsTest( - 'Faber agent v2', - 'Alice agent v2' - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + + credentialDefinitionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber agent v2', + holderName: 'Alice agent v2', + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) }) afterAll(async () => { @@ -39,34 +68,39 @@ describe('Present Proof', () => { test(`Alice Creates and sends Proof Proposal to Faber`, async () => { testLogger.test('Alice sends proof proposal to Faber') - const faberPresentationRecordPromise = waitForProofExchangeRecord(faberAgent, { - state: ProofState.ProposalReceived, - }) - - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + let aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v2', proofFormats: { indy: { name: 'ProofRequest', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + value: 'Alice', + credentialDefinitionId, + }, + ], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], }, }, comment: 'V2 propose proof test', }) testLogger.test('Faber waits for presentation from Alice') - faberProofExchangeRecord = await faberPresentationRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const proposal = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2ProposePresentationMessage, + let faberProofExchangeRecord = await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.ProposalReceived, }) + const proposal = await faberAgent.proofs.findProposalMessage(faberProofExchangeRecord.id) expect(proposal).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/propose-presentation', formats: [ @@ -93,32 +127,20 @@ describe('Present Proof', () => { state: ProofState.ProposalReceived, protocolVersion: 'v2', }) - }) - test(`Faber accepts the Proposal sent by Alice`, async () => { // Accept Proposal - const acceptProposalOptions: AcceptProofProposalOptions = { + testLogger.test('Faber accepts presentation proposal from Alice') + faberProofExchangeRecord = await faberAgent.proofs.acceptProposal({ proofRecordId: faberProofExchangeRecord.id, - } - - const alicePresentationRecordPromise = waitForProofExchangeRecord(aliceAgent, { - threadId: faberProofExchangeRecord.threadId, - state: ProofState.RequestReceived, }) - testLogger.test('Faber accepts presentation proposal from Alice') - faberProofExchangeRecord = await faberAgent.proofs.acceptProposal(acceptProposalOptions) - testLogger.test('Alice waits for proof request from Faber') - aliceProofExchangeRecord = await alicePresentationRecordPromise - - didCommMessageRepository = faberAgent.dependencyManager.resolve(DidCommMessageRepository) - - const request = await didCommMessageRepository.findAgentMessage(faberAgent.context, { - associatedRecordId: faberProofExchangeRecord.id, - messageClass: V2RequestPresentationMessage, + aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { + threadId: faberProofExchangeRecord.threadId, + state: ProofState.RequestReceived, }) + const request = await faberAgent.proofs.findRequestMessage(faberProofExchangeRecord.id) expect(request).toMatchObject({ type: 'https://didcomm.org/present-proof/2.0/request-presentation', formats: [ diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts index e3a9841613..06ddf5cc34 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts @@ -1,29 +1,61 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { V1PresentationPreview } from '../../v1' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' -import testLogger from '../../../../../../tests/logger' -import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { + issueLegacyAnonCredsCredential, + setupAnonCredsTests, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForProofExchangeRecord, testLogger } from '../../../../../../tests' import { AutoAcceptProof, ProofState } from '../../../models' describe('Auto accept present proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let presentationPreview: V1PresentationPreview - - describe('Auto accept on `always`', () => { + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let credentialDefinitionId: string + let faberConnectionId: string + let aliceConnectionId: string + + describe("Auto accept on 'always'", () => { beforeAll(async () => { - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest( - 'Faber Auto Accept Always Proofs', - 'Alice Auto Accept Always Proofs', - AutoAcceptProof.Always - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Auto Accept Always Proofs', + holderName: 'Alice Auto Accept Always Proofs', + attributeNames: ['name', 'age'], + autoAcceptProofs: AutoAcceptProof.Always, + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) }) + afterAll(async () => { await faberAgent.shutdown() await faberAgent.wallet.delete() @@ -31,18 +63,31 @@ describe('Auto accept present proof', () => { await aliceAgent.wallet.delete() }) - test('Alice starts with proof proposal to Faber, both with autoAcceptProof on `always`', async () => { + test("Alice starts with proof proposal to Faber, both with autoAcceptProof on 'always'", async () => { testLogger.test('Alice sends presentation proposal to Faber') await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', proofFormats: { indy: { name: 'abc', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + credentialDefinitionId, + name: 'name', + value: 'Alice', + }, + ], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 50, + }, + ], }, }, }) @@ -55,40 +100,38 @@ describe('Auto accept present proof', () => { ]) }) - test('Faber starts with proof requests to Alice, both with autoAcceptProof on `always`', async () => { + test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'always'", async () => { testLogger.test('Faber sends presentation request to Alice') - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } await faberAgent.proofs.requestProof({ protocolVersion: 'v2', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) @@ -105,13 +148,43 @@ describe('Auto accept present proof', () => { describe("Auto accept on 'contentApproved'", () => { beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest( - 'Faber Auto Accept Content Approved Proofs', - 'Alice Auto Accept Content Approved Proofs', - AutoAcceptProof.ContentApproved - )) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber Auto Accept ContentApproved Proofs', + holderName: 'Alice Auto Accept ContentApproved Proofs', + attributeNames: ['name', 'age'], + autoAcceptProofs: AutoAcceptProof.ContentApproved, + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) }) + afterAll(async () => { testLogger.test('Shutting down both agents') await faberAgent.shutdown() @@ -128,14 +201,27 @@ describe('Auto accept present proof', () => { }) await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', proofFormats: { indy: { - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, name: 'abc', version: '1.0', + attributes: [ + { + credentialDefinitionId, + name: 'name', + value: 'Alice', + }, + ], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 50, + }, + ], }, }, }) @@ -153,28 +239,6 @@ describe('Auto accept present proof', () => { test("Faber starts with proof requests to Alice, both with autoAcceptProof on 'contentApproved'", async () => { testLogger.test('Faber sends presentation request to Alice') - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, @@ -182,13 +246,33 @@ describe('Auto accept present proof', () => { await faberAgent.proofs.requestProof({ protocolVersion: 'v2', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts index 815188bf69..29a7fce4c8 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts @@ -1,29 +1,82 @@ -import type { Agent } from '../../../../../agent/Agent' -import type { ConnectionRecord } from '../../../../connections' -import type { V1PresentationPreview } from '../../v1' - -import { setupProofsTest, waitForProofExchangeRecord } from '../../../../../../tests/helpers' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import type { EventReplaySubject } from '../../../../../../tests' + +import { + issueLegacyAnonCredsCredential, + setupAnonCredsTests, +} from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { waitForProofExchangeRecord } from '../../../../../../tests' import testLogger from '../../../../../../tests/logger' -import { getGroupKeysFromIndyProofFormatData } from '../../../formats/indy/__tests__/groupKeys' -import { ProofAttributeInfo, AttributeFilter, ProofPredicateInfo, PredicateType } from '../../../formats/indy/models' +import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' import { ProofState } from '../../../models' import { ProofExchangeRecord } from '../../../repository' import { V2ProposePresentationMessage, V2RequestPresentationMessage, V2PresentationMessage } from '../messages' describe('Present Proof', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: string - let aliceConnection: ConnectionRecord - let faberConnection: ConnectionRecord + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let credentialDefinitionId: string + let aliceConnectionId: string + let faberConnectionId: string let faberProofExchangeRecord: ProofExchangeRecord let aliceProofExchangeRecord: ProofExchangeRecord - let presentationPreview: V1PresentationPreview beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('Faber agent indy proofs', 'Alice agent indy proofs')) + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber agent indy proofs', + holderName: 'Alice agent indy proofs', + attributeNames: ['name', 'age', 'image_0', 'image_1'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'John', + }, + { + name: 'age', + value: '99', + }, + ], + linkedAttachments: [ + new LinkedAttachment({ + name: 'image_0', + attachment: new Attachment({ + filename: 'picture-of-a-cat.png', + data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + }), + }), + new LinkedAttachment({ + name: 'image_1', + attachment: new Attachment({ + filename: 'picture-of-a-dog.png', + data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + }), + }), + ], + }, + }) }) afterAll(async () => { @@ -43,14 +96,27 @@ describe('Present Proof', () => { }) aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + connectionId: aliceConnectionId, protocolVersion: 'v2', proofFormats: { indy: { name: 'abc', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + value: 'Alice', + credentialDefinitionId, + }, + ], + predicates: [ + { + name: 'age', + predicate: '>=', + threshold: 50, + credentialDefinitionId, + }, + ], }, }, }) @@ -217,9 +283,6 @@ describe('Present Proof', () => { const formatData = await aliceAgent.proofs.getFormatData(aliceProofExchangeRecord.id) - // eslint-disable-next-line prefer-const - let { proposeKey1, proposeKey2, requestKey1, requestKey2 } = getGroupKeysFromIndyProofFormatData(formatData) - expect(formatData).toMatchObject({ proposal: { indy: { @@ -227,26 +290,23 @@ describe('Present Proof', () => { version: '1.0', nonce: expect.any(String), requested_attributes: { - 0: { + [Object.keys(formatData.proposal?.indy?.requested_attributes ?? {})[0]]: { name: 'name', - }, - [proposeKey1]: { - name: 'image_0', restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, }, requested_predicates: { - [proposeKey2]: { + [Object.keys(formatData.proposal?.indy?.requested_predicates ?? {})[0]]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -259,26 +319,23 @@ describe('Present Proof', () => { version: '1.0', nonce: expect.any(String), requested_attributes: { - 0: { + [Object.keys(formatData.request?.indy?.requested_attributes ?? {})[0]]: { name: 'name', - }, - [requestKey1]: { - name: 'image_0', restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, }, requested_predicates: { - [requestKey2]: { + [Object.keys(formatData.request?.indy?.requested_predicates ?? {})[0]]: { name: 'age', p_type: '>=', p_value: 50, restrictions: [ { - cred_def_id: credDefId, + cred_def_id: credentialDefinitionId, }, ], }, @@ -307,39 +364,6 @@ describe('Present Proof', () => { }) test('Faber starts with proof request to Alice', async () => { - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Sample predicates - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - let aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) @@ -348,13 +372,41 @@ describe('Present Proof', () => { testLogger.test('Faber sends a presentation request to Alice') faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v2', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'Proof Request', version: '1.0.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) @@ -476,39 +528,6 @@ describe('Present Proof', () => { }) test('Alice provides credentials via call to getRequestedCredentials', async () => { - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Sample predicates - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) @@ -517,13 +536,41 @@ describe('Present Proof', () => { testLogger.test('Faber sends a presentation request to Alice') faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v2', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'Proof Request', version: '1.0.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) @@ -545,7 +592,7 @@ describe('Present Proof', () => { credentialId: expect.any(String), revealed: true, credentialInfo: { - referent: expect.any(String), + credentialId: expect.any(String), attributes: { image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', @@ -564,7 +611,7 @@ describe('Present Proof', () => { credentialId: expect.any(String), revealed: true, credentialInfo: { - referent: expect.any(String), + credentialId: expect.any(String), attributes: { age: '99', image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', @@ -584,7 +631,7 @@ describe('Present Proof', () => { { credentialId: expect.any(String), credentialInfo: { - referent: expect.any(String), + credentialId: expect.any(String), attributes: { image_1: 'hl:zQmRHBT9rDs5QhsnYuPY3mNpXxgLcnNXkhjWJvTSAPMmcVd', image_0: 'hl:zQmfDXo7T3J43j3CTkEZaz7qdHuABhWktksZ7JEBueZ5zUS', @@ -605,39 +652,6 @@ describe('Present Proof', () => { }) test('Faber starts with proof request to Alice but gets Problem Reported', async () => { - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - image_0: new ProofAttributeInfo({ - name: 'image_0', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - // Sample predicates - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { state: ProofState.RequestReceived, }) @@ -646,13 +660,41 @@ describe('Present Proof', () => { testLogger.test('Faber sends a presentation request to Alice') faberProofExchangeRecord = await faberAgent.proofs.requestProof({ protocolVersion: 'v2', - connectionId: faberConnection.id, + connectionId: faberConnectionId, proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + image_0: { + name: 'image_0', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 7c9bfab59f..e792d1c32c 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -7,25 +7,31 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' +import { sleep } from '../../../utils/sleep' import { ConnectionRecord, HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' -const recipientAgentOptions = getAgentOptions('Mediation: Recipient', { - indyLedgers: [], -}) -const mediatorAgentOptions = getAgentOptions('Mediation: Mediator', { - autoAcceptMediationRequests: true, - endpoints: ['rxjs:mediator'], - indyLedgers: [], -}) - -const senderAgentOptions = getAgentOptions('Mediation: Sender', { - endpoints: ['rxjs:sender'], - indyLedgers: [], -}) +const recipientAgentOptions = getAgentOptions('Mediation: Recipient', {}, getIndySdkModules()) +const mediatorAgentOptions = getAgentOptions( + 'Mediation: Mediator', + { + autoAcceptMediationRequests: true, + endpoints: ['rxjs:mediator'], + }, + getIndySdkModules() +) + +const senderAgentOptions = getAgentOptions( + 'Mediation: Sender', + { + endpoints: ['rxjs:sender'], + }, + getIndySdkModules() +) describe('mediator establishment', () => { let recipientAgent: Agent @@ -150,27 +156,27 @@ describe('mediator establishment', () => { 6. Send basic message from sender to recipient and assert it is received on the recipient side `, async () => { await e2eMediationTest(mediatorAgentOptions, { + ...recipientAgentOptions, config: { ...recipientAgentOptions.config, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - dependencies: recipientAgentOptions.dependencies, }) }) test('Mediation end-to-end flow (not using did:key)', async () => { await e2eMediationTest( { + ...mediatorAgentOptions, config: { ...mediatorAgentOptions.config, useDidKeyInProtocols: false }, - dependencies: mediatorAgentOptions.dependencies, }, { + ...recipientAgentOptions, config: { ...recipientAgentOptions.config, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, useDidKeyInProtocols: false, }, - dependencies: recipientAgentOptions.dependencies, } ) }) @@ -279,5 +285,7 @@ describe('mediator establishment', () => { }) expect(basicMessage.content).toBe(message) + + await recipientAgent.mediationRecipient.stopMessagePickup() }) }) diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts b/packages/core/src/modules/routing/__tests__/pickup.test.ts index 8b33fcacf2..c67bac9b89 100644 --- a/packages/core/src/modules/routing/__tests__/pickup.test.ts +++ b/packages/core/src/modules/routing/__tests__/pickup.test.ts @@ -5,20 +5,27 @@ import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions, waitForBasicMessage, waitForTrustPingReceivedEvent } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' -const recipientOptions = getAgentOptions('Mediation: Recipient Pickup', { - autoAcceptConnections: true, - indyLedgers: [], -}) -const mediatorOptions = getAgentOptions('Mediation: Mediator Pickup', { - autoAcceptConnections: true, - endpoints: ['wss://mediator'], - indyLedgers: [], -}) +const recipientOptions = getAgentOptions( + 'Mediation: Recipient Pickup', + { + autoAcceptConnections: true, + }, + getIndySdkModules() +) +const mediatorOptions = getAgentOptions( + 'Mediation: Mediator Pickup', + { + autoAcceptConnections: true, + endpoints: ['wss://mediator'], + }, + getIndySdkModules() +) describe('E2E Pick Up protocol', () => { let recipientAgent: Agent diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index 25dd20538b..e00b530afc 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -1,5 +1,4 @@ import type { AgentContext } from '../../../../agent' -import type { Wallet } from '../../../../wallet/Wallet' import type { Routing } from '../../../connections/services/ConnectionService' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../tests/helpers' @@ -8,11 +7,9 @@ import { AgentEventTypes } from '../../../../agent/Events' import { MessageSender } from '../../../../agent/MessageSender' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import { Key } from '../../../../crypto' -import { SigningProviderRegistry } from '../../../../crypto/signing-provider' import { Attachment } from '../../../../decorators/attachment/Attachment' import { AriesFrameworkError } from '../../../../error' import { uuid } from '../../../../utils/uuid' -import { IndyWallet } from '../../../../wallet/IndyWallet' import { DidExchangeState } from '../../../connections' import { ConnectionMetadataKeys } from '../../../connections/repository/ConnectionMetadataTypes' import { ConnectionRepository } from '../../../connections/repository/ConnectionRepository' @@ -63,7 +60,6 @@ describe('MediationRecipientService', () => { connectionImageUrl, }) - let wallet: Wallet let mediationRepository: MediationRepository let didRepository: DidRepository let didRegistrarService: DidRegistrarService @@ -76,16 +72,9 @@ describe('MediationRecipientService', () => { let agentContext: AgentContext beforeAll(async () => { - wallet = new IndyWallet(config.agentDependencies, config.logger, new SigningProviderRegistry([])) agentContext = getAgentContext({ agentConfig: config, }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(config.walletConfig!) - }) - - afterAll(async () => { - await wallet.delete() }) beforeEach(async () => { diff --git a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts index de6763884f..14ec7c22b8 100644 --- a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts @@ -1,40 +1,39 @@ +import type { Wallet } from '../../../../wallet' + import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../tests/helpers' import { EventEmitter } from '../../../../agent/EventEmitter' import { Key } from '../../../../crypto' -import { IndyWallet } from '../../../../wallet/IndyWallet' import { RoutingEventTypes } from '../../RoutingEvents' import { MediationRecipientService } from '../MediationRecipientService' import { RoutingService } from '../RoutingService' -jest.mock('../../../../wallet/IndyWallet') -const IndyWalletMock = IndyWallet as jest.Mock - jest.mock('../MediationRecipientService') const MediationRecipientServiceMock = MediationRecipientService as jest.Mock +const recipientKey = Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') const agentConfig = getAgentConfig('RoutingService', { endpoints: ['http://endpoint.com'], }) const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject()) -const wallet = new IndyWalletMock() +const wallet = { + createKey: jest.fn().mockResolvedValue(recipientKey), + // with satisfies Partial we still get type errors when the interface changes +} satisfies Partial const agentContext = getAgentContext({ - wallet, + wallet: wallet as unknown as Wallet, agentConfig, }) const mediationRecipientService = new MediationRecipientServiceMock() const routingService = new RoutingService(mediationRecipientService, eventEmitter) -const recipientKey = Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') - const routing = { endpoints: ['http://endpoint.com'], recipientKey, routingKeys: [], } mockFunction(mediationRecipientService.addMediationRouting).mockResolvedValue(routing) -mockFunction(wallet.createKey).mockResolvedValue(recipientKey) describe('RoutingService', () => { afterEach(() => { diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index 06e0b42245..f0f08fe07c 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -1,11 +1,13 @@ import type { AgentContext } from '../../../agent' +import type { Wallet } from '../../../wallet' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' import { KeyType } from '../../../crypto' import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { TypedArrayEncoder } from '../../../utils' import { JsonTransformer } from '../../../utils/JsonTransformer' -import { IndyWallet } from '../../../wallet/IndyWallet' import { WalletError } from '../../../wallet/error' import { DidKey } from '../../dids' import { @@ -43,8 +45,6 @@ const signatureSuiteRegistry = new SignatureSuiteRegistry([ const signingProviderRegistry = new SigningProviderRegistry([]) -jest.mock('../../ledger/services/IndyLedgerService') - jest.mock('../repository/W3cCredentialRepository') const W3cCredentialRepositoryMock = W3cCredentialRepository as jest.Mock @@ -64,16 +64,15 @@ const credentialRecordFactory = async (credential: W3cVerifiableCredential) => { } describe('W3cCredentialService', () => { - let wallet: IndyWallet + let wallet: Wallet let agentContext: AgentContext let w3cCredentialService: W3cCredentialService let w3cCredentialRepository: W3cCredentialRepository const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, signingProviderRegistry) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) + wallet = new IndySdkWallet(indySdk, agentConfig.logger, signingProviderRegistry) + await wallet.createAndOpen(agentConfig.walletConfig) agentContext = getAgentContext({ agentConfig, wallet, diff --git a/packages/core/src/plugins/Module.ts b/packages/core/src/plugins/Module.ts index 93196d71cf..bca1bc5ff7 100644 --- a/packages/core/src/plugins/Module.ts +++ b/packages/core/src/plugins/Module.ts @@ -1,10 +1,12 @@ import type { DependencyManager } from './DependencyManager' +import type { AgentContext } from '../agent' import type { FeatureRegistry } from '../agent/FeatureRegistry' import type { Constructor } from '../utils/mixins' export interface Module { api?: Constructor register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void + initialize?(agentContext: AgentContext): Promise } export interface ApiModule extends Module { diff --git a/packages/core/src/storage/BaseRecord.ts b/packages/core/src/storage/BaseRecord.ts index 9a8408032d..5b63bcfe86 100644 --- a/packages/core/src/storage/BaseRecord.ts +++ b/packages/core/src/storage/BaseRecord.ts @@ -18,7 +18,10 @@ export type RecordTags = ReturnType + // We want an empty object, as Record will make typescript + // not infer the types correctly + // eslint-disable-next-line @typescript-eslint/ban-types + MetadataValues = {} > { protected _tags: CustomTags = {} as CustomTags diff --git a/packages/core/src/storage/IndyStorageService.ts b/packages/core/src/storage/IndyStorageService.ts deleted file mode 100644 index bd71d1701f..0000000000 --- a/packages/core/src/storage/IndyStorageService.ts +++ /dev/null @@ -1,327 +0,0 @@ -import type { BaseRecord, TagsBase } from './BaseRecord' -import type { BaseRecordConstructor, Query, StorageService } from './StorageService' -import type { AgentContext } from '../agent' -import type { IndyWallet } from '../wallet/IndyWallet' -import type { default as Indy, WalletQuery, WalletRecord, WalletSearchOptions } from 'indy-sdk' - -import { AgentDependencies } from '../agent/AgentDependencies' -import { InjectionSymbols } from '../constants' -import { IndySdkError, RecordDuplicateError, RecordNotFoundError } from '../error' -import { injectable, inject } from '../plugins' -import { JsonTransformer } from '../utils/JsonTransformer' -import { isIndyError } from '../utils/indyError' -import { isBoolean } from '../utils/type' -import { assertIndyWallet } from '../wallet/util/assertIndyWallet' - -@injectable() -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export class IndyStorageService> implements StorageService { - private indy: typeof Indy - - private static DEFAULT_QUERY_OPTIONS = { - retrieveType: true, - retrieveTags: true, - } - - public constructor(@inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies) { - this.indy = agentDependencies.indy - } - - private transformToRecordTagValues(tags: { [key: number]: string | undefined }): TagsBase { - const transformedTags: TagsBase = {} - - for (const [key, value] of Object.entries(tags)) { - // If the value is a boolean string ('1' or '0') - // use the boolean val - if (value === '1' && key?.includes(':')) { - const [tagName, tagValue] = key.split(':') - - const transformedValue = transformedTags[tagName] - - if (Array.isArray(transformedValue)) { - transformedTags[tagName] = [...transformedValue, tagValue] - } else { - transformedTags[tagName] = [tagValue] - } - } - // Transform '1' and '0' to boolean - else if (value === '1' || value === '0') { - transformedTags[key] = value === '1' - } - // If 1 or 0 is prefixed with 'n__' we need to remove it. This is to prevent - // casting the value to a boolean - else if (value === 'n__1' || value === 'n__0') { - transformedTags[key] = value === 'n__1' ? '1' : '0' - } - // Otherwise just use the value - else { - transformedTags[key] = value - } - } - - return transformedTags - } - - private transformFromRecordTagValues(tags: TagsBase): { [key: string]: string | undefined } { - const transformedTags: { [key: string]: string | undefined } = {} - - for (const [key, value] of Object.entries(tags)) { - // If the value is of type null we use the value undefined - // Indy doesn't support null as a value - if (value === null) { - transformedTags[key] = undefined - } - // If the value is a boolean use the indy - // '1' or '0' syntax - else if (isBoolean(value)) { - transformedTags[key] = value ? '1' : '0' - } - // If the value is 1 or 0, we need to add something to the value, otherwise - // the next time we deserialize the tag values it will be converted to boolean - else if (value === '1' || value === '0') { - transformedTags[key] = `n__${value}` - } - // If the value is an array we create a tag for each array - // item ("tagName:arrayItem" = "1") - else if (Array.isArray(value)) { - value.forEach((item) => { - const tagName = `${key}:${item}` - transformedTags[tagName] = '1' - }) - } - // Otherwise just use the value - else { - transformedTags[key] = value - } - } - - return transformedTags - } - - /** - * Transforms the search query into a wallet query compatible with indy WQL. - * - * The format used by AFJ is almost the same as the indy query, with the exception of - * the encoding of values, however this is handled by the {@link IndyStorageService.transformToRecordTagValues} - * method. - */ - private indyQueryFromSearchQuery(query: Query): Record { - // eslint-disable-next-line prefer-const - let { $and, $or, $not, ...tags } = query - - $and = ($and as Query[] | undefined)?.map((q) => this.indyQueryFromSearchQuery(q)) - $or = ($or as Query[] | undefined)?.map((q) => this.indyQueryFromSearchQuery(q)) - $not = $not ? this.indyQueryFromSearchQuery($not as Query) : undefined - - const indyQuery = { - ...this.transformFromRecordTagValues(tags as unknown as TagsBase), - $and, - $or, - $not, - } - - return indyQuery - } - - private recordToInstance(record: WalletRecord, recordClass: BaseRecordConstructor): T { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const instance = JsonTransformer.deserialize(record.value!, recordClass) - instance.id = record.id - - const tags = record.tags ? this.transformToRecordTagValues(record.tags) : {} - instance.replaceTags(tags) - - return instance - } - - /** @inheritDoc */ - public async save(agentContext: AgentContext, record: T) { - assertIndyWallet(agentContext.wallet) - - record.updatedAt = new Date() - - const value = JsonTransformer.serialize(record) - const tags = this.transformFromRecordTagValues(record.getTags()) as Record - - try { - await this.indy.addWalletRecord(agentContext.wallet.handle, record.type, record.id, value, tags) - } catch (error) { - // Record already exists - if (isIndyError(error, 'WalletItemAlreadyExists')) { - throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async update(agentContext: AgentContext, record: T): Promise { - assertIndyWallet(agentContext.wallet) - - record.updatedAt = new Date() - - const value = JsonTransformer.serialize(record) - const tags = this.transformFromRecordTagValues(record.getTags()) as Record - - try { - await this.indy.updateWalletRecordValue(agentContext.wallet.handle, record.type, record.id, value) - await this.indy.updateWalletRecordTags(agentContext.wallet.handle, record.type, record.id, tags) - } catch (error) { - // Record does not exist - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`record with id ${record.id} not found.`, { - recordType: record.type, - cause: error, - }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async delete(agentContext: AgentContext, record: T) { - assertIndyWallet(agentContext.wallet) - - try { - await this.indy.deleteWalletRecord(agentContext.wallet.handle, record.type, record.id) - } catch (error) { - // Record does not exist - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`record with id ${record.id} not found.`, { - recordType: record.type, - cause: error, - }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async deleteById( - agentContext: AgentContext, - recordClass: BaseRecordConstructor, - id: string - ): Promise { - assertIndyWallet(agentContext.wallet) - - try { - await this.indy.deleteWalletRecord(agentContext.wallet.handle, recordClass.type, id) - } catch (error) { - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`record with id ${id} not found.`, { - recordType: recordClass.type, - cause: error, - }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async getById(agentContext: AgentContext, recordClass: BaseRecordConstructor, id: string): Promise { - assertIndyWallet(agentContext.wallet) - - try { - const record = await this.indy.getWalletRecord( - agentContext.wallet.handle, - recordClass.type, - id, - IndyStorageService.DEFAULT_QUERY_OPTIONS - ) - return this.recordToInstance(record, recordClass) - } catch (error) { - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`record with id ${id} not found.`, { - recordType: recordClass.type, - cause: error, - }) - } - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - /** @inheritDoc */ - public async getAll(agentContext: AgentContext, recordClass: BaseRecordConstructor): Promise { - assertIndyWallet(agentContext.wallet) - - const recordIterator = this.search( - agentContext.wallet, - recordClass.type, - {}, - IndyStorageService.DEFAULT_QUERY_OPTIONS - ) - const records = [] - for await (const record of recordIterator) { - records.push(this.recordToInstance(record, recordClass)) - } - return records - } - - /** @inheritDoc */ - public async findByQuery( - agentContext: AgentContext, - recordClass: BaseRecordConstructor, - query: Query - ): Promise { - assertIndyWallet(agentContext.wallet) - - const indyQuery = this.indyQueryFromSearchQuery(query) - - const recordIterator = this.search( - agentContext.wallet, - recordClass.type, - indyQuery, - IndyStorageService.DEFAULT_QUERY_OPTIONS - ) - const records = [] - for await (const record of recordIterator) { - records.push(this.recordToInstance(record, recordClass)) - } - return records - } - - private async *search( - wallet: IndyWallet, - type: string, - query: WalletQuery, - { limit = Infinity, ...options }: WalletSearchOptions & { limit?: number } - ) { - try { - const searchHandle = await this.indy.openWalletSearch(wallet.handle, type, query, options) - - let records: Indy.WalletRecord[] = [] - - // Allow max of 256 per fetch operation - const chunk = limit ? Math.min(256, limit) : 256 - - // Loop while limit not reached (or no limit specified) - while (!limit || records.length < limit) { - // Retrieve records - const recordsJson = await this.indy.fetchWalletSearchNextRecords(wallet.handle, searchHandle, chunk) - - if (recordsJson.records) { - records = [...records, ...recordsJson.records] - - for (const record of recordsJson.records) { - yield record - } - } - - // If the number of records returned is less than chunk - // It means we reached the end of the iterator (no more records) - if (!records.length || !recordsJson.records || recordsJson.records.length < chunk) { - await this.indy.closeWalletSearch(searchHandle) - - return - } - } - } catch (error) { - throw new IndySdkError(error, `Searching '${type}' records for query '${JSON.stringify(query)}' failed`) - } - } -} diff --git a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts index 067a290dcb..1329595f56 100644 --- a/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts +++ b/packages/core/src/storage/__tests__/DidCommMessageRepository.test.ts @@ -1,15 +1,17 @@ +import type { StorageService } from '../StorageService' + import { Subject } from 'rxjs' +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' import { getAgentConfig, getAgentContext, mockFunction } from '../../../tests/helpers' import { EventEmitter } from '../../agent/EventEmitter' import { ConnectionInvitationMessage } from '../../modules/connections' import { JsonTransformer } from '../../utils/JsonTransformer' -import { IndyStorageService } from '../IndyStorageService' import { DidCommMessageRecord, DidCommMessageRepository, DidCommMessageRole } from '../didcomm' -jest.mock('../IndyStorageService') +jest.mock('../../../../../tests/InMemoryStorageService') -const StorageMock = IndyStorageService as unknown as jest.Mock> +const StorageMock = InMemoryStorageService as unknown as jest.Mock> const invitationJson = { '@type': 'https://didcomm.org/connections/1.0/invitation', @@ -24,7 +26,7 @@ const agentContext = getAgentContext() describe('DidCommMessageRepository', () => { let repository: DidCommMessageRepository - let storageMock: IndyStorageService + let storageMock: StorageService let eventEmitter: EventEmitter beforeEach(async () => { diff --git a/packages/core/src/storage/__tests__/IndyStorageService.test.ts b/packages/core/src/storage/__tests__/IndyStorageService.test.ts deleted file mode 100644 index b517641408..0000000000 --- a/packages/core/src/storage/__tests__/IndyStorageService.test.ts +++ /dev/null @@ -1,323 +0,0 @@ -import type { AgentContext } from '../../agent' -import type { TagsBase } from '../BaseRecord' -import type * as Indy from 'indy-sdk' - -import { agentDependencies, getAgentConfig, getAgentContext } from '../../../tests/helpers' -import { SigningProviderRegistry } from '../../crypto/signing-provider' -import { RecordDuplicateError, RecordNotFoundError } from '../../error' -import { sleep } from '../../utils/sleep' -import { IndyWallet } from '../../wallet/IndyWallet' -import { IndyStorageService } from '../IndyStorageService' - -import { TestRecord } from './TestRecord' - -const startDate = Date.now() - -describe('IndyStorageService', () => { - let wallet: IndyWallet - let indy: typeof Indy - let storageService: IndyStorageService - let agentContext: AgentContext - - beforeEach(async () => { - const agentConfig = getAgentConfig('IndyStorageServiceTest') - indy = agentConfig.agentDependencies.indy - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) - agentContext = getAgentContext({ - wallet, - agentConfig, - }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) - storageService = new IndyStorageService(agentConfig.agentDependencies) - }) - - afterEach(async () => { - await wallet.delete() - }) - - const insertRecord = async ({ id, tags }: { id?: string; tags?: TagsBase }) => { - const props = { - id, - foo: 'bar', - tags: tags ?? { myTag: 'foobar' }, - } - const record = new TestRecord(props) - await storageService.save(agentContext, record) - return record - } - - describe('tag transformation', () => { - it('should correctly transform tag values to string before storing', async () => { - const record = await insertRecord({ - id: 'test-id', - tags: { - someBoolean: true, - someOtherBoolean: false, - someStringValue: 'string', - anArrayValue: ['foo', 'bar'], - // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' - someStringNumberValue: '1', - anotherStringNumberValue: '0', - }, - }) - - const retrieveRecord = await indy.getWalletRecord(wallet.handle, record.type, record.id, { - retrieveType: true, - retrieveTags: true, - }) - - expect(retrieveRecord.tags).toEqual({ - someBoolean: '1', - someOtherBoolean: '0', - someStringValue: 'string', - 'anArrayValue:foo': '1', - 'anArrayValue:bar': '1', - someStringNumberValue: 'n__1', - anotherStringNumberValue: 'n__0', - }) - }) - - it('should correctly transform tag values from string after retrieving', async () => { - await indy.addWalletRecord(wallet.handle, TestRecord.type, 'some-id', '{}', { - someBoolean: '1', - someOtherBoolean: '0', - someStringValue: 'string', - 'anArrayValue:foo': '1', - 'anArrayValue:bar': '1', - // booleans are stored as '1' and '0' so we store the string values '1' and '0' as 'n__1' and 'n__0' - someStringNumberValue: 'n__1', - anotherStringNumberValue: 'n__0', - }) - - const record = await storageService.getById(agentContext, TestRecord, 'some-id') - - expect(record.getTags()).toEqual({ - someBoolean: true, - someOtherBoolean: false, - someStringValue: 'string', - anArrayValue: expect.arrayContaining(['bar', 'foo']), - someStringNumberValue: '1', - anotherStringNumberValue: '0', - }) - }) - }) - - describe('save()', () => { - it('should throw RecordDuplicateError if a record with the id already exists', async () => { - const record = await insertRecord({ id: 'test-id' }) - - return expect(() => storageService.save(agentContext, record)).rejects.toThrowError(RecordDuplicateError) - }) - - it('should save the record', async () => { - const record = await insertRecord({ id: 'test-id' }) - const found = await storageService.getById(agentContext, TestRecord, 'test-id') - - expect(record).toEqual(found) - }) - - it('After a save the record should have update the updatedAt property', async () => { - const time = startDate - const record = await insertRecord({ id: 'test-updatedAt' }) - expect(record.updatedAt?.getTime()).toBeGreaterThan(time) - }) - }) - - describe('getById()', () => { - it('should throw RecordNotFoundError if the record does not exist', async () => { - return expect(() => storageService.getById(agentContext, TestRecord, 'does-not-exist')).rejects.toThrowError( - RecordNotFoundError - ) - }) - - it('should return the record by id', async () => { - const record = await insertRecord({ id: 'test-id' }) - const found = await storageService.getById(agentContext, TestRecord, 'test-id') - - expect(found).toEqual(record) - }) - }) - - describe('update()', () => { - it('should throw RecordNotFoundError if the record does not exist', async () => { - const record = new TestRecord({ - id: 'test-id', - foo: 'test', - tags: { some: 'tag' }, - }) - - return expect(() => storageService.update(agentContext, record)).rejects.toThrowError(RecordNotFoundError) - }) - - it('should update the record', async () => { - const record = await insertRecord({ id: 'test-id' }) - - record.replaceTags({ ...record.getTags(), foo: 'bar' }) - record.foo = 'foobaz' - await storageService.update(agentContext, record) - - const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) - expect(retrievedRecord).toEqual(record) - }) - - it('After a record has been updated it should have updated the updatedAT property', async () => { - const time = startDate - const record = await insertRecord({ id: 'test-id' }) - - record.replaceTags({ ...record.getTags(), foo: 'bar' }) - record.foo = 'foobaz' - await storageService.update(agentContext, record) - - const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) - expect(retrievedRecord.createdAt.getTime()).toBeGreaterThan(time) - }) - }) - - describe('delete()', () => { - it('should throw RecordNotFoundError if the record does not exist', async () => { - const record = new TestRecord({ - id: 'test-id', - foo: 'test', - tags: { some: 'tag' }, - }) - - return expect(() => storageService.delete(agentContext, record)).rejects.toThrowError(RecordNotFoundError) - }) - - it('should delete the record', async () => { - const record = await insertRecord({ id: 'test-id' }) - await storageService.delete(agentContext, record) - - return expect(() => storageService.getById(agentContext, TestRecord, record.id)).rejects.toThrowError( - RecordNotFoundError - ) - }) - }) - - describe('getAll()', () => { - it('should retrieve all records', async () => { - const createdRecords = await Promise.all( - Array(5) - .fill(undefined) - .map((_, index) => insertRecord({ id: `record-${index}` })) - ) - - const records = await storageService.getAll(agentContext, TestRecord) - - expect(records).toEqual(expect.arrayContaining(createdRecords)) - }) - }) - - describe('findByQuery()', () => { - it('should retrieve all records that match the query', async () => { - const expectedRecord = await insertRecord({ tags: { myTag: 'foobar' } }) - await insertRecord({ tags: { myTag: 'notfoobar' } }) - - const records = await storageService.findByQuery(agentContext, TestRecord, { myTag: 'foobar' }) - - expect(records.length).toBe(1) - expect(records[0]).toEqual(expectedRecord) - }) - - it('finds records using $and statements', async () => { - const expectedRecord = await insertRecord({ tags: { myTag: 'foo', anotherTag: 'bar' } }) - await insertRecord({ tags: { myTag: 'notfoobar' } }) - - const records = await storageService.findByQuery(agentContext, TestRecord, { - $and: [{ myTag: 'foo' }, { anotherTag: 'bar' }], - }) - - expect(records.length).toBe(1) - expect(records[0]).toEqual(expectedRecord) - }) - - it('finds records using $or statements', async () => { - const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) - const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) - await insertRecord({ tags: { myTag: 'notfoobar' } }) - - const records = await storageService.findByQuery(agentContext, TestRecord, { - $or: [{ myTag: 'foo' }, { anotherTag: 'bar' }], - }) - - expect(records.length).toBe(2) - expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) - }) - - it('finds records using $not statements', async () => { - const expectedRecord = await insertRecord({ tags: { myTag: 'foo' } }) - const expectedRecord2 = await insertRecord({ tags: { anotherTag: 'bar' } }) - await insertRecord({ tags: { myTag: 'notfoobar' } }) - - const records = await storageService.findByQuery(agentContext, TestRecord, { - $not: { myTag: 'notfoobar' }, - }) - - expect(records.length).toBe(2) - expect(records).toEqual(expect.arrayContaining([expectedRecord, expectedRecord2])) - }) - - it('correctly transforms an advanced query into a valid WQL query', async () => { - const indySpy = jest.fn() - const storageServiceWithoutIndy = new IndyStorageService({ - ...agentDependencies, - indy: { - openWalletSearch: indySpy, - fetchWalletSearchNextRecords: jest.fn(() => ({ records: undefined })), - closeWalletSearch: jest.fn(), - } as unknown as typeof Indy, - }) - - await storageServiceWithoutIndy.findByQuery(agentContext, TestRecord, { - $and: [ - { - $or: [{ myTag: true }, { myTag: false }], - }, - { - $and: [{ theNumber: '0' }, { theNumber: '1' }], - }, - ], - $or: [ - { - aValue: ['foo', 'bar'], - }, - ], - $not: { myTag: 'notfoobar' }, - }) - - const expectedQuery = { - $and: [ - { - $and: undefined, - $not: undefined, - $or: [ - { myTag: '1', $and: undefined, $or: undefined, $not: undefined }, - { myTag: '0', $and: undefined, $or: undefined, $not: undefined }, - ], - }, - { - $or: undefined, - $not: undefined, - $and: [ - { theNumber: 'n__0', $and: undefined, $or: undefined, $not: undefined }, - { theNumber: 'n__1', $and: undefined, $or: undefined, $not: undefined }, - ], - }, - ], - $or: [ - { - 'aValue:foo': '1', - 'aValue:bar': '1', - $and: undefined, - $or: undefined, - $not: undefined, - }, - ], - $not: { myTag: 'notfoobar', $and: undefined, $or: undefined, $not: undefined }, - } - - expect(indySpy).toBeCalledWith(expect.anything(), expect.anything(), expectedQuery, expect.anything()) - }) - }) -}) diff --git a/packages/core/src/storage/__tests__/Repository.test.ts b/packages/core/src/storage/__tests__/Repository.test.ts index 11cb3dd9cc..a5ae5fd52f 100644 --- a/packages/core/src/storage/__tests__/Repository.test.ts +++ b/packages/core/src/storage/__tests__/Repository.test.ts @@ -1,27 +1,28 @@ import type { AgentContext } from '../../agent' import type { TagsBase } from '../BaseRecord' import type { RecordDeletedEvent, RecordSavedEvent, RecordUpdatedEvent } from '../RepositoryEvents' +import type { StorageService } from '../StorageService' import { Subject } from 'rxjs' +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' import { getAgentConfig, getAgentContext, mockFunction } from '../../../tests/helpers' import { EventEmitter } from '../../agent/EventEmitter' import { AriesFrameworkError, RecordDuplicateError, RecordNotFoundError } from '../../error' -import { IndyStorageService } from '../IndyStorageService' import { Repository } from '../Repository' import { RepositoryEventTypes } from '../RepositoryEvents' import { TestRecord } from './TestRecord' -jest.mock('../IndyStorageService') +jest.mock('../../../../../tests/InMemoryStorageService') -const StorageMock = IndyStorageService as unknown as jest.Mock> +const StorageMock = InMemoryStorageService as unknown as jest.Mock> const config = getAgentConfig('Repository') describe('Repository', () => { let repository: Repository - let storageMock: IndyStorageService + let storageMock: StorageService let agentContext: AgentContext let eventEmitter: EventEmitter diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index f38ba94a87..cd5fddaccf 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -5,6 +5,9 @@ import { unlinkSync, readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { IndySdkSymbol } from '../../../../../indy-sdk/src/types' +import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../../../../src' import { agentDependencies as dependencies } from '../../../../tests/helpers' import { InjectionSymbols } from '../../../constants' @@ -39,6 +42,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { @@ -95,6 +101,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { @@ -153,6 +162,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { @@ -211,6 +223,9 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index 9fb991253b..1f619b1b57 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -4,7 +4,10 @@ import { unlinkSync, readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { IndySdkSymbol } from '../../../../../indy-sdk/src/types' import { Agent } from '../../../../src' +import { indySdk } from '../../../../tests' import { agentDependencies } from '../../../../tests/helpers' import { InjectionSymbols } from '../../../constants' import { DependencyManager } from '../../../plugins' @@ -33,6 +36,9 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { @@ -99,6 +105,9 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { @@ -143,6 +152,9 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) const agent = new Agent( diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index 124cdf0a88..29ed62d1b9 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -4,6 +4,9 @@ import { unlinkSync, readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { IndySdkSymbol } from '../../../../../indy-sdk/src/types' +import { indySdk } from '../../../../tests' import { agentDependencies } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' @@ -31,6 +34,9 @@ describe('UpdateAssistant | v0.3 - v0.3.1', () => { const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) const agent = new Agent( { diff --git a/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts index d1677a5648..2f9e3e80a3 100644 --- a/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts +++ b/packages/core/src/storage/migration/__tests__/UpdateAssistant.test.ts @@ -1,6 +1,9 @@ import type { BaseRecord } from '../../BaseRecord' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { IndySdkWallet } from '../../../../../indy-sdk/src' +import { IndySdkSymbol } from '../../../../../indy-sdk/src/types' +import { indySdk } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' @@ -8,7 +11,7 @@ import { DependencyManager } from '../../../plugins' import { UpdateAssistant } from '../UpdateAssistant' import { CURRENT_FRAMEWORK_STORAGE_VERSION } from '../updates' -const agentOptions = getAgentOptions('UpdateAssistant') +const agentOptions = getAgentOptions('UpdateAssistant', {}) describe('UpdateAssistant', () => { let updateAssistant: UpdateAssistant @@ -18,6 +21,9 @@ describe('UpdateAssistant', () => { beforeEach(async () => { const dependencyManager = new DependencyManager() storageService = new InMemoryStorageService() + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) agent = new Agent(agentOptions, dependencyManager) diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 8fe31caafb..a64116a5f5 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -380,8 +380,6 @@ Object { "tags": Object { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "credentialIds": Array [], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -429,8 +427,6 @@ Object { "credentialIds": Array [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -679,8 +675,6 @@ Object { "tags": Object { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "credentialIds": Array [], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -728,8 +722,6 @@ Object { "credentialIds": Array [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -2823,8 +2815,6 @@ Object { "tags": Object { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "credentialIds": Array [], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -2872,8 +2862,6 @@ Object { "credentialIds": Array [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, @@ -3122,8 +3110,6 @@ Object { "tags": Object { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "credentialIds": Array [], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, @@ -3171,8 +3157,6 @@ Object { "credentialIds": Array [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], - "indyCredentialRevocationId": undefined, - "indyRevocationRegistryId": undefined, "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index 8a8b351a38..e582263b57 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -4,6 +4,7 @@ import type { StorageUpdateError } from '../error/StorageUpdateError' import { readFileSync, unlinkSync } from 'fs' import path from 'path' +import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { InjectionSymbols } from '../../../constants' @@ -13,7 +14,7 @@ import { JsonTransformer } from '../../../utils' import { StorageUpdateService } from '../StorageUpdateService' import { UpdateAssistant } from '../UpdateAssistant' -const agentOptions = getAgentOptions('UpdateAssistant | Backup') +const agentOptions = getAgentOptions('UpdateAssistant | Backup', {}, getIndySdkModules()) const aliceCredentialRecordsString = readFileSync( path.join(__dirname, '__fixtures__/alice-4-credentials-0.1.json'), diff --git a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts index 647f8e8a40..61c2ddb3af 100644 --- a/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts +++ b/packages/core/src/storage/migration/updates/0.1-0.2/credential.ts @@ -1,9 +1,8 @@ import type { BaseAgent } from '../../../../agent/BaseAgent' -import type { CredentialMetadata, CredentialExchangeRecord } from '../../../../modules/credentials' +import type { CredentialExchangeRecord } from '../../../../modules/credentials' import type { JsonObject } from '../../../../types' import { CredentialState } from '../../../../modules/credentials/models/CredentialState' -import { CredentialMetadataKeys } from '../../../../modules/credentials/repository/CredentialMetadataTypes' import { CredentialRepository } from '../../../../modules/credentials/repository/CredentialRepository' import { Metadata } from '../../../Metadata' import { DidCommMessageRepository, DidCommMessageRecord, DidCommMessageRole } from '../../../didcomm' @@ -122,27 +121,25 @@ export async function updateIndyMetadata( agent.config.logger.debug(`Updating indy metadata to use the generic metadata api available to records.`) const { requestMetadata, schemaId, credentialDefinitionId, ...rest } = credentialRecord.metadata.data - const metadata = new Metadata(rest) + const metadata = new Metadata>(rest) + const indyRequestMetadataKey = '_internal/indyRequest' + const indyCredentialMetadataKey = '_internal/indyCredential' if (requestMetadata) { - agent.config.logger.trace( - `Found top-level 'requestMetadata' key, moving to '${CredentialMetadataKeys.IndyRequest}'` - ) - metadata.add(CredentialMetadataKeys.IndyRequest, { ...requestMetadata }) + agent.config.logger.trace(`Found top-level 'requestMetadata' key, moving to '${indyRequestMetadataKey}'`) + metadata.add(indyRequestMetadataKey, { ...requestMetadata }) } if (schemaId && typeof schemaId === 'string') { - agent.config.logger.trace( - `Found top-level 'schemaId' key, moving to '${CredentialMetadataKeys.IndyCredential}.schemaId'` - ) - metadata.add(CredentialMetadataKeys.IndyCredential, { schemaId }) + agent.config.logger.trace(`Found top-level 'schemaId' key, moving to '${indyCredentialMetadataKey}.schemaId'`) + metadata.add(indyCredentialMetadataKey, { schemaId }) } if (credentialDefinitionId && typeof credentialDefinitionId === 'string') { agent.config.logger.trace( - `Found top-level 'credentialDefinitionId' key, moving to '${CredentialMetadataKeys.IndyCredential}.credentialDefinitionId'` + `Found top-level 'credentialDefinitionId' key, moving to '${indyCredentialMetadataKey}.credentialDefinitionId'` ) - metadata.add(CredentialMetadataKeys.IndyCredential, { credentialDefinitionId }) + metadata.add(indyCredentialMetadataKey, { credentialDefinitionId }) } credentialRecord.metadata = metadata @@ -170,7 +167,7 @@ export async function updateIndyMetadata( * "credentials": [ * { * "credentialRecordId": "09e46da9-a575-4909-b016-040e96c3c539", - * "credentialRecordType": "indy", + * "credentialRecordType": "anoncreds" * } * ] * } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 23fe599c84..f621d4393e 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,6 +1,5 @@ import type { Logger } from './logger' import type { AutoAcceptCredential } from './modules/credentials/models/CredentialAutoAcceptType' -import type { IndyPoolConfig } from './modules/ledger/IndyPool' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' @@ -84,20 +83,6 @@ export interface InitConfig { */ autoAcceptCredentials?: AutoAcceptCredential - /** - * @deprecated configure `indyLedgers` on the `LedgerModule` class - * @note This setting will be ignored if the `LedgerModule` is manually configured as - * a module - */ - indyLedgers?: IndyPoolConfig[] - - /** - * @deprecated configure `connectToIndyLedgersOnStartup` on the `LedgerModule` class - * @note This setting will be ignored if the `LedgerModule` is manually configured as - * a module - */ - connectToIndyLedgersOnStartup?: boolean - /** * @deprecated configure `autoAcceptMediationRequests` on the `RecipientModule` class * @note This setting will be ignored if the `RecipientModule` is manually configured as diff --git a/packages/core/src/utils/__tests__/did.test.ts b/packages/core/src/utils/__tests__/did.test.ts deleted file mode 100644 index 3d7aa07792..0000000000 --- a/packages/core/src/utils/__tests__/did.test.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { - getIndyDidFromVerificationMethod, - isAbbreviatedVerkey, - isDid, - isDidIdentifier, - isFullVerkey, - isSelfCertifiedDid, - isVerkey, -} from '../did' - -const validAbbreviatedVerkeys = [ - '~PKAYz8Ev4yoQgr2LaMAWFx', - '~Soy1augaQrQYtNZRRHsikB', - '~BUF7uxYTxZ6qYdZ4G9e1Gi', - '~DbZ4gkBqhFRVsT5P7BJqyZ', - '~4zmNTdG78iYyMAQdEQLrf8', -] - -const invalidAbbreviatedVerkeys = [ - '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvt', - '8jG2Bim1HNSybCTdKBRppP4PCQSSijx1pBnreqsdo8JG', - 'ABUF7uxYTxZ6qYdZ4G9e1Gi', - '~Db3IgkBqhFRVsT5P7BJqyZ', - '~4zmNTlG78iYyMAQdEQLrf8', -] - -const validFullVerkeys = [ - '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvt', - '8jG2Bim1HNSybCTdKBRppP4PCQSSijx1pBnreqsdo8JG', - '9wMLhw9SSxtTUyosrndMbvWY4TtDbVvRnMtzG2NysniP', - '6m2XT39vivJ7tLSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', - 'CAgL85iEecPNQMmxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', - 'MqXmB7cTsTXqyxDPBbrgu5EPqw61kouK1qjMvnoPa96', -] - -const invalidFullVerkeys = [ - '~PKAYz8Ev4yoQgr2LaMAWFx', - '~Soy1augaQrQYtNZRRHsikB', - '6YnVN5Qdb6mqimTRQcQmSXrHXKdTEdRn5YHZReezUTvta', - '6m2XT39vIvJ7tLSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', - 'CAgL85iEecPNQMlxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', -] - -const invalidVerkeys = [ - '6YnVN5Qdb6mqimTIQcQmSXrHXKdTEdRn5YHZReezUTvta', - '6m2XT39vIvJ7tlSxNPM8siMnhYCZcdMxbkTcJDSzAQTu', - 'CAgL85iEecPNQMlxQ1hgbqczwq7SAerQ8RbWTRtC7SoK', - '6YnVN5Qdb6mqilTRQcQmSXrHXKdTEdRn5YHZReezUTvt', - '8jG2Bim1HNIybCTdKBRppP4PCQSSijx1pBnreqsdo8JG', - 'ABUF7uxYTxZ6qYdZ4G9e1Gi', - '~Db3IgkBqhFRVsT5P7BJqyZ', - '~4zmNTlG78IYyMAQdEQLrf8', - 'randomverkey', -] - -const validDids = [ - 'did:indy:BBPoJqRKatdcfLEAFL7exC', - 'did:sov:N8NQHLtCKfPmWMgCSdfa7h', - 'did:random:FBSegXg6AsF8J73kx22gjk', - 'did:sov:8u2b8ZH6sHeWfvphyQuHCL', - 'did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a', - 'did:btcr:xyv2-xzpq-q9wa-p7t', -] - -const invalidDids = [ - '6YnVN5Qdb6mqimTIQcQmSXrHXKdTEdRn5YHZReezUTvta', - 'did:BBPoJqRKatdcfLEAFL7exC', - 'sov:N8NQHLtCKfPmWMgCSdfa7h', - '8kyt-fzzq-qpqq-ljsc-5l', - 'did:test1:N8NQHLtCKfPmWMgCSdfa7h', - 'deid:ethr:9noxi4nL4SiJAsFcMLp2U4', -] - -const validDidIdentifiers = [ - '8kyt-fzzq-qpqq-ljsc-5l', - 'fEMDp21GvaafC5hXLaLHf', - '9noxi4nL4SiJAsFcMLp2U4', - 'QdAJFDpbVoHYrUpNAMe3An', - 'B9Y3e8PUKrM1ShumWU36xW', - '0xf3beac30c498d9e26865f34fcaa57dbb935b0d74', -] - -const invalidDidIdentifiers = [ - '6YnVN5Qdb6mqimTIQcQmSXrHXKdTEdRn5YHZReezUTvt/a', - 'did:BBPoJqRKatdcfLEAFL7exC', - 'sov:N8NQHLtCKfPmWMgCSdfa7h', - 'did:test1:N8NQHLtCKfPmWMgCSdfa7h', - 'deid:ethr:9noxi4nL4SiJAsFcMLp2U4', -] - -const verificationMethod = { - id: 'did:key:z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr#z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr', - type: 'Ed25519VerificationKey2018', - controller: 'did:key:z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr', - publicKeyBase58: 'VExfvq3kqkbAfCA3PZ8CRbzH2A3HyV9FWwdSq6WhtgU', -} - -const invalidVerificationMethod = [ - { - id: 'did:key:z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr#z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr', - type: 'Ed25519VerificationKey2018', - controller: 'did:key:z6MkewW1GB5V6PF4HA2rixWy3X9z6bRthrjVwXrZH74Xd7Tr', - publicKeyBase58: '', - }, -] - -const indyDid = 'tpF86Zd1cf9JdVmqKdMW2' - -describe('Utils | Did', () => { - describe('isSelfCertifiedDid()', () => { - test('returns true if the verkey is abbreviated', () => { - expect(isSelfCertifiedDid('PW8ZHpNupeWXbmpPWog6Ki', '~QQ5jiH1dgXPAnvHdJvazn9')).toBe(true) - }) - - test('returns true if the verkey is not abbreviated and the did is generated from the verkey', () => { - expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'HyEoPRNvC7q4jj5joUo8AWYtxbNccbEnTAeuMYkpmNS2')).toBe(true) - }) - - test('returns false if the verkey is not abbreviated and the did is not generated from the verkey', () => { - expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'AcU7DnRqoXGYATD6VqsRq4eHuz55gdM3uzFBEhFd6rGh')).toBe(false) - }) - }) - - describe('isAbbreviatedVerkey()', () => { - test.each(validAbbreviatedVerkeys)('returns true when valid abbreviated verkey "%s" is passed in', (verkey) => { - expect(isAbbreviatedVerkey(verkey)).toBe(true) - }) - - test.each(invalidAbbreviatedVerkeys)( - 'returns false when invalid abbreviated verkey "%s" is passed in', - (verkey) => { - expect(isAbbreviatedVerkey(verkey)).toBe(false) - } - ) - }) - - describe('isFullVerkey()', () => { - test.each(validFullVerkeys)('returns true when valid full verkey "%s" is passed in', (verkey) => { - expect(isFullVerkey(verkey)).toBe(true) - }) - - test.each(invalidFullVerkeys)('returns false when invalid full verkey "%s" is passed in', (verkey) => { - expect(isFullVerkey(verkey)).toBe(false) - }) - }) - - describe('isVerkey()', () => { - const validVerkeys = [...validAbbreviatedVerkeys, ...validFullVerkeys] - - test.each(validVerkeys)('returns true when valid verkey "%s" is passed in', (verkey) => { - expect(isVerkey(verkey)).toBe(true) - }) - - test.each(invalidVerkeys)('returns false when invalid verkey "%s" is passed in', (verkey) => { - expect(isVerkey(verkey)).toBe(false) - }) - }) - - describe('isDid()', () => { - test.each(validDids)('returns true when valid did "%s" is passed in', (did) => { - expect(isDid(did)).toBe(true) - }) - - test.each(invalidDids)('returns false when invalid did "%s" is passed in', (did) => { - expect(isDid(did)).toBe(false) - }) - }) - - describe('isDidIdentifier()', () => { - test.each(validDidIdentifiers)('returns true when valid did identifier "%s" is passed in', (didIdentifier) => { - expect(isDidIdentifier(didIdentifier)).toBe(true) - }) - - test.each(invalidDidIdentifiers)('returns false when invalid did identifier "%s" is passed in', (didIdentifier) => { - expect(isDidIdentifier(didIdentifier)).toBe(false) - }) - }) - - describe('getIndyDidFromVerificationMethod()', () => { - expect(getIndyDidFromVerificationMethod(verificationMethod)).toBe(indyDid) - - test.each(invalidVerificationMethod)('throw error when invalid public key in verification method', (method) => { - expect(() => { - getIndyDidFromVerificationMethod(method) - }).toThrow() - }) - }) -}) diff --git a/packages/core/src/utils/__tests__/indyIdentifiers.test.ts b/packages/core/src/utils/__tests__/indyIdentifiers.test.ts deleted file mode 100644 index 8da274a789..0000000000 --- a/packages/core/src/utils/__tests__/indyIdentifiers.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { - isQualifiedIndyIdentifier, - getQualifiedIndyCredentialDefinitionId, - getQualifiedIndySchemaId, - getLegacyCredentialDefinitionId, - getLegacySchemaId, -} from '../indyIdentifiers' - -const indyNamespace = 'some:staging' -const did = 'q7ATwTYbQDgiigVijUAej' -const qualifiedSchemaId = `did:indy:${indyNamespace}:${did}/anoncreds/v0/SCHEMA/awesomeSchema/4.2.0` -const qualifiedCredentialDefinitionId = `did:indy:${indyNamespace}:${did}/anoncreds/v0/CLAIM_DEF/99/sth` -const unqualifiedSchemaId = `${did}:2:awesomeSchema:4.2.0` -const unqualifiedCredentialDefinitionId = `${did}:3:CL:99:sth` - -describe('Mangle indy identifiers', () => { - test('is a qualified identifier', async () => { - expect(isQualifiedIndyIdentifier(qualifiedSchemaId)).toBe(true) - }) - - test('is NOT a qualified identifier', async () => { - expect(isQualifiedIndyIdentifier(did)).toBe(false) - }) - - describe('get the qualified identifier', () => { - it('should return the qualified identifier if the identifier is already qualified', () => { - expect(getQualifiedIndyCredentialDefinitionId(indyNamespace, qualifiedCredentialDefinitionId)).toBe( - qualifiedCredentialDefinitionId - ) - }) - - it('should return the qualified identifier for a credential definition', () => { - expect(getQualifiedIndyCredentialDefinitionId(indyNamespace, unqualifiedCredentialDefinitionId)).toBe( - qualifiedCredentialDefinitionId - ) - }) - - it('should return the qualified identifier for a schema', () => { - expect(getQualifiedIndySchemaId(indyNamespace, qualifiedSchemaId)).toBe(qualifiedSchemaId) - }) - - it('should return the qualified identifier for a schema', () => { - expect(getQualifiedIndySchemaId(indyNamespace, unqualifiedSchemaId)).toBe(qualifiedSchemaId) - }) - }) - - // generateSchemaId - it('Should return a valid schema ID given did name and version', () => { - const did = '12345', - name = 'backbench', - version = '420' - expect(getLegacySchemaId(did, name, version)).toEqual('12345:2:backbench:420') - }) - - // generateCredentialDefinitionId - it('Should return a valid schema ID given did name and version', () => { - const did = '12345', - seqNo = 420, - tag = 'someTag' - expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') - }) -}) diff --git a/packages/core/src/utils/__tests__/regex.test.ts b/packages/core/src/utils/__tests__/regex.test.ts deleted file mode 100644 index 93cbaa7ae8..0000000000 --- a/packages/core/src/utils/__tests__/regex.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { credDefIdRegex, indyDidRegex, schemaIdRegex, schemaVersionRegex } from '../regex' - -describe('Valid Regular Expression', () => { - const invalidTest = 'test' - - test('test for credDefIdRegex', async () => { - const test = 'q7ATwTYbQDgiigVijUAej:3:CL:160971:1.0.0' - expect(test).toMatch(credDefIdRegex) - expect(credDefIdRegex.test(invalidTest)).toBeFalsy() - }) - - test('test for indyDidRegex', async () => { - const test = 'did:sov:q7ATwTYbQDgiigVijUAej' - expect(test).toMatch(indyDidRegex) - expect(indyDidRegex.test(invalidTest)).toBeFalsy - }) - - test('test for schemaIdRegex', async () => { - const test = 'q7ATwTYbQDgiigVijUAej:2:test:1.0' - expect(test).toMatch(schemaIdRegex) - expect(schemaIdRegex.test(invalidTest)).toBeFalsy - }) - - test('test for schemaVersionRegex', async () => { - const test = '1.0.0' - expect(test).toMatch(schemaVersionRegex) - expect(schemaVersionRegex.test(invalidTest)).toBeFalsy - }) -}) diff --git a/packages/core/src/utils/did.ts b/packages/core/src/utils/did.ts index 74f346560d..d21405252d 100644 --- a/packages/core/src/utils/did.ts +++ b/packages/core/src/utils/did.ts @@ -1,53 +1,4 @@ -/** - * Based on DidUtils implementation in Aries Framework .NET - * @see: https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Utils/DidUtils.cs - * - * Some context about full verkeys versus abbreviated verkeys: - * A standard verkey is 32 bytes, and by default in Indy the DID is chosen as the first 16 bytes of that key, before base58 encoding. - * An abbreviated verkey replaces the first 16 bytes of the verkey with ~ when it matches the DID. - * - * When a full verkey is used to register on the ledger, this is stored as a full verkey on the ledger and also returned from the ledger as a full verkey. - * The same applies to an abbreviated verkey. If an abbreviated verkey is used to register on the ledger, this is stored as an abbreviated verkey on the ledger and also returned from the ledger as an abbreviated verkey. - * - * For this reason we need some methods to check whether verkeys are full or abbreviated, so we can align this with `indy.abbreviateVerkey` - * - * Aries Framework .NET also abbreviates verkey before sending to ledger: - * https://github.com/hyperledger/aries-framework-dotnet/blob/f90eaf9db8548f6fc831abea917e906201755763/src/Hyperledger.Aries/Ledger/DefaultLedgerService.cs#L139-L147 - */ - -import type { VerificationMethod } from './../modules/dids/domain/verificationMethod/VerificationMethod' - import { TypedArrayEncoder } from './TypedArrayEncoder' -import { Buffer } from './buffer' - -export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ -export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ -export const VERKEY_REGEX = new RegExp(`${FULL_VERKEY_REGEX.source}|${ABBREVIATED_VERKEY_REGEX.source}`) -export const DID_REGEX = /^did:([a-z]+):([a-zA-z\d]+)/ -export const DID_IDENTIFIER_REGEX = /^[a-zA-z\d-]+$/ - -/** - * Check whether the did is a self certifying did. If the verkey is abbreviated this method - * will always return true. Make sure that the verkey you pass in this method belongs to the - * did passed in - * - * @return Boolean indicating whether the did is self certifying - */ -export function isSelfCertifiedDid(did: string, verkey: string): boolean { - // If the verkey is Abbreviated, it means the full verkey - // is the did + the verkey - if (isAbbreviatedVerkey(verkey)) { - return true - } - - const didFromVerkey = indyDidFromPublicKeyBase58(verkey) - - if (didFromVerkey === did) { - return true - } - - return false -} export function indyDidFromPublicKeyBase58(publicKeyBase58: string): string { const buffer = TypedArrayEncoder.fromBase58(publicKeyBase58) @@ -56,108 +7,3 @@ export function indyDidFromPublicKeyBase58(publicKeyBase58: string): string { return did } - -export function getFullVerkey(did: string, verkey: string) { - if (isFullVerkey(verkey)) return verkey - - // Did could have did:xxx prefix, only take the last item after : - const id = did.split(':').pop() ?? did - // Verkey is prefixed with ~ if abbreviated - const verkeyWithoutTilde = verkey.slice(1) - - // Create base58 encoded public key (32 bytes) - return TypedArrayEncoder.toBase58( - Buffer.concat([ - // Take did identifier (16 bytes) - TypedArrayEncoder.fromBase58(id), - // Concat the abbreviated verkey (16 bytes) - TypedArrayEncoder.fromBase58(verkeyWithoutTilde), - ]) - ) -} - -/** - * Extract did from schema id - */ -export function didFromSchemaId(schemaId: string) { - const [did] = schemaId.split(':') - - return did -} - -/** - * Extract did from credential definition id - */ -export function didFromCredentialDefinitionId(credentialDefinitionId: string) { - const [did] = credentialDefinitionId.split(':') - - return did -} - -/** - * Extract did from revocation registry definition id - */ -export function didFromRevocationRegistryDefinitionId(revocationRegistryId: string) { - const [did] = revocationRegistryId.split(':') - - return did -} - -/** - * Check a base58 encoded string against a regex expression to determine if it is a full valid verkey - * @param verkey Base58 encoded string representation of a verkey - * @return Boolean indicating if the string is a valid verkey - */ -export function isFullVerkey(verkey: string): boolean { - return FULL_VERKEY_REGEX.test(verkey) -} - -/** - * Check a base58 encoded string against a regex expression to determine if it is a valid abbreviated verkey - * @param verkey Base58 encoded string representation of an abbreviated verkey - * @returns Boolean indicating if the string is a valid abbreviated verkey - */ -export function isAbbreviatedVerkey(verkey: string): boolean { - return ABBREVIATED_VERKEY_REGEX.test(verkey) -} - -/** - * Check a base58 encoded string to determine if it is a valid verkey - * @param verkey Base58 encoded string representation of a verkey - * @returns Boolean indicating if the string is a valid verkey - */ -export function isVerkey(verkey: string): boolean { - return VERKEY_REGEX.test(verkey) -} - -/** - * Check a string to determine if it is a valid did - * @param did - * @return Boolean indicating if the string is a valid did - */ -export function isDid(did: string): boolean { - return DID_REGEX.test(did) -} - -/** - * Check a string to determine if it is a valid did identifier. - * @param identifier Did identifier. This is a did without the did:method part - * @return Boolean indicating if the string is a valid did identifier - */ -export function isDidIdentifier(identifier: string): boolean { - return DID_IDENTIFIER_REGEX.test(identifier) -} - -/** - * Get indy did from verification method - * @param verificationMethod - * @returns indy did - */ -export function getIndyDidFromVerificationMethod(verificationMethod: VerificationMethod): string { - if (!verificationMethod?.publicKeyBase58) { - throw new Error(`Unable to get publicKeyBase58 from verification method`) - } - const buffer = TypedArrayEncoder.fromBase58(verificationMethod.publicKeyBase58) - const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) - return did -} diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 8875930e15..a432b6bc6c 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -5,13 +5,10 @@ export * from './MultiBaseEncoder' export * from './buffer' export * from './MultiHashEncoder' export * from './JWE' -export * from './regex' -export * from './indyProofRequest' export * from './VarintEncoder' export * from './Hasher' export * from './validators' export * from './type' -export * from './indyIdentifiers' export * from './deepEquality' export * from './objectEquality' export * from './MessageValidator' diff --git a/packages/core/src/utils/indyIdentifiers.ts b/packages/core/src/utils/indyIdentifiers.ts deleted file mode 100644 index 0d6343a3e7..0000000000 --- a/packages/core/src/utils/indyIdentifiers.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * - * @see For the definitions below see also: https://hyperledger.github.io/indy-did-method/#indy-did-method-identifiers - * - */ -export type Did = 'did' -export type DidIndyMethod = 'indy' -// Maybe this can be typed more strictly than string. Choosing string for now as this can be eg just `sovrin` or eg `sovrin:staging` -export type DidIndyNamespace = string -// NOTE: because of the ambiguous nature - whether there is a colon or not within DidIndyNamespace this is the substring after the ***last*** colon -export type NamespaceIdentifier = string - -// TODO: This template literal type can possibly be improved. This version leaves the substrings as potentially undefined -export type IndyNamespace = `${Did}:${DidIndyMethod}:${DidIndyNamespace}:${NamespaceIdentifier}` - -export function isQualifiedIndyIdentifier(identifier: string | undefined): identifier is IndyNamespace { - if (!identifier || identifier === '') return false - return identifier.startsWith('did:indy:') -} - -export function getQualifiedIndyCredentialDefinitionId( - indyNamespace: string, - unqualifiedCredentialDefinitionId: string -): IndyNamespace { - if (isQualifiedIndyIdentifier(unqualifiedCredentialDefinitionId)) return unqualifiedCredentialDefinitionId - - // 5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npbd - const [did, , , seqNo, tag] = unqualifiedCredentialDefinitionId.split(':') - - return `did:indy:${indyNamespace}:${did}/anoncreds/v0/CLAIM_DEF/${seqNo}/${tag}` -} - -/** - * - * @see https://hyperledger.github.io/indy-did-method/#schema - * - */ -export function getQualifiedIndySchemaId(indyNamespace: string, schemaId: string): IndyNamespace { - if (isQualifiedIndyIdentifier(schemaId)) return schemaId - - // F72i3Y3Q4i466efjYJYCHM:2:npdb:4.3.4 - const [did, , schemaName, schemaVersion] = schemaId.split(':') - - return `did:indy:${indyNamespace}:${did}/anoncreds/v0/SCHEMA/${schemaName}/${schemaVersion}` -} - -export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { - return `${unqualifiedDid}:2:${name}:${version}` -} - -export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: number, tag: string) { - return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` -} diff --git a/packages/core/src/utils/indyProofRequest.ts b/packages/core/src/utils/indyProofRequest.ts deleted file mode 100644 index df52b72cdc..0000000000 --- a/packages/core/src/utils/indyProofRequest.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { ProofRequest } from '../modules/proofs/formats/indy/models/ProofRequest' - -import { AriesFrameworkError } from '../error/AriesFrameworkError' - -function attributeNamesToArray(proofRequest: ProofRequest) { - // Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array - // containing all attribute names from the requested attributes. - return Array.from(proofRequest.requestedAttributes.values()).reduce( - (names, a) => [...names, ...(a.name ? [a.name] : a.names ? a.names : [])], - [] - ) -} - -function predicateNamesToArray(proofRequest: ProofRequest) { - return Array.from(new Set(Array.from(proofRequest.requestedPredicates.values()).map((a) => a.name))) -} - -function assertNoDuplicates(predicates: string[], attributeNames: string[]) { - const duplicates = predicates.filter((item) => attributeNames.indexOf(item) !== -1) - if (duplicates.length > 0) { - throw new AriesFrameworkError( - `The proof request contains duplicate predicates and attributes: ${duplicates.toString()}` - ) - } -} - -// TODO: This is still not ideal. The requested groups can specify different credentials using restrictions. -export function assertNoDuplicateGroupsNamesInProofRequest(proofRequest: ProofRequest) { - const attributes = attributeNamesToArray(proofRequest) - const predicates = predicateNamesToArray(proofRequest) - assertNoDuplicates(predicates, attributes) -} diff --git a/packages/core/src/utils/regex.ts b/packages/core/src/utils/regex.ts deleted file mode 100644 index 629be026df..0000000000 --- a/packages/core/src/utils/regex.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const schemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ -export const schemaVersionRegex = /^(\d+\.)?(\d+\.)?(\*|\d+)$/ -export const credDefIdRegex = /^([a-zA-Z0-9]{21,22}):3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ -export const indyDidRegex = /^(did:sov:)?[a-zA-Z0-9]{21,22}$/ diff --git a/packages/core/src/utils/transformers.ts b/packages/core/src/utils/transformers.ts index eb6dea844a..005f0065da 100644 --- a/packages/core/src/utils/transformers.ts +++ b/packages/core/src/utils/transformers.ts @@ -6,45 +6,6 @@ import { DateTime } from 'luxon' import { Metadata } from '../storage/Metadata' -import { JsonTransformer } from './JsonTransformer' - -/** - * Decorator that transforms json to and from corresponding record. - * - * @example - * class Example { - * RecordTransformer(Service) - * private services: Record; - * } - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function RecordTransformer(Class: { new (...args: any[]): T }) { - return Transform(({ value, type }) => { - switch (type) { - case TransformationType.CLASS_TO_PLAIN: - return Object.entries(value).reduce( - (accumulator, [key, attribute]) => ({ - ...accumulator, - [key]: JsonTransformer.toJSON(attribute), - }), - {} - ) - - case TransformationType.PLAIN_TO_CLASS: - return Object.entries(value).reduce( - (accumulator, [key, attribute]) => ({ - ...accumulator, - [key]: JsonTransformer.fromJSON(attribute, Class), - }), - {} - ) - - default: - return value - } - }) -} - /* * Decorator that transforms to and from a metadata instance. */ diff --git a/packages/core/src/utils/type.ts b/packages/core/src/utils/type.ts index 2155975323..064ca0ce75 100644 --- a/packages/core/src/utils/type.ts +++ b/packages/core/src/utils/type.ts @@ -4,10 +4,6 @@ export type SingleOrArray = T | T[] export type Optional = Pick, K> & Omit -export const isString = (value: unknown): value is string => typeof value === 'string' -export const isNumber = (value: unknown): value is number => typeof value === 'number' -export const isBoolean = (value: unknown): value is boolean => typeof value === 'boolean' - export const isJsonObject = (value: unknown): value is JsonObject => { return value !== undefined && typeof value === 'object' && value !== null && !Array.isArray(value) } diff --git a/packages/core/src/wallet/IndyWallet.test.ts b/packages/core/src/wallet/IndyWallet.test.ts deleted file mode 100644 index 8a600a2592..0000000000 --- a/packages/core/src/wallet/IndyWallet.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import type { WalletConfig } from '../types' - -import { SIGNATURE_LENGTH as ED25519_SIGNATURE_LENGTH } from '@stablelib/ed25519' - -import { agentDependencies } from '../../tests/helpers' -import testLogger from '../../tests/logger' -import { KeyType } from '../crypto' -import { SigningProviderRegistry } from '../crypto/signing-provider' -import { KeyDerivationMethod } from '../types' -import { TypedArrayEncoder } from '../utils' - -import { IndyWallet } from './IndyWallet' -import { WalletError } from './error' - -// use raw key derivation method to speed up wallet creating / opening / closing between tests -const walletConfig: WalletConfig = { - id: 'Wallet: IndyWalletTest', - // generated using indy.generateWalletKey - key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', - keyDerivationMethod: KeyDerivationMethod.Raw, -} - -const walletConfigWithMasterSecretId: WalletConfig = { - id: 'Wallet: WalletTestWithMasterSecretId', - // generated using indy.generateWalletKey - key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', - keyDerivationMethod: KeyDerivationMethod.Raw, - masterSecretId: 'customMasterSecretId', -} - -describe('IndyWallet', () => { - let indyWallet: IndyWallet - - const privateKey = TypedArrayEncoder.fromString('sample-seed') - const message = TypedArrayEncoder.fromString('sample-message') - - beforeEach(async () => { - indyWallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) - await indyWallet.createAndOpen(walletConfig) - }) - - afterEach(async () => { - await indyWallet.delete() - }) - - test('Get the public DID', async () => { - await indyWallet.initPublicDid({ seed: '000000000000000000000000Trustee9' }) - expect(indyWallet.publicDid).toMatchObject({ - did: expect.any(String), - verkey: expect.any(String), - }) - }) - - test('Get the Master Secret', () => { - expect(indyWallet.masterSecretId).toEqual('Wallet: IndyWalletTest') - }) - - test('Get the wallet handle', () => { - expect(indyWallet.handle).toEqual(expect.any(Number)) - }) - - test('Initializes a public did', async () => { - await indyWallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) - - expect(indyWallet.publicDid).toEqual({ - did: 'DtWRdd6C5dN5vpcN6XRAvu', - verkey: '82RBSn3heLgXzZd74UsMC8Q8YRfEEhQoAM7LUqE6bevJ', - }) - }) - - test('Generate Nonce', async () => { - await expect(indyWallet.generateNonce()).resolves.toEqual(expect.any(String)) - }) - - test('Create ed25519 keypair from private key', async () => { - const key = await indyWallet.createKey({ - privateKey: TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67'), - keyType: KeyType.Ed25519, - }) - - expect(key).toMatchObject({ - keyType: KeyType.Ed25519, - }) - }) - - test('Fail to create ed25519 keypair from seed', async () => { - await expect(indyWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError(WalletError) - }) - - test('Fail to create x25519 keypair', async () => { - await expect(indyWallet.createKey({ privateKey, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) - }) - - test('Create a signature with a ed25519 keypair', async () => { - const ed25519Key = await indyWallet.createKey({ keyType: KeyType.Ed25519 }) - const signature = await indyWallet.sign({ - data: message, - key: ed25519Key, - }) - expect(signature.length).toStrictEqual(ED25519_SIGNATURE_LENGTH) - }) - - test('Verify a signed message with a ed25519 publicKey', async () => { - const ed25519Key = await indyWallet.createKey({ keyType: KeyType.Ed25519 }) - const signature = await indyWallet.sign({ - data: message, - key: ed25519Key, - }) - await expect(indyWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) - }) - - test('masterSecretId is equal to wallet ID by default', async () => { - expect(indyWallet.masterSecretId).toEqual(walletConfig.id) - }) -}) - -describe('IndyWallet with custom Master Secret Id', () => { - let indyWallet: IndyWallet - - beforeEach(async () => { - indyWallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) - await indyWallet.createAndOpen(walletConfigWithMasterSecretId) - }) - - afterEach(async () => { - await indyWallet.delete() - }) - - test('masterSecretId is set by config', async () => { - expect(indyWallet.masterSecretId).toEqual(walletConfigWithMasterSecretId.masterSecretId) - }) -}) diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts deleted file mode 100644 index 8c8e83d575..0000000000 --- a/packages/core/src/wallet/IndyWallet.ts +++ /dev/null @@ -1,703 +0,0 @@ -import type { - WalletCreateKeyOptions, - DidConfig, - DidInfo, - WalletSignOptions, - UnpackedMessageContext, - WalletVerifyOptions, - Wallet, -} from './Wallet' -import type { KeyPair } from '../crypto/signing-provider/SigningProvider' -import type { - EncryptedMessage, - KeyDerivationMethod, - WalletConfig, - WalletConfigRekey, - WalletExportImportConfig, -} from '../types' -import type { Buffer } from '../utils/buffer' -import type { default as Indy, WalletStorageConfig } from 'indy-sdk' - -import { inject, injectable } from 'tsyringe' - -import { AgentDependencies } from '../agent/AgentDependencies' -import { InjectionSymbols } from '../constants' -import { KeyType } from '../crypto' -import { Key } from '../crypto/Key' -import { SigningProviderRegistry } from '../crypto/signing-provider/SigningProviderRegistry' -import { AriesFrameworkError, IndySdkError, RecordDuplicateError, RecordNotFoundError } from '../error' -import { Logger } from '../logger' -import { TypedArrayEncoder } from '../utils' -import { JsonEncoder } from '../utils/JsonEncoder' -import { isError } from '../utils/error' -import { isIndyError } from '../utils/indyError' - -import { WalletDuplicateError, WalletError, WalletNotFoundError } from './error' -import { WalletInvalidKeyError } from './error/WalletInvalidKeyError' - -@injectable() -export class IndyWallet implements Wallet { - private walletConfig?: WalletConfig - private walletHandle?: number - - private logger: Logger - private signingKeyProviderRegistry: SigningProviderRegistry - private publicDidInfo: DidInfo | undefined - private indy: typeof Indy - - public constructor( - @inject(InjectionSymbols.AgentDependencies) agentDependencies: AgentDependencies, - @inject(InjectionSymbols.Logger) logger: Logger, - signingKeyProviderRegistry: SigningProviderRegistry - ) { - this.logger = logger - this.signingKeyProviderRegistry = signingKeyProviderRegistry - this.indy = agentDependencies.indy - } - - public get isProvisioned() { - return this.walletConfig !== undefined - } - - public get isInitialized() { - return this.walletHandle !== undefined - } - - /** - * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be - * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but - * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather - * use the `DidsModule`. - */ - public get publicDid() { - return this.publicDidInfo - } - - public get handle() { - if (!this.walletHandle) { - throw new AriesFrameworkError( - 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' - ) - } - - return this.walletHandle - } - - public get masterSecretId() { - if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) { - throw new AriesFrameworkError( - 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' - ) - } - - return this.walletConfig?.masterSecretId ?? this.walletConfig.id - } - - /** - * Dispose method is called when an agent context is disposed. - */ - public async dispose() { - if (this.isInitialized) { - await this.close() - } - } - - private walletStorageConfig(walletConfig: WalletConfig): Indy.WalletConfig { - const walletStorageConfig: Indy.WalletConfig = { - id: walletConfig.id, - storage_type: walletConfig.storage?.type, - } - - if (walletConfig.storage?.config) { - walletStorageConfig.storage_config = walletConfig.storage?.config as WalletStorageConfig - } - - return walletStorageConfig - } - - private walletCredentials( - walletConfig: WalletConfig, - rekey?: string, - rekeyDerivation?: KeyDerivationMethod - ): Indy.OpenWalletCredentials { - const walletCredentials: Indy.OpenWalletCredentials = { - key: walletConfig.key, - key_derivation_method: walletConfig.keyDerivationMethod, - } - if (rekey) { - walletCredentials.rekey = rekey - } - if (rekeyDerivation) { - walletCredentials.rekey_derivation_method = rekeyDerivation - } - if (walletConfig.storage?.credentials) { - walletCredentials.storage_credentials = walletConfig.storage?.credentials as Record - } - - return walletCredentials - } - - /** - * @throws {WalletDuplicateError} if the wallet already exists - * @throws {WalletError} if another error occurs - */ - public async create(walletConfig: WalletConfig): Promise { - await this.createAndOpen(walletConfig) - await this.close() - } - - /** - * @throws {WalletDuplicateError} if the wallet already exists - * @throws {WalletError} if another error occurs - */ - public async createAndOpen(walletConfig: WalletConfig): Promise { - this.logger.debug(`Creating wallet '${walletConfig.id}' using SQLite storage`) - - try { - await this.indy.createWallet(this.walletStorageConfig(walletConfig), this.walletCredentials(walletConfig)) - this.walletConfig = walletConfig - - // We usually want to create master secret only once, therefore, we can to do so when creating a wallet. - await this.open(walletConfig) - - // We need to open wallet before creating master secret because we need wallet handle here. - await this.createMasterSecret(this.handle, this.masterSecretId) - } catch (error) { - // If an error ocurred while creating the master secret, we should close the wallet - if (this.isInitialized) await this.close() - - if (isIndyError(error, 'WalletAlreadyExistsError')) { - const errorMessage = `Wallet '${walletConfig.id}' already exists` - this.logger.debug(errorMessage) - - throw new WalletDuplicateError(errorMessage, { - walletType: 'IndyWallet', - cause: error, - }) - } else { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - const errorMessage = `Error creating wallet '${walletConfig.id}'` - this.logger.error(errorMessage, { - error, - errorMessage: error.message, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - - this.logger.debug(`Successfully created wallet '${walletConfig.id}'`) - } - - /** - * @throws {WalletNotFoundError} if the wallet does not exist - * @throws {WalletError} if another error occurs - */ - public async open(walletConfig: WalletConfig): Promise { - await this._open(walletConfig) - } - - /** - * @throws {WalletNotFoundError} if the wallet does not exist - * @throws {WalletError} if another error occurs - */ - public async rotateKey(walletConfig: WalletConfigRekey): Promise { - if (!walletConfig.rekey) { - throw new WalletError('Wallet rekey undefined!. Please specify the new wallet key') - } - await this._open( - { - id: walletConfig.id, - key: walletConfig.key, - keyDerivationMethod: walletConfig.keyDerivationMethod, - }, - walletConfig.rekey, - walletConfig.rekeyDerivationMethod - ) - } - - /** - * @throws {WalletNotFoundError} if the wallet does not exist - * @throws {WalletError} if another error occurs - */ - private async _open( - walletConfig: WalletConfig, - rekey?: string, - rekeyDerivation?: KeyDerivationMethod - ): Promise { - if (this.walletHandle) { - throw new WalletError( - 'Wallet instance already opened. Close the currently opened wallet before re-opening the wallet' - ) - } - - try { - this.walletHandle = await this.indy.openWallet( - this.walletStorageConfig(walletConfig), - this.walletCredentials(walletConfig, rekey, rekeyDerivation) - ) - if (rekey) { - this.walletConfig = { ...walletConfig, key: rekey, keyDerivationMethod: rekeyDerivation } - } else { - this.walletConfig = walletConfig - } - } catch (error) { - if (isIndyError(error, 'WalletNotFoundError')) { - const errorMessage = `Wallet '${walletConfig.id}' not found` - this.logger.debug(errorMessage) - - throw new WalletNotFoundError(errorMessage, { - walletType: 'IndyWallet', - cause: error, - }) - } else if (isIndyError(error, 'WalletAccessFailed')) { - const errorMessage = `Incorrect key for wallet '${walletConfig.id}'` - this.logger.debug(errorMessage) - throw new WalletInvalidKeyError(errorMessage, { - walletType: 'IndyWallet', - cause: error, - }) - } else { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - const errorMessage = `Error opening wallet '${walletConfig.id}': ${error.message}` - this.logger.error(errorMessage, { - error, - errorMessage: error.message, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - - this.logger.debug(`Wallet '${walletConfig.id}' opened with handle '${this.handle}'`) - } - - /** - * @throws {WalletNotFoundError} if the wallet does not exist - * @throws {WalletError} if another error occurs - */ - public async delete(): Promise { - if (!this.walletConfig) { - throw new WalletError( - 'Can not delete wallet that does not have wallet config set. Make sure to call create wallet before deleting the wallet' - ) - } - - this.logger.info(`Deleting wallet '${this.walletConfig.id}'`) - - if (this.walletHandle) { - await this.close() - } - - try { - await this.indy.deleteWallet( - this.walletStorageConfig(this.walletConfig), - this.walletCredentials(this.walletConfig) - ) - } catch (error) { - if (isIndyError(error, 'WalletNotFoundError')) { - const errorMessage = `Error deleting wallet: wallet '${this.walletConfig.id}' not found` - this.logger.debug(errorMessage) - - throw new WalletNotFoundError(errorMessage, { - walletType: 'IndyWallet', - cause: error, - }) - } else { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - const errorMessage = `Error deleting wallet '${this.walletConfig.id}': ${error.message}` - this.logger.error(errorMessage, { - error, - errorMessage: error.message, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - } - - public async export(exportConfig: WalletExportImportConfig) { - try { - this.logger.debug(`Exporting wallet ${this.walletConfig?.id} to path ${exportConfig.path}`) - await this.indy.exportWallet(this.handle, exportConfig) - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - const errorMessage = `Error exporting wallet: ${error.message}` - this.logger.error(errorMessage, { - error, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - - public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig) { - try { - this.logger.debug(`Importing wallet ${walletConfig.id} from path ${importConfig.path}`) - await this.indy.importWallet( - { id: walletConfig.id }, - { key: walletConfig.key, key_derivation_method: walletConfig.keyDerivationMethod }, - importConfig - ) - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - const errorMessage = `Error importing wallet': ${error.message}` - this.logger.error(errorMessage, { - error, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - - /** - * @throws {WalletError} if the wallet is already closed or another error occurs - */ - public async close(): Promise { - this.logger.debug(`Closing wallet ${this.walletConfig?.id}`) - if (!this.walletHandle) { - throw new WalletError('Wallet is in invalid state, you are trying to close wallet that has no `walletHandle`.') - } - - try { - await this.indy.closeWallet(this.walletHandle) - this.walletHandle = undefined - this.publicDidInfo = undefined - } catch (error) { - if (isIndyError(error, 'WalletInvalidHandle')) { - const errorMessage = `Error closing wallet: wallet already closed` - this.logger.debug(errorMessage) - - throw new WalletError(errorMessage, { - cause: error, - }) - } else { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - const errorMessage = `Error closing wallet': ${error.message}` - this.logger.error(errorMessage, { - error, - errorMessage: error.message, - }) - - throw new WalletError(errorMessage, { cause: error }) - } - } - } - - /** - * Create master secret with specified id in currently opened wallet. - * - * If a master secret by this id already exists in the current wallet, the method - * will return without doing anything. - * - * @throws {WalletError} if an error occurs - */ - private async createMasterSecret(walletHandle: number, masterSecretId: string): Promise { - this.logger.debug(`Creating master secret with id '${masterSecretId}' in wallet with handle '${walletHandle}'`) - - try { - await this.indy.proverCreateMasterSecret(walletHandle, masterSecretId) - - return masterSecretId - } catch (error) { - if (isIndyError(error, 'AnoncredsMasterSecretDuplicateNameError')) { - // master secret id is the same as the master secret id passed in the create function - // so if it already exists we can just assign it. - this.logger.debug( - `Master secret with id '${masterSecretId}' already exists in wallet with handle '${walletHandle}'`, - { - indyError: 'AnoncredsMasterSecretDuplicateNameError', - } - ) - - return masterSecretId - } else { - if (!isIndyError(error)) { - throw new AriesFrameworkError('Attempted to throw Indy error, but it was not an Indy error') - } - - this.logger.error(`Error creating master secret with id ${masterSecretId}`, { - indyError: error.indyName, - error, - }) - - throw new WalletError( - `Error creating master secret with id ${masterSecretId} in wallet with handle '${walletHandle}'`, - { cause: error } - ) - } - } - } - - /** - * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be - * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but - * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather - * use the `DidsModule`. - */ - public async initPublicDid(didConfig: DidConfig) { - // The Indy SDK cannot use a key to sign a request for the ledger. This is the only place where we need to call createDid - try { - const [did, verkey] = await this.indy.createAndStoreMyDid(this.handle, didConfig || {}) - - this.publicDidInfo = { - did, - verkey, - } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError('Error creating Did', { cause: error }) - } - } - - /** - * Create a key with an optional private key and keyType. - * The keypair is also automatically stored in the wallet afterwards - * - * Bls12381g1g2 and X25519 are not supported. - * - * @param privateKey Buffer Private key (formerly called 'seed') - * @param keyType KeyType the type of key that should be created - * - * @returns a Key instance with a publicKeyBase58 - * - * @throws {WalletError} When an unsupported keytype is requested - * @throws {WalletError} When the key could not be created - */ - public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { - try { - if (seed && privateKey) { - throw new AriesFrameworkError('Only one of seed and privateKey can be set') - } - - // Ed25519 is supported natively in Indy wallet - if (keyType === KeyType.Ed25519) { - if (seed) { - throw new AriesFrameworkError( - 'IndyWallet does not support seed. You may rather want to specify a private key for deterministic ed25519 key generation' - ) - } - - const verkey = await this.indy.createKey(this.handle, { - seed: privateKey?.toString(), - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - crypto_type: 'ed25519', - }) - return Key.fromPublicKeyBase58(verkey, keyType) - } - - // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) - - const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) - await this.storeKeyPair(keyPair) - return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) - } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) - } - - throw new WalletError(`Unsupported key type: '${keyType}' for wallet IndyWallet`) - } - - /** - * sign a Buffer with an instance of a Key class - * - * Bls12381g1g2, Bls12381g1 and X25519 are not supported. - * - * @param data Buffer The data that needs to be signed - * @param key Key The key that is used to sign the data - * - * @returns A signature for the data - */ - public async sign({ data, key }: WalletSignOptions): Promise { - try { - // Ed25519 is supported natively in Indy wallet - if (key.keyType === KeyType.Ed25519) { - // Checks to see if it is an not an Array of messages, but just a single one - if (!TypedArrayEncoder.isTypedArray(data)) { - throw new WalletError(`${KeyType.Ed25519} does not support multiple singing of multiple messages`) - } - return await this.indy.cryptoSign(this.handle, key.publicKeyBase58, data as Buffer) - } - - // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) - - const keyPair = await this.retrieveKeyPair(key.publicKeyBase58) - const signed = await signingKeyProvider.sign({ - data, - privateKeyBase58: keyPair.privateKeyBase58, - publicKeyBase58: key.publicKeyBase58, - }) - - return signed - } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}`, { cause: error }) - } - throw new WalletError(`Unsupported keyType: ${key.keyType}`) - } - - /** - * Verify the signature with the data and the used key - * - * Bls12381g1g2, Bls12381g1 and X25519 are not supported. - * - * @param data Buffer The data that has to be confirmed to be signed - * @param key Key The key that was used in the signing process - * @param signature Buffer The signature that was created by the signing process - * - * @returns A boolean whether the signature was created with the supplied data and key - * - * @throws {WalletError} When it could not do the verification - * @throws {WalletError} When an unsupported keytype is used - */ - public async verify({ data, key, signature }: WalletVerifyOptions): Promise { - try { - // Ed25519 is supported natively in Indy wallet - if (key.keyType === KeyType.Ed25519) { - // Checks to see if it is an not an Array of messages, but just a single one - if (!TypedArrayEncoder.isTypedArray(data)) { - throw new WalletError(`${KeyType.Ed25519} does not support multiple singing of multiple messages`) - } - return await this.indy.cryptoVerify(key.publicKeyBase58, data as Buffer, signature) - } - - // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) - - const signed = await signingKeyProvider.verify({ - data, - signature, - publicKeyBase58: key.publicKeyBase58, - }) - - return signed - } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError(`Error verifying signature of data signed with verkey ${key.publicKeyBase58}`, { - cause: error, - }) - } - throw new WalletError(`Unsupported keyType: ${key.keyType}`) - } - - public async pack( - payload: Record, - recipientKeys: string[], - senderVerkey?: string - ): Promise { - try { - const messageRaw = JsonEncoder.toBuffer(payload) - const packedMessage = await this.indy.packMessage(this.handle, messageRaw, recipientKeys, senderVerkey ?? null) - return JsonEncoder.fromBuffer(packedMessage) - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError('Error packing message', { cause: error }) - } - } - - public async unpack(messagePackage: EncryptedMessage): Promise { - try { - const unpackedMessageBuffer = await this.indy.unpackMessage(this.handle, JsonEncoder.toBuffer(messagePackage)) - const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer) - return { - senderKey: unpackedMessage.sender_verkey, - recipientKey: unpackedMessage.recipient_verkey, - plaintextMessage: JsonEncoder.fromString(unpackedMessage.message), - } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError('Error unpacking message', { cause: error }) - } - } - - public async generateNonce(): Promise { - try { - return await this.indy.generateNonce() - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error') - } - throw new WalletError('Error generating nonce', { cause: error }) - } - } - - private async retrieveKeyPair(publicKeyBase58: string): Promise { - try { - const { value } = await this.indy.getWalletRecord(this.handle, 'KeyPairRecord', `key-${publicKeyBase58}`, {}) - if (value) { - return JsonEncoder.fromString(value) as KeyPair - } else { - throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) - } - } catch (error) { - if (isIndyError(error, 'WalletItemNotFound')) { - throw new RecordNotFoundError(`KeyPairRecord not found for public key: ${publicKeyBase58}.`, { - recordType: 'KeyPairRecord', - cause: error, - }) - } - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - private async storeKeyPair(keyPair: KeyPair): Promise { - try { - await this.indy.addWalletRecord( - this.handle, - 'KeyPairRecord', - `key-${keyPair.publicKeyBase58}`, - JSON.stringify(keyPair), - { - keyType: keyPair.keyType, - } - ) - } catch (error) { - if (isIndyError(error, 'WalletItemAlreadyExists')) { - throw new RecordDuplicateError(`Record already exists`, { recordType: 'KeyPairRecord' }) - } - throw isIndyError(error) ? new IndySdkError(error) : error - } - } - - public async generateWalletKey() { - try { - return await this.indy.generateWalletKey() - } catch (error) { - throw new WalletError('Error generating wallet key', { cause: error }) - } - } -} diff --git a/packages/core/src/wallet/index.ts b/packages/core/src/wallet/index.ts index 6e19fc5d3c..e60dcfdb68 100644 --- a/packages/core/src/wallet/index.ts +++ b/packages/core/src/wallet/index.ts @@ -1,4 +1,3 @@ export * from './Wallet' -export * from './IndyWallet' export * from './WalletApi' export * from './WalletModule' diff --git a/packages/core/src/wallet/util/assertIndyWallet.ts b/packages/core/src/wallet/util/assertIndyWallet.ts deleted file mode 100644 index a26c43f0fe..0000000000 --- a/packages/core/src/wallet/util/assertIndyWallet.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Wallet } from '../Wallet' - -import { AriesFrameworkError } from '../../error' -import { IndyWallet } from '../IndyWallet' - -export function assertIndyWallet(wallet: Wallet): asserts wallet is IndyWallet { - if (!(wallet instanceof IndyWallet)) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const walletClassName = (wallet as any).constructor?.name ?? 'unknown' - throw new AriesFrameworkError(`Expected wallet to be instance of IndyWallet, found ${walletClassName}`) - } -} diff --git a/packages/core/tests/agents.test.ts b/packages/core/tests/agents.test.ts index 47393e371b..9bded8ba18 100644 --- a/packages/core/tests/agents.test.ts +++ b/packages/core/tests/agents.test.ts @@ -1,22 +1,27 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { ConnectionRecord } from '../src/modules/connections' -import { Subject } from 'rxjs' - -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { HandshakeProtocol } from '../src/modules/connections' import { waitForBasicMessage, getAgentOptions } from './helpers' - -const aliceAgentOptions = getAgentOptions('Agents Alice', { - endpoints: ['rxjs:alice'], -}) -const bobAgentOptions = getAgentOptions('Agents Bob', { - endpoints: ['rxjs:bob'], -}) +import { setupSubjectTransports } from './transport' + +const aliceAgentOptions = getAgentOptions( + 'Agents Alice', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) +const bobAgentOptions = getAgentOptions( + 'Agents Bob', + { + endpoints: ['rxjs:bob'], + }, + getIndySdkModules() +) describe('agents', () => { let aliceAgent: Agent @@ -32,22 +37,12 @@ describe('agents', () => { }) test('make a connection between agents', async () => { - const aliceMessages = new Subject() - const bobMessages = new Subject() + aliceAgent = new Agent(aliceAgentOptions) + bobAgent = new Agent(bobAgentOptions) - const subjectMap = { - 'rxjs:alice': aliceMessages, - 'rxjs:bob': bobMessages, - } + setupSubjectTransports([aliceAgent, bobAgent]) - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - - bobAgent = new Agent(bobAgentOptions) - bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) - bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await bobAgent.initialize() const aliceBobOutOfBandRecord = await aliceAgent.oob.createInvitation({ diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 24c03ad907..2525879598 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -1,11 +1,9 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { AgentMessageProcessedEvent, KeylistUpdate } from '../src' -import { filter, firstValueFrom, map, Subject, timeout } from 'rxjs' +import { filter, firstValueFrom, map, timeout } from 'rxjs' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Key, AgentEventTypes, @@ -19,6 +17,7 @@ import { didKeyToVerkey } from '../src/modules/dids/helpers' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { getAgentOptions, waitForTrustPingResponseReceivedEvent } from './helpers' +import { setupSubjectTransports } from './transport' describe('connections', () => { let faberAgent: Agent @@ -27,50 +26,46 @@ describe('connections', () => { let mediatorAgent: Agent beforeEach(async () => { - const faberAgentOptions = getAgentOptions('Faber Agent Connections', { - endpoints: ['rxjs:faber'], - }) - const aliceAgentOptions = getAgentOptions('Alice Agent Connections', { - endpoints: ['rxjs:alice'], - }) - const acmeAgentOptions = getAgentOptions('Acme Agent Connections', { - endpoints: ['rxjs:acme'], - }) - const mediatorAgentOptions = getAgentOptions('Mediator Agent Connections', { - endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, - }) + const faberAgentOptions = getAgentOptions( + 'Faber Agent Connections', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() + ) + const aliceAgentOptions = getAgentOptions( + 'Alice Agent Connections', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() + ) + const acmeAgentOptions = getAgentOptions( + 'Acme Agent Connections', + { + endpoints: ['rxjs:acme'], + }, + getIndySdkModules() + ) + const mediatorAgentOptions = getAgentOptions( + 'Mediator Agent Connections', + { + endpoints: ['rxjs:mediator'], + autoAcceptMediationRequests: true, + }, + getIndySdkModules() + ) - const faberMessages = new Subject() - const aliceMessages = new Subject() - const acmeMessages = new Subject() - const mediatorMessages = new Subject() + faberAgent = new Agent(faberAgentOptions) + aliceAgent = new Agent(aliceAgentOptions) + acmeAgent = new Agent(acmeAgentOptions) + mediatorAgent = new Agent(mediatorAgentOptions) - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - 'rxjs:acme': acmeMessages, - 'rxjs:mediator': mediatorMessages, - } + setupSubjectTransports([faberAgent, aliceAgent, acmeAgent, mediatorAgent]) - faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await faberAgent.initialize() - - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - - acmeAgent = new Agent(acmeAgentOptions) - acmeAgent.registerInboundTransport(new SubjectInboundTransport(acmeMessages)) - acmeAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await acmeAgent.initialize() - - mediatorAgent = new Agent(mediatorAgentOptions) - mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) - mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await mediatorAgent.initialize() }) diff --git a/packages/core/tests/events.ts b/packages/core/tests/events.ts new file mode 100644 index 0000000000..e48f689f1e --- /dev/null +++ b/packages/core/tests/events.ts @@ -0,0 +1,21 @@ +import type { Agent, BaseEvent } from '../src' + +import { ReplaySubject } from 'rxjs' + +export type EventReplaySubject = ReplaySubject + +export function setupEventReplaySubjects(agents: Agent[], eventTypes: string[]): ReplaySubject[] { + const replaySubjects: EventReplaySubject[] = [] + + for (const agent of agents) { + const replaySubject = new ReplaySubject() + + for (const eventType of eventTypes) { + agent.events.observable(eventType).subscribe(replaySubject) + } + + replaySubjects.push(replaySubject) + } + + return replaySubjects +} diff --git a/packages/core/tests/generic-records.test.ts b/packages/core/tests/generic-records.test.ts index 627fcb6540..3d37def0ed 100644 --- a/packages/core/tests/generic-records.test.ts +++ b/packages/core/tests/generic-records.test.ts @@ -1,13 +1,18 @@ import type { GenericRecord } from '../src/modules/generic-records/repository/GenericRecord' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { RecordNotFoundError } from '../src/error' import { getAgentOptions } from './helpers' -const aliceAgentOptions = getAgentOptions('Generic Records Alice', { - endpoints: ['rxjs:alice'], -}) +const aliceAgentOptions = getAgentOptions( + 'Generic Records Alice', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) describe('genericRecords', () => { let aliceAgent: Agent diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index e8fd1f8a29..1ae3f39709 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -1,76 +1,52 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { - AcceptCredentialOfferOptions, AgentDependencies, + BaseEvent, BasicMessage, BasicMessageStateChangedEvent, ConnectionRecordProps, - CredentialDefinitionTemplate, CredentialStateChangedEvent, InitConfig, InjectionToken, ProofStateChangedEvent, - SchemaTemplate, Wallet, + Agent, + CredentialState, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' -import type { IndyOfferCredentialFormat } from '../src/modules/credentials/formats/indy/IndyCredentialFormat' -import type { ProofAttributeInfo, ProofPredicateInfoOptions } from '../src/modules/proofs/formats/indy/models' -import type { AutoAcceptProof } from '../src/modules/proofs/models/ProofAutoAcceptType' -import type { Awaited, WalletConfig } from '../src/types' -import type { CredDef, Schema } from 'indy-sdk' +import type { ProofState } from '../src/modules/proofs/models/ProofState' +import type { WalletConfig } from '../src/types' import type { Observable } from 'rxjs' import { readFileSync } from 'fs' import path from 'path' -import { firstValueFrom, ReplaySubject, Subject } from 'rxjs' -import { catchError, filter, map, timeout } from 'rxjs/operators' +import { lastValueFrom, firstValueFrom, ReplaySubject } from 'rxjs' +import { catchError, filter, map, take, timeout } from 'rxjs/operators' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { BbsModule } from '../../bbs-signatures/src/BbsModule' -import { agentDependencies, WalletScheme } from '../../node/src' +import { agentDependencies, IndySdkPostgresWalletScheme } from '../../node/src' import { - CredentialsModule, - IndyCredentialFormatService, - JsonLdCredentialFormatService, - V1CredentialProtocol, - V2CredentialProtocol, - W3cVcModule, - Agent, AgentConfig, AgentContext, - AriesFrameworkError, BasicMessageEventTypes, ConnectionRecord, CredentialEventTypes, - CredentialState, - TrustPingEventTypes, DependencyManager, DidExchangeRole, DidExchangeState, HandshakeProtocol, InjectionSymbols, ProofEventTypes, + TrustPingEventTypes, } from '../src' import { Key, KeyType } from '../src/crypto' -import { Attachment, AttachmentData } from '../src/decorators/attachment/Attachment' -import { AutoAcceptCredential } from '../src/modules/credentials/models/CredentialAutoAcceptType' -import { V1CredentialPreview } from '../src/modules/credentials/protocol/v1/messages/V1CredentialPreview' import { DidCommV1Service } from '../src/modules/dids' import { DidKey } from '../src/modules/dids/methods/key' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' import { OutOfBandInvitation } from '../src/modules/oob/messages' import { OutOfBandRecord } from '../src/modules/oob/repository' -import { PredicateType } from '../src/modules/proofs/formats/indy/models' -import { ProofState } from '../src/modules/proofs/models/ProofState' -import { V1PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' -import { customDocumentLoader } from '../src/modules/vc/__tests__/documentLoader' import { KeyDerivationMethod } from '../src/types' -import { LinkedAttachment } from '../src/utils/LinkedAttachment' import { uuid } from '../src/utils/uuid' import testLogger, { TestLogger } from './logger' @@ -82,8 +58,8 @@ export const genesisPath = process.env.GENESIS_TXN_PATH export const genesisTransactions = readFileSync(genesisPath).toString('utf-8') export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9' -const taaVersion = (process.env.TEST_AGENT_TAA_VERSION ?? '1') as `${number}.${number}` | `${number}` -const taaAcceptanceMechanism = process.env.TEST_AGENT_TAA_ACCEPTANCE_MECHANISM ?? 'accept' +export const taaVersion = (process.env.TEST_AGENT_TAA_VERSION ?? '1') as `${number}.${number}` | `${number}` +export const taaAcceptanceMechanism = process.env.TEST_AGENT_TAA_ACCEPTANCE_MECHANISM ?? 'accept' export { agentDependencies } export function getAgentOptions( @@ -91,25 +67,16 @@ export function getAgentOptions = {}, modules?: AgentModules ): { config: InitConfig; modules: AgentModules; dependencies: AgentDependencies } { + const random = uuid().slice(0, 4) const config: InitConfig = { - label: `Agent: ${name}`, + label: `Agent: ${name} - ${random}`, walletConfig: { - id: `Wallet: ${name}`, + id: `Wallet: ${name} - ${random}`, key: 'DZ9hPqFWTPxemcGea72C1X1nusqk5wFNLq6QPjwXGqAa', // generated using indy.generateWalletKey keyDerivationMethod: KeyDerivationMethod.Raw, }, publicDidSeed, autoAcceptConnections: true, - connectToIndyLedgersOnStartup: false, - indyLedgers: [ - { - id: `pool-${name}`, - isProduction: false, - genesisPath, - indyNamespace: `pool:localtest`, - transactionAuthorAgreement: { version: taaVersion, acceptanceMechanism: taaAcceptanceMechanism }, - }, - ], // TODO: determine the log level based on an environment variable. This will make it // possible to run e.g. failed github actions in debug mode for extra logs logger: TestLogger.fromLogger(testLogger, name), @@ -119,17 +86,22 @@ export function getAgentOptions = {}) { +export function getPostgresAgentOptions( + name: string, + extraConfig: Partial = {}, + modules?: AgentModules +) { const config: InitConfig = { label: `Agent: ${name}`, walletConfig: { - id: `Wallet${name}`, + // NOTE: IndySDK Postgres database per wallet doesn't support special characters/spaces in the wallet name + id: `PostGresWallet${name}`, key: `Key${name}`, storage: { type: 'postgres_storage', config: { url: 'localhost:5432', - wallet_scheme: WalletScheme.DatabasePerWallet, + wallet_scheme: IndySdkPostgresWalletScheme.DatabasePerWallet, }, credentials: { account: 'postgres', @@ -142,19 +114,11 @@ export function getPostgresAgentOptions(name: string, extraConfig: Partial + e.type === ProofEventTypes.ProofStateChanged +const isCredentialStateChangedEvent = (e: BaseEvent): e is CredentialStateChangedEvent => + e.type === CredentialEventTypes.CredentialStateChanged +const isTrustPingReceivedEvent = (e: BaseEvent): e is TrustPingReceivedEvent => + e.type === TrustPingEventTypes.TrustPingReceivedEvent +const isTrustPingResponseReceivedEvent = (e: BaseEvent): e is TrustPingResponseReceivedEvent => + e.type === TrustPingEventTypes.TrustPingResponseReceivedEvent + export function waitForProofExchangeRecordSubject( - subject: ReplaySubject | Observable, + subject: ReplaySubject | Observable, { threadId, parentThreadId, state, previousState, timeoutMs = 10000, + count = 1, }: { threadId?: string parentThreadId?: string state?: ProofState previousState?: ProofState | null timeoutMs?: number + count?: number } ) { - const observable: Observable = - subject instanceof ReplaySubject ? subject.asObservable() : subject - return firstValueFrom( + const observable: Observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + return lastValueFrom( observable.pipe( + filter(isProofStateChangedEvent), filter((e) => previousState === undefined || e.payload.previousState === previousState), filter((e) => threadId === undefined || e.payload.proofRecord.threadId === threadId), filter((e) => parentThreadId === undefined || e.payload.proofRecord.parentThreadId === parentThreadId), @@ -234,13 +209,14 @@ export function waitForProofExchangeRecordSubject( catchError(() => { throw new Error( `ProofStateChangedEvent event not emitted within specified timeout: ${timeoutMs} - previousState: ${previousState}, - threadId: ${threadId}, - parentThreadId: ${parentThreadId}, - state: ${state} -}` + previousState: ${previousState}, + threadId: ${threadId}, + parentThreadId: ${parentThreadId}, + state: ${state} + }` ) }), + take(count), map((e) => e.payload.proofRecord) ) ) @@ -259,7 +235,7 @@ export async function waitForTrustPingReceivedEvent( } export function waitForTrustPingReceivedEventSubject( - subject: ReplaySubject | Observable, + subject: ReplaySubject | Observable, { threadId, timeoutMs = 10000, @@ -271,6 +247,7 @@ export function waitForTrustPingReceivedEventSubject( const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject return firstValueFrom( observable.pipe( + filter(isTrustPingReceivedEvent), filter((e) => threadId === undefined || e.payload.message.threadId === threadId), timeout(timeoutMs), catchError(() => { @@ -300,7 +277,7 @@ export async function waitForTrustPingResponseReceivedEvent( } export function waitForTrustPingResponseReceivedEventSubject( - subject: ReplaySubject | Observable, + subject: ReplaySubject | Observable, { threadId, timeoutMs = 10000, @@ -312,6 +289,7 @@ export function waitForTrustPingResponseReceivedEventSubject( const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject return firstValueFrom( observable.pipe( + filter(isTrustPingResponseReceivedEvent), filter((e) => threadId === undefined || e.payload.message.threadId === threadId), timeout(timeoutMs), catchError(() => { @@ -327,7 +305,7 @@ export function waitForTrustPingResponseReceivedEventSubject( } export function waitForCredentialRecordSubject( - subject: ReplaySubject | Observable, + subject: ReplaySubject | Observable, { threadId, state, @@ -344,6 +322,7 @@ export function waitForCredentialRecordSubject( return firstValueFrom( observable.pipe( + filter(isCredentialStateChangedEvent), filter((e) => previousState === undefined || e.payload.previousState === previousState), filter((e) => threadId === undefined || e.payload.credentialRecord.threadId === threadId), filter((e) => state === undefined || e.payload.credentialRecord.state === state), @@ -478,129 +457,6 @@ export async function makeConnection(agentA: Agent, agentB: Agent) { return [agentAConnection, agentBConnection] } -export async function registerSchema(agent: Agent, schemaTemplate: SchemaTemplate): Promise { - const schema = await agent.ledger.registerSchema(schemaTemplate) - testLogger.test(`created schema with id ${schema.id}`, schema) - return schema -} - -export async function registerDefinition( - agent: Agent, - definitionTemplate: CredentialDefinitionTemplate -): Promise { - const credentialDefinition = await agent.ledger.registerCredentialDefinition(definitionTemplate) - testLogger.test(`created credential definition with id ${credentialDefinition.id}`, credentialDefinition) - return credentialDefinition -} - -export async function prepareForIssuance(agent: Agent, attributes: string[]) { - const publicDid = agent.publicDid?.did - - if (!publicDid) { - throw new AriesFrameworkError('No public did') - } - - await ensurePublicDidIsOnLedger(agent, publicDid) - - const schema = await registerSchema(agent, { - attributes, - name: `schema-${uuid()}`, - version: '1.0', - }) - - const definition = await registerDefinition(agent, { - schema, - signatureType: 'CL', - supportRevocation: false, - tag: 'default', - }) - - return { - schema, - definition, - publicDid, - } -} - -export async function ensurePublicDidIsOnLedger(agent: Agent, publicDid: string) { - try { - testLogger.test(`Ensure test DID ${publicDid} is written to ledger`) - await agent.ledger.getPublicDid(publicDid) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { - // Unfortunately, this won't prevent from the test suite running because of Jest runner runs all tests - // regardless of thrown errors. We're more explicit about the problem with this error handling. - throw new Error(`Test DID ${publicDid} is not written on ledger or ledger is not available: ${error.message}`) - } -} - -/** - * Assumes that the autoAcceptCredential is set to {@link AutoAcceptCredential.ContentApproved} - */ -export async function issueCredential({ - issuerAgent, - issuerConnectionId, - holderAgent, - credentialTemplate, -}: { - issuerAgent: Agent - issuerConnectionId: string - holderAgent: Agent - credentialTemplate: IndyOfferCredentialFormat -}) { - const issuerReplay = new ReplaySubject() - const holderReplay = new ReplaySubject() - - issuerAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(issuerReplay) - holderAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(holderReplay) - - let issuerCredentialRecord = await issuerAgent.credentials.offerCredential({ - comment: 'some comment about credential', - connectionId: issuerConnectionId, - protocolVersion: 'v1', - credentialFormats: { - indy: { - attributes: credentialTemplate.attributes, - credentialDefinitionId: credentialTemplate.credentialDefinitionId, - linkedAttachments: credentialTemplate.linkedAttachments, - }, - }, - autoAcceptCredential: AutoAcceptCredential.ContentApproved, - }) - - let holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { - threadId: issuerCredentialRecord.threadId, - state: CredentialState.OfferReceived, - }) - - const acceptOfferOptions: AcceptCredentialOfferOptions = { - credentialRecordId: holderCredentialRecord.id, - autoAcceptCredential: AutoAcceptCredential.ContentApproved, - } - - await holderAgent.credentials.acceptOffer(acceptOfferOptions) - - // Because we use auto-accept it can take a while to have the whole credential flow finished - // Both parties need to interact with the ledger and sign/verify the credential - holderCredentialRecord = await waitForCredentialRecordSubject(holderReplay, { - threadId: issuerCredentialRecord.threadId, - state: CredentialState.Done, - }) - issuerCredentialRecord = await waitForCredentialRecordSubject(issuerReplay, { - threadId: issuerCredentialRecord.threadId, - state: CredentialState.Done, - }) - - return { - issuerCredential: issuerCredentialRecord, - holderCredential: holderCredentialRecord, - } -} - /** * Returns mock of function with correct type annotations according to original function `fn`. * It can be used also for class methods. @@ -620,266 +476,3 @@ export function mockFunction any>(fn: T): jest.Moc export function mockProperty(object: T, property: K, value: T[K]) { Object.defineProperty(object, property, { get: () => value }) } - -export async function presentProof({ - verifierAgent, - verifierConnectionId, - holderAgent, - presentationTemplate: { attributes, predicates }, -}: { - verifierAgent: Agent - verifierConnectionId: string - holderAgent: Agent - presentationTemplate: { - attributes?: Record - predicates?: Record - } -}) { - const verifierReplay = new ReplaySubject() - const holderReplay = new ReplaySubject() - - verifierAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(verifierReplay) - holderAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(holderReplay) - - let holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { - state: ProofState.RequestReceived, - }) - - let verifierRecord = await verifierAgent.proofs.requestProof({ - connectionId: verifierConnectionId, - proofFormats: { - indy: { - name: 'test-proof-request', - requestedAttributes: attributes, - requestedPredicates: predicates, - version: '1.0', - }, - }, - protocolVersion: 'v2', - }) - - let holderRecord = await holderProofExchangeRecordPromise - - const requestedCredentials = await holderAgent.proofs.selectCredentialsForRequest({ - proofRecordId: holderRecord.id, - }) - - const verifierProofExchangeRecordPromise = waitForProofExchangeRecordSubject(verifierReplay, { - threadId: holderRecord.threadId, - state: ProofState.PresentationReceived, - }) - - await holderAgent.proofs.acceptRequest({ - proofRecordId: holderRecord.id, - proofFormats: { indy: requestedCredentials.proofFormats.indy }, - }) - - verifierRecord = await verifierProofExchangeRecordPromise - - // assert presentation is valid - expect(verifierRecord.isVerified).toBe(true) - - holderProofExchangeRecordPromise = waitForProofExchangeRecordSubject(holderReplay, { - threadId: holderRecord.threadId, - state: ProofState.Done, - }) - - verifierRecord = await verifierAgent.proofs.acceptPresentation({ proofRecordId: verifierRecord.id }) - holderRecord = await holderProofExchangeRecordPromise - - return { - verifierProof: verifierRecord, - holderProof: holderRecord, - } -} - -// Helper type to get the type of the agents (with the custom modules) for the credential tests -export type CredentialTestsAgent = Awaited>['aliceAgent'] -export async function setupCredentialTests( - faberName: string, - aliceName: string, - autoAcceptCredentials?: AutoAcceptCredential -) { - const faberMessages = new Subject() - const aliceMessages = new Subject() - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - - const indyCredentialFormat = new IndyCredentialFormatService() - const jsonLdCredentialFormat = new JsonLdCredentialFormatService() - - // TODO remove the dependency on BbsModule - const modules = { - bbs: new BbsModule(), - - // Initialize custom credentials module (with jsonLdCredentialFormat enabled) - credentials: new CredentialsModule({ - autoAcceptCredentials, - credentialProtocols: [ - new V1CredentialProtocol({ indyCredentialFormat }), - new V2CredentialProtocol({ - credentialFormats: [indyCredentialFormat, jsonLdCredentialFormat], - }), - ], - }), - // Register custom w3cVc module so we can define the test document loader - w3cVc: new W3cVcModule({ - documentLoader: customDocumentLoader, - }), - } - const faberAgentOptions = getAgentOptions( - faberName, - { - endpoints: ['rxjs:faber'], - }, - modules - ) - - const aliceAgentOptions = getAgentOptions( - aliceName, - { - endpoints: ['rxjs:alice'], - }, - modules - ) - const faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - - const aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await aliceAgent.initialize() - - const { - schema, - definition: { id: credDefId }, - } = await prepareForIssuance(faberAgent, ['name', 'age', 'profile_picture', 'x-ray']) - - const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) - - const faberReplay = new ReplaySubject() - const aliceReplay = new ReplaySubject() - - faberAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(faberReplay) - aliceAgent.events - .observable(CredentialEventTypes.CredentialStateChanged) - .subscribe(aliceReplay) - - return { faberAgent, aliceAgent, credDefId, schema, faberConnection, aliceConnection, faberReplay, aliceReplay } -} - -export async function setupProofsTest(faberName: string, aliceName: string, autoAcceptProofs?: AutoAcceptProof) { - const credentialPreview = V1CredentialPreview.fromRecord({ - name: 'John', - age: '99', - }) - - const unique = uuid().substring(0, 4) - - const faberAgentOptions = getAgentOptions(`${faberName} - ${unique}`, { - autoAcceptProofs, - endpoints: ['rxjs:faber'], - }) - - const aliceAgentOptions = getAgentOptions(`${aliceName} - ${unique}`, { - autoAcceptProofs, - endpoints: ['rxjs:alice'], - }) - - const faberMessages = new Subject() - const aliceMessages = new Subject() - - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - } - const faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await faberAgent.initialize() - - const aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await aliceAgent.initialize() - - const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) - - const [agentAConnection, agentBConnection] = await makeConnection(faberAgent, aliceAgent) - expect(agentAConnection.isReady).toBe(true) - expect(agentBConnection.isReady).toBe(true) - - const faberConnection = agentAConnection - const aliceConnection = agentBConnection - - const presentationPreview = new V1PresentationPreview({ - attributes: [ - { - name: 'name', - credentialDefinitionId: definition.id, - referent: '0', - value: 'John', - }, - { - name: 'image_0', - credentialDefinitionId: definition.id, - }, - ], - predicates: [ - { - name: 'age', - credentialDefinitionId: definition.id, - predicate: PredicateType.GreaterThanOrEqualTo, - threshold: 50, - }, - ], - }) - - await issueCredential({ - issuerAgent: faberAgent, - issuerConnectionId: faberConnection.id, - holderAgent: aliceAgent, - credentialTemplate: { - credentialDefinitionId: definition.id, - attributes: credentialPreview.attributes, - linkedAttachments: [ - new LinkedAttachment({ - name: 'image_0', - attachment: new Attachment({ - filename: 'picture-of-a-cat.png', - data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), - }), - }), - new LinkedAttachment({ - name: 'image_1', - attachment: new Attachment({ - filename: 'picture-of-a-dog.png', - data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), - }), - }), - ], - }, - }) - const faberReplay = new ReplaySubject() - const aliceReplay = new ReplaySubject() - - faberAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(faberReplay) - aliceAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(aliceReplay) - - return { - faberAgent, - aliceAgent, - credDefId: definition.id, - faberConnection, - aliceConnection, - presentationPreview, - faberReplay, - aliceReplay, - } -} diff --git a/packages/core/tests/index.ts b/packages/core/tests/index.ts new file mode 100644 index 0000000000..2822fb23e1 --- /dev/null +++ b/packages/core/tests/index.ts @@ -0,0 +1,9 @@ +export * from './jsonld' +export * from './transport' +export * from './events' +export * from './helpers' +export * from './indySdk' + +import testLogger from './logger' + +export { testLogger } diff --git a/packages/core/tests/indySdk.ts b/packages/core/tests/indySdk.ts new file mode 100644 index 0000000000..b5e5a3075d --- /dev/null +++ b/packages/core/tests/indySdk.ts @@ -0,0 +1,3 @@ +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' + +export { indySdk } diff --git a/packages/core/tests/jsonld.ts b/packages/core/tests/jsonld.ts new file mode 100644 index 0000000000..354599cfb3 --- /dev/null +++ b/packages/core/tests/jsonld.ts @@ -0,0 +1,171 @@ +import type { EventReplaySubject } from './events' +import type { AutoAcceptCredential, AutoAcceptProof, ConnectionRecord } from '../src' + +import { BbsModule } from '../../bbs-signatures/src/BbsModule' +import { IndySdkModule } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { + CacheModule, + CredentialEventTypes, + InMemoryLruCache, + ProofEventTypes, + Agent, + ProofsModule, + CredentialsModule, + JsonLdCredentialFormatService, + V2CredentialProtocol, + W3cVcModule, +} from '../src' +import { customDocumentLoader } from '../src/modules/vc/__tests__/documentLoader' + +import { setupEventReplaySubjects } from './events' +import { getAgentOptions, makeConnection } from './helpers' +import { setupSubjectTransports } from './transport' + +export type JsonLdTestsAgent = Agent> + +export const getJsonLdModules = ({ + autoAcceptCredentials, + autoAcceptProofs, +}: { autoAcceptCredentials?: AutoAcceptCredential; autoAcceptProofs?: AutoAcceptProof } = {}) => + ({ + credentials: new CredentialsModule({ + credentialProtocols: [new V2CredentialProtocol({ credentialFormats: [new JsonLdCredentialFormatService()] })], + autoAcceptCredentials, + }), + w3cVc: new W3cVcModule({ + documentLoader: customDocumentLoader, + }), + proofs: new ProofsModule({ + autoAcceptProofs, + }), + cache: new CacheModule({ + cache: new InMemoryLruCache({ limit: 100 }), + }), + indySdk: new IndySdkModule({ + indySdk, + }), + bbs: new BbsModule(), + } as const) + +interface SetupJsonLdTestsReturn { + issuerAgent: JsonLdTestsAgent + issuerReplay: EventReplaySubject + + holderAgent: JsonLdTestsAgent + holderReplay: EventReplaySubject + + issuerHolderConnectionId: CreateConnections extends true ? string : undefined + holderIssuerConnectionId: CreateConnections extends true ? string : undefined + + verifierHolderConnectionId: CreateConnections extends true + ? VerifierName extends string + ? string + : undefined + : undefined + holderVerifierConnectionId: CreateConnections extends true + ? VerifierName extends string + ? string + : undefined + : undefined + + verifierAgent: VerifierName extends string ? JsonLdTestsAgent : undefined + verifierReplay: VerifierName extends string ? EventReplaySubject : undefined + + credentialDefinitionId: string +} + +export async function setupJsonLdTests< + VerifierName extends string | undefined = undefined, + CreateConnections extends boolean = true +>({ + issuerName, + holderName, + verifierName, + autoAcceptCredentials, + autoAcceptProofs, + createConnections, +}: { + issuerName: string + holderName: string + verifierName?: VerifierName + autoAcceptCredentials?: AutoAcceptCredential + autoAcceptProofs?: AutoAcceptProof + createConnections?: CreateConnections +}): Promise> { + const modules = getJsonLdModules({ + autoAcceptCredentials, + autoAcceptProofs, + }) + + const issuerAgent = new Agent( + getAgentOptions( + issuerName, + { + endpoints: ['rxjs:issuer'], + }, + modules + ) + ) + + const holderAgent = new Agent( + getAgentOptions( + holderName, + { + endpoints: ['rxjs:holder'], + }, + modules + ) + ) + + const verifierAgent = verifierName + ? new Agent( + getAgentOptions( + verifierName, + { + endpoints: ['rxjs:verifier'], + }, + modules + ) + ) + : undefined + + setupSubjectTransports(verifierAgent ? [issuerAgent, holderAgent, verifierAgent] : [issuerAgent, holderAgent]) + const [issuerReplay, holderReplay, verifierReplay] = setupEventReplaySubjects( + verifierAgent ? [issuerAgent, holderAgent, verifierAgent] : [issuerAgent, holderAgent], + [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + ) + + await issuerAgent.initialize() + await holderAgent.initialize() + if (verifierAgent) await verifierAgent.initialize() + + let issuerHolderConnection: ConnectionRecord | undefined + let holderIssuerConnection: ConnectionRecord | undefined + let verifierHolderConnection: ConnectionRecord | undefined + let holderVerifierConnection: ConnectionRecord | undefined + + if (createConnections ?? true) { + ;[issuerHolderConnection, holderIssuerConnection] = await makeConnection(issuerAgent, holderAgent) + + if (verifierAgent) { + ;[holderVerifierConnection, verifierHolderConnection] = await makeConnection(holderAgent, verifierAgent) + } + } + + return { + issuerAgent, + issuerReplay, + + holderAgent, + holderReplay, + + verifierAgent: verifierName ? verifierAgent : undefined, + verifierReplay: verifierName ? verifierReplay : undefined, + + issuerHolderConnectionId: issuerHolderConnection?.id, + holderIssuerConnectionId: holderIssuerConnection?.id, + holderVerifierConnectionId: holderVerifierConnection?.id, + verifierHolderConnectionId: verifierHolderConnection?.id, + } as unknown as SetupJsonLdTestsReturn +} diff --git a/packages/core/tests/ledger.test.ts b/packages/core/tests/ledger.test.ts deleted file mode 100644 index 9d3411e54d..0000000000 --- a/packages/core/tests/ledger.test.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { promises } from 'fs' -import * as indy from 'indy-sdk' - -import { KeyType } from '../src' -import { Agent } from '../src/agent/Agent' -import { - DID_IDENTIFIER_REGEX, - indyDidFromPublicKeyBase58, - isAbbreviatedVerkey, - isFullVerkey, - VERKEY_REGEX, -} from '../src/utils/did' -import { sleep } from '../src/utils/sleep' - -import { genesisPath, getAgentOptions } from './helpers' -import testLogger from './logger' - -describe('ledger', () => { - let faberAgent: Agent - let schemaId: indy.SchemaId - - beforeAll(async () => { - faberAgent = new Agent(getAgentOptions('Faber Ledger')) - await faberAgent.initialize() - }) - - afterAll(async () => { - await faberAgent.shutdown() - await faberAgent.wallet.delete() - }) - - test(`initialization of agent's public DID`, async () => { - const publicDid = faberAgent.publicDid - testLogger.test('faberAgentPublicDid', publicDid) - - expect(publicDid).toEqual( - expect.objectContaining({ - did: expect.stringMatching(DID_IDENTIFIER_REGEX), - verkey: expect.stringMatching(VERKEY_REGEX), - }) - ) - }) - - test('get public DID from ledger', async () => { - if (!faberAgent.publicDid) { - throw new Error('Agent does not have public did.') - } - - const result = await faberAgent.ledger.getPublicDid(faberAgent.publicDid.did) - - let { verkey } = faberAgent.publicDid - // Agent’s public did stored locally in Indy wallet and created from public did seed during - // its initialization always returns full verkey. Therefore we need to align that here. - if (isFullVerkey(verkey) && isAbbreviatedVerkey(result.verkey)) { - verkey = await indy.abbreviateVerkey(faberAgent.publicDid.did, verkey) - } - - expect(result).toEqual( - expect.objectContaining({ - did: faberAgent.publicDid.did, - verkey: verkey, - role: '0', - }) - ) - }) - - test('register public DID on ledger', async () => { - if (!faberAgent.publicDid) { - throw new Error('Agent does not have public did.') - } - - const faberWallet = faberAgent.context.wallet - const key = await faberWallet.createKey({ keyType: KeyType.Ed25519 }) - const did = indyDidFromPublicKeyBase58(key.publicKeyBase58) - - const result = await faberAgent.ledger.registerPublicDid(did, key.publicKeyBase58, 'alias', 'TRUST_ANCHOR') - - expect(result).toEqual(did) - }) - - test('register schema on ledger', async () => { - if (!faberAgent.publicDid) { - throw new Error('Agent does not have public did.') - } - - const schemaName = `test-schema-${Date.now()}` - const schemaTemplate = { - name: schemaName, - attributes: ['name', 'age'], - version: '1.0', - } - - const schema = await faberAgent.ledger.registerSchema(schemaTemplate) - schemaId = schema.id - - await sleep(2000) - - const ledgerSchema = await faberAgent.ledger.getSchema(schemaId) - - expect(schemaId).toBe(`${faberAgent.publicDid.did}:2:${schemaName}:1.0`) - - expect(ledgerSchema).toEqual( - expect.objectContaining({ - attrNames: expect.arrayContaining(schemaTemplate.attributes), - id: `${faberAgent.publicDid.did}:2:${schemaName}:1.0`, - name: schemaName, - seqNo: schema.seqNo, - ver: schemaTemplate.version, - version: schemaTemplate.version, - }) - ) - }) - - test('register definition on ledger', async () => { - if (!faberAgent.publicDid) { - throw new Error('Agent does not have public did.') - } - const schema = await faberAgent.ledger.getSchema(schemaId) - const credentialDefinitionTemplate = { - schema: schema, - tag: 'TAG', - signatureType: 'CL' as const, - supportRevocation: true, - } - - const credentialDefinition = await faberAgent.ledger.registerCredentialDefinition(credentialDefinitionTemplate) - - await sleep(2000) - - const ledgerCredDef = await faberAgent.ledger.getCredentialDefinition(credentialDefinition.id) - - const credDefIdRegExp = new RegExp(`${faberAgent.publicDid.did}:3:CL:[0-9]+:TAG`) - expect(ledgerCredDef).toEqual( - expect.objectContaining({ - id: expect.stringMatching(credDefIdRegExp), - schemaId: String(schema.seqNo), - type: credentialDefinitionTemplate.signatureType, - tag: credentialDefinitionTemplate.tag, - ver: '1.0', - value: expect.objectContaining({ - primary: expect.anything(), - revocation: expect.anything(), - }), - }) - ) - }) - - it('should correctly store the genesis file if genesis transactions is passed', async () => { - const genesisTransactions = await promises.readFile(genesisPath, { encoding: 'utf-8' }) - const agentOptions = getAgentOptions('Faber Ledger Genesis Transactions', { - indyLedgers: [ - { - id: 'pool-Faber Ledger Genesis Transactions', - indyNamespace: 'pool-faber-ledger-genesis-transactions', - isProduction: false, - genesisTransactions, - }, - ], - }) - const agent = new Agent(agentOptions) - await agent.initialize() - - if (!faberAgent.publicDid?.did) { - throw new Error('No public did') - } - - const did = await agent.ledger.getPublicDid(faberAgent.publicDid.did) - expect(did.did).toEqual(faberAgent.publicDid.did) - }) -}) diff --git a/packages/core/tests/migration.test.ts b/packages/core/tests/migration.test.ts index 0dbf6b02dc..fbf05abf3f 100644 --- a/packages/core/tests/migration.test.ts +++ b/packages/core/tests/migration.test.ts @@ -1,11 +1,12 @@ import type { VersionString } from '../src/utils/version' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { UpdateAssistant } from '../src/storage/migration/UpdateAssistant' import { getAgentOptions } from './helpers' -const agentOptions = getAgentOptions('Migration', { publicDidSeed: undefined, indyLedgers: [] }) +const agentOptions = getAgentOptions('Migration', { publicDidSeed: undefined }, getIndySdkModules()) describe('migration', () => { test('manually initiating the update assistant to perform an update', async () => { diff --git a/packages/core/tests/multi-protocol-version.test.ts b/packages/core/tests/multi-protocol-version.test.ts index c7197ca36f..4f7596d5ff 100644 --- a/packages/core/tests/multi-protocol-version.test.ts +++ b/packages/core/tests/multi-protocol-version.test.ts @@ -1,23 +1,30 @@ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { AgentMessageProcessedEvent } from '../src/agent/Events' -import { filter, firstValueFrom, Subject, timeout } from 'rxjs' +import { filter, firstValueFrom, timeout } from 'rxjs' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { parseMessageType, MessageSender, Dispatcher, AgentMessage, IsValidMessageType } from '../src' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' +import { parseMessageType, MessageSender, AgentMessage, IsValidMessageType } from '../src' import { Agent } from '../src/agent/Agent' import { AgentEventTypes } from '../src/agent/Events' import { OutboundMessageContext } from '../src/agent/models' import { getAgentOptions } from './helpers' - -const aliceAgentOptions = getAgentOptions('Multi Protocol Versions - Alice', { - endpoints: ['rxjs:alice'], -}) -const bobAgentOptions = getAgentOptions('Multi Protocol Versions - Bob', { - endpoints: ['rxjs:bob'], -}) +import { setupSubjectTransports } from './transport' + +const aliceAgentOptions = getAgentOptions( + 'Multi Protocol Versions - Alice', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) +const bobAgentOptions = getAgentOptions( + 'Multi Protocol Versions - Bob', + { + endpoints: ['rxjs:bob'], + }, + getIndySdkModules() +) describe('multi version protocols', () => { let aliceAgent: Agent @@ -31,29 +38,15 @@ describe('multi version protocols', () => { }) test('should successfully handle a message with a lower minor version than the currently supported version', async () => { - const aliceMessages = new Subject() - const bobMessages = new Subject() - - const subjectMap = { - 'rxjs:alice': aliceMessages, - 'rxjs:bob': bobMessages, - } - - const mockHandle = jest.fn() - + bobAgent = new Agent(bobAgentOptions) aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + setupSubjectTransports([aliceAgent, bobAgent]) // Register the test handler with the v1.3 version of the message - const dispatcher = aliceAgent.dependencyManager.resolve(Dispatcher) - dispatcher.registerMessageHandler({ supportedMessages: [TestMessageV13], handle: mockHandle }) + const mockHandle = jest.fn() + aliceAgent.dependencyManager.registerMessageHandlers([{ supportedMessages: [TestMessageV13], handle: mockHandle }]) await aliceAgent.initialize() - - bobAgent = new Agent(bobAgentOptions) - bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) - bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await bobAgent.initialize() const { outOfBandInvitation, id } = await aliceAgent.oob.createInvitation() diff --git a/packages/core/tests/oob-mediation-provision.test.ts b/packages/core/tests/oob-mediation-provision.test.ts index abfebc9f14..6468d27d7f 100644 --- a/packages/core/tests/oob-mediation-provision.test.ts +++ b/packages/core/tests/oob-mediation-provision.test.ts @@ -1,28 +1,37 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { OutOfBandInvitation } from '../src/modules/oob/messages' -import { Subject } from 'rxjs' - -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' import { MediationState, MediatorPickupStrategy } from '../src/modules/routing' import { getAgentOptions, waitForBasicMessage } from './helpers' - -const faberAgentOptions = getAgentOptions('OOB mediation provision - Faber Agent', { - endpoints: ['rxjs:faber'], -}) -const aliceAgentOptions = getAgentOptions('OOB mediation provision - Alice Recipient Agent', { - endpoints: ['rxjs:alice'], - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) -const mediatorAgentOptions = getAgentOptions('OOB mediation provision - Mediator Agent', { - endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, -}) +import { setupSubjectTransports } from './transport' + +const faberAgentOptions = getAgentOptions( + 'OOB mediation provision - Faber Agent', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() +) +const aliceAgentOptions = getAgentOptions( + 'OOB mediation provision - Alice Recipient Agent', + { + endpoints: ['rxjs:alice'], + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getIndySdkModules() +) +const mediatorAgentOptions = getAgentOptions( + 'OOB mediation provision - Mediator Agent', + { + endpoints: ['rxjs:mediator'], + autoAcceptMediationRequests: true, + }, + getIndySdkModules() +) describe('out of band with mediation set up with provision method', () => { const makeConnectionConfig = { @@ -40,32 +49,19 @@ describe('out of band with mediation set up with provision method', () => { let mediatorOutOfBandInvitation: OutOfBandInvitation beforeAll(async () => { - const faberMessages = new Subject() - const aliceMessages = new Subject() - const mediatorMessages = new Subject() - const subjectMap = { - 'rxjs:faber': faberMessages, - 'rxjs:alice': aliceMessages, - 'rxjs:mediator': mediatorMessages, - } - mediatorAgent = new Agent(mediatorAgentOptions) - mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) - mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await mediatorAgent.initialize() - + aliceAgent = new Agent(aliceAgentOptions) faberAgent = new Agent(faberAgentOptions) - faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) - faberAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + + setupSubjectTransports([mediatorAgent, aliceAgent, faberAgent]) + + await mediatorAgent.initialize() + await aliceAgent.initialize() await faberAgent.initialize() - aliceAgent = new Agent(aliceAgentOptions) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) const mediationOutOfBandRecord = await mediatorAgent.oob.createInvitation(makeConnectionConfig) mediatorOutOfBandInvitation = mediationOutOfBandRecord.outOfBandInvitation - await aliceAgent.initialize() let { connectionRecord } = await aliceAgent.oob.receiveInvitation(mediatorOutOfBandInvitation) connectionRecord = await aliceAgent.connections.returnWhenIsConnected(connectionRecord!.id) await aliceAgent.mediationRecipient.provision(connectionRecord!) diff --git a/packages/core/tests/oob-mediation.test.ts b/packages/core/tests/oob-mediation.test.ts index f085b41f88..72d92ea0de 100644 --- a/packages/core/tests/oob-mediation.test.ts +++ b/packages/core/tests/oob-mediation.test.ts @@ -7,6 +7,7 @@ import { filter, firstValueFrom, map, Subject, timeout } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { AgentEventTypes } from '../src/agent/Events' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' @@ -21,19 +22,31 @@ import { import { getAgentOptions, waitForBasicMessage } from './helpers' -const faberAgentOptions = getAgentOptions('OOB mediation - Faber Agent', { - endpoints: ['rxjs:faber'], -}) -const aliceAgentOptions = getAgentOptions('OOB mediation - Alice Recipient Agent', { - endpoints: ['rxjs:alice'], - // FIXME: discover features returns that we support this protocol, but we don't support all roles - // we should return that we only support the mediator role so we don't have to explicitly declare this - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) -const mediatorAgentOptions = getAgentOptions('OOB mediation - Mediator Agent', { - endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, -}) +const faberAgentOptions = getAgentOptions( + 'OOB mediation - Faber Agent', + { + endpoints: ['rxjs:faber'], + }, + getIndySdkModules() +) +const aliceAgentOptions = getAgentOptions( + 'OOB mediation - Alice Recipient Agent', + { + endpoints: ['rxjs:alice'], + // FIXME: discover features returns that we support this protocol, but we don't support all roles + // we should return that we only support the mediator role so we don't have to explicitly declare this + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getIndySdkModules() +) +const mediatorAgentOptions = getAgentOptions( + 'OOB mediation - Mediator Agent', + { + endpoints: ['rxjs:mediator'], + autoAcceptMediationRequests: true, + }, + getIndySdkModules() +) describe('out of band with mediation', () => { const makeConnectionConfig = { diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 94161fd838..836ca766bd 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -1,24 +1,20 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { CreateCredentialOfferOptions, DefaultCredentialProtocols } from '../src/modules/credentials' +import type { V1CredentialProtocol } from '../../anoncreds/src' +import type { CreateCredentialOfferOptions } from '../src/modules/credentials' import type { AgentMessage, AgentMessageReceivedEvent } from '@aries-framework/core' import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' +import { getLegacyAnonCredsModules, prepareForAnonCredsIssuance } from '../../anoncreds/tests/legacyAnonCredsSetup' import { Agent } from '../src/agent/Agent' -import { Key } from '../src/crypto' -import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' -import { - AgentEventTypes, - AriesFrameworkError, - AutoAcceptCredential, - CredentialState, - V1CredentialPreview, -} from '@aries-framework/core' +import { AgentEventTypes, AriesFrameworkError, AutoAcceptCredential, CredentialState } from '@aries-framework/core' +import { Key } from '../src/crypto' +import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' import { OutOfBandDidCommService } from '../src/modules/oob/domain/OutOfBandDidCommService' import { OutOfBandEventTypes } from '../src/modules/oob/domain/OutOfBandEvents' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' @@ -28,14 +24,26 @@ import { DidCommMessageRepository, DidCommMessageRole } from '../src/storage' import { JsonEncoder } from '../src/utils' import { TestMessage } from './TestMessage' -import { getAgentOptions, prepareForIssuance, waitForCredentialRecord } from './helpers' - -const faberAgentOptions = getAgentOptions('Faber Agent OOB', { - endpoints: ['rxjs:faber'], -}) -const aliceAgentOptions = getAgentOptions('Alice Agent OOB', { - endpoints: ['rxjs:alice'], -}) +import { getAgentOptions, waitForCredentialRecord } from './helpers' + +const faberAgentOptions = getAgentOptions( + 'Faber Agent OOB', + { + endpoints: ['rxjs:faber'], + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) +const aliceAgentOptions = getAgentOptions( + 'Alice Agent OOB', + { + endpoints: ['rxjs:alice'], + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) describe('out of band', () => { const makeConnectionConfig = { @@ -56,9 +64,9 @@ describe('out of band', () => { autoAcceptConnection: false, } - let faberAgent: Agent - let aliceAgent: Agent - let credentialTemplate: CreateCredentialOfferOptions + let faberAgent: Agent> + let aliceAgent: Agent> + let credentialTemplate: CreateCredentialOfferOptions<[V1CredentialProtocol]> beforeAll(async () => { const faberMessages = new Subject() @@ -79,19 +87,34 @@ describe('out of band', () => { aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() - const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'profile_picture', 'x-ray']) + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { + attributeNames: ['name', 'age', 'profile_picture', 'x-ray'], + issuerId: faberAgent.publicDid?.did as string, + }) credentialTemplate = { protocolVersion: 'v1', credentialFormats: { indy: { - attributes: V1CredentialPreview.fromRecord({ - name: 'name', - age: 'age', - profile_picture: 'profile_picture', - 'x-ray': 'x-ray', - }).attributes, - credentialDefinitionId: definition.id, + attributes: [ + { + name: 'name', + value: 'name', + }, + { + name: 'age', + value: 'age', + }, + { + name: 'profile_picture', + value: 'profile_picture', + }, + { + name: 'x-ray', + value: 'x-ray', + }, + ], + credentialDefinitionId: credentialDefinition.credentialDefinitionId, }, }, autoAcceptCredential: AutoAcceptCredential.Never, diff --git a/packages/core/tests/proofs-sub-protocol.test.ts b/packages/core/tests/proofs-sub-protocol.test.ts index 45fa30e713..244eacd496 100644 --- a/packages/core/tests/proofs-sub-protocol.test.ts +++ b/packages/core/tests/proofs-sub-protocol.test.ts @@ -1,33 +1,58 @@ -import type { Agent, ConnectionRecord, ProofExchangeRecord } from '../src' -import type { V1PresentationPreview } from '../src/modules/proofs/protocol/v1/models/V1PresentationPreview' -import type { CredDefId } from 'indy-sdk' - -import { - ProofAttributeInfo, - AttributeFilter, - ProofPredicateInfo, - PredicateType, -} from '../src/modules/proofs/formats/indy/models' +import type { EventReplaySubject } from './events' +import type { AnonCredsTestsAgent } from '../../anoncreds/tests/legacyAnonCredsSetup' + +import { issueLegacyAnonCredsCredential, setupAnonCredsTests } from '../../anoncreds/tests/legacyAnonCredsSetup' import { ProofState } from '../src/modules/proofs/models/ProofState' import { uuid } from '../src/utils/uuid' -import { setupProofsTest, waitForProofExchangeRecord } from './helpers' +import { waitForProofExchangeRecord } from './helpers' import testLogger from './logger' describe('Present Proof Subprotocol', () => { - let faberAgent: Agent - let aliceAgent: Agent - let credDefId: CredDefId - let faberConnection: ConnectionRecord - let aliceConnection: ConnectionRecord - let aliceProofExchangeRecord: ProofExchangeRecord - let presentationPreview: V1PresentationPreview + let faberAgent: AnonCredsTestsAgent + let faberReplay: EventReplaySubject + let aliceAgent: AnonCredsTestsAgent + let aliceReplay: EventReplaySubject + let credentialDefinitionId: string + let faberConnectionId: string + let aliceConnectionId: string beforeAll(async () => { testLogger.test('Initializing the agents') - ;({ faberAgent, aliceAgent, credDefId, faberConnection, aliceConnection, presentationPreview } = - await setupProofsTest('Faber agent', 'Alice agent')) - testLogger.test('Issuing second credential') + ;({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + holderIssuerConnectionId: aliceConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber agent', + holderName: 'Alice agent', + attributeNames: ['name', 'age'], + })) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '50', + }, + ], + credentialDefinitionId, + }, + }) }) afterAll(async () => { @@ -49,16 +74,29 @@ describe('Present Proof Subprotocol', () => { state: ProofState.ProposalReceived, }) - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v1', parentThreadId, proofFormats: { indy: { name: 'abc', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + credentialDefinitionId, + value: 'Alice', + }, + ], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 40, + }, + ], }, }, }) @@ -118,30 +156,6 @@ describe('Present Proof Subprotocol', () => { const parentThreadId = uuid() testLogger.test('Faber sends presentation request to Alice') - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { parentThreadId, state: ProofState.RequestReceived, @@ -150,15 +164,35 @@ describe('Present Proof Subprotocol', () => { // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') const faberProofExchangeRecord = await faberAgent.proofs.requestProof({ - connectionId: faberConnection.id, + connectionId: faberConnectionId, parentThreadId, protocolVersion: 'v1', proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) @@ -216,16 +250,29 @@ describe('Present Proof Subprotocol', () => { state: ProofState.ProposalReceived, }) - aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ - connectionId: aliceConnection.id, + const aliceProofExchangeRecord = await aliceAgent.proofs.proposeProof({ + connectionId: aliceConnectionId, protocolVersion: 'v2', parentThreadId, proofFormats: { indy: { name: 'abc', version: '1.0', - attributes: presentationPreview.attributes, - predicates: presentationPreview.predicates, + attributes: [ + { + name: 'name', + credentialDefinitionId, + value: 'Alice', + }, + ], + predicates: [ + { + credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 40, + }, + ], }, }, }) @@ -285,30 +332,6 @@ describe('Present Proof Subprotocol', () => { const parentThreadId = uuid() testLogger.test('Faber sends presentation request to Alice') - const attributes = { - name: new ProofAttributeInfo({ - name: 'name', - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - - const predicates = { - age: new ProofPredicateInfo({ - name: 'age', - predicateType: PredicateType.GreaterThanOrEqualTo, - predicateValue: 50, - restrictions: [ - new AttributeFilter({ - credentialDefinitionId: credDefId, - }), - ], - }), - } - const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { parentThreadId, state: ProofState.RequestReceived, @@ -317,15 +340,35 @@ describe('Present Proof Subprotocol', () => { // Faber sends a presentation request to Alice testLogger.test('Faber sends a presentation request to Alice') const faberProofExchangeRecord = await faberAgent.proofs.requestProof({ - connectionId: faberConnection.id, + connectionId: faberConnectionId, parentThreadId, protocolVersion: 'v2', proofFormats: { indy: { name: 'proof-request', version: '1.0', - requestedAttributes: attributes, - requestedPredicates: predicates, + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, }, }, }) diff --git a/packages/core/tests/transport.ts b/packages/core/tests/transport.ts new file mode 100644 index 0000000000..2577fdd428 --- /dev/null +++ b/packages/core/tests/transport.ts @@ -0,0 +1,18 @@ +import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { Agent } from '../src' + +import { Subject } from 'rxjs' + +import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' +import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' + +export function setupSubjectTransports(agents: Agent[]) { + const subjectMap: Record> = {} + + for (const agent of agents) { + const messages = new Subject() + subjectMap[agent.config.endpoints[0]] = messages + agent.registerInboundTransport(new SubjectInboundTransport(messages)) + agent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + } +} diff --git a/packages/core/tests/wallet.test.ts b/packages/core/tests/wallet.test.ts index 3362741146..2168ce72ac 100644 --- a/packages/core/tests/wallet.test.ts +++ b/packages/core/tests/wallet.test.ts @@ -1,6 +1,7 @@ import { tmpdir } from 'os' import path from 'path' +import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { Agent } from '../src/agent/Agent' import { BasicMessageRepository, BasicMessageRecord, BasicMessageRole } from '../src/modules/basic-messages' import { KeyDerivationMethod } from '../src/types' @@ -11,8 +12,8 @@ import { WalletNotFoundError } from '../src/wallet/error/WalletNotFoundError' import { getAgentOptions } from './helpers' -const aliceAgentOptions = getAgentOptions('wallet-tests-Alice') -const bobAgentOptions = getAgentOptions('wallet-tests-Bob') +const aliceAgentOptions = getAgentOptions('wallet-tests-Alice', {}, getIndySdkModules()) +const bobAgentOptions = getAgentOptions('wallet-tests-Bob', {}, getIndySdkModules()) describe('wallet', () => { let aliceAgent: Agent diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 7646c74776..8e6acc74c4 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -33,6 +33,7 @@ "tsyringe": "^4.7.0" }, "devDependencies": { + "@stablelib/ed25519": "^1.0.3", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/indy-sdk/src/IndySdkModule.ts b/packages/indy-sdk/src/IndySdkModule.ts index 20574f3d46..d099591543 100644 --- a/packages/indy-sdk/src/IndySdkModule.ts +++ b/packages/indy-sdk/src/IndySdkModule.ts @@ -1,15 +1,16 @@ import type { IndySdkModuleConfigOptions } from './IndySdkModuleConfig' -import type { DependencyManager, Module } from '@aries-framework/core' +import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' import { AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, AnonCredsVerifierServiceSymbol, } from '@aries-framework/anoncreds' -import { InjectionSymbols } from '@aries-framework/core' +import { AriesFrameworkError, InjectionSymbols } from '@aries-framework/core' import { IndySdkModuleConfig } from './IndySdkModuleConfig' import { IndySdkHolderService, IndySdkIssuerService, IndySdkVerifierService } from './anoncreds' +import { IndySdkPoolService } from './ledger' import { IndySdkStorageService } from './storage' import { IndySdkSymbol } from './types' import { IndySdkWallet } from './wallet' @@ -24,12 +25,36 @@ export class IndySdkModule implements Module { public register(dependencyManager: DependencyManager) { dependencyManager.registerInstance(IndySdkSymbol, this.config.indySdk) + // Register config + dependencyManager.registerInstance(IndySdkModuleConfig, this.config) + + if (dependencyManager.isRegistered(InjectionSymbols.Wallet)) { + throw new AriesFrameworkError('There is an instance of Wallet already registered') + } else { + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + } + + if (dependencyManager.isRegistered(InjectionSymbols.StorageService)) { + throw new AriesFrameworkError('There is an instance of StorageService already registered') + } else { + dependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) + } + // NOTE: for now we are registering the needed indy services. We may want to make this // more explicit and require the user to register the services they need on the specific modules. - dependencyManager.registerSingleton(InjectionSymbols.Wallet, IndySdkWallet) - dependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) + dependencyManager.registerSingleton(IndySdkPoolService) dependencyManager.registerSingleton(AnonCredsIssuerServiceSymbol, IndySdkIssuerService) dependencyManager.registerSingleton(AnonCredsHolderServiceSymbol, IndySdkHolderService) dependencyManager.registerSingleton(AnonCredsVerifierServiceSymbol, IndySdkVerifierService) } + + public async initialize(agentContext: AgentContext): Promise { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + + for (const pool of indySdkPoolService.pools) { + if (pool.config.connectOnStartup) { + await pool.connect() + } + } + } } diff --git a/packages/indy-sdk/src/IndySdkModuleConfig.ts b/packages/indy-sdk/src/IndySdkModuleConfig.ts index e5b16142ee..3a269a93f5 100644 --- a/packages/indy-sdk/src/IndySdkModuleConfig.ts +++ b/packages/indy-sdk/src/IndySdkModuleConfig.ts @@ -1,3 +1,4 @@ +import type { IndySdkPoolConfig } from './ledger' import type * as IndySdk from 'indy-sdk' /** @@ -29,6 +30,26 @@ export interface IndySdkModuleConfigOptions { * ``` */ indySdk: typeof IndySdk + + /** + * Array of indy networks to connect to. Each item in the list must include either the `genesisPath` or `genesisTransactions` property. + * + * @default [] + * + * @example + * ``` + * { + * isProduction: false, + * genesisPath: '/path/to/genesis.txn', + * indyNamespace: 'localhost:test', + * transactionAuthorAgreement: { + * version: '1', + * acceptanceMechanism: 'accept' + * } + * } + * ``` + */ + networks?: IndySdkPoolConfig[] } export class IndySdkModuleConfig { @@ -42,4 +63,8 @@ export class IndySdkModuleConfig { public get indySdk() { return this.options.indySdk } + + public get networks() { + return this.options.networks ?? [] + } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 664b86e4de..8b9157221a 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -322,6 +322,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const request = await indySdk.buildCredDefRequest(options.credentialDefinition.issuerId, { id: credentialDefinitionId, + // Indy ledger requires the credential schemaId to be a string of the schema seqNo. schemaId: schemaMetadata.indyLedgerSeqNo.toString(), tag: options.credentialDefinition.tag, type: options.credentialDefinition.type, @@ -447,7 +448,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( - `Using ledger '${pool.id}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` + `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` ) // TODO: implement caching for returned deltas diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts index c043673cd1..7f7cf38ebd 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -1,5 +1,6 @@ import type { IndyEndpointAttrib } from './didSovUtil' import type { IndySdkPool } from '../ledger' +import type { IndySdk } from '../types' import type { AgentContext, DidRegistrar, @@ -8,37 +9,28 @@ import type { DidDeactivateResult, DidUpdateResult, Key, + Buffer, } from '@aries-framework/core' import type { NymRole } from 'indy-sdk' -import { inject, injectable, DidDocumentRole, DidRecord, DidRepository } from '@aries-framework/core' +import { DidDocumentRole, DidRecord, DidRepository } from '@aries-framework/core' import { IndySdkError } from '../error' import { isIndyError } from '../error/indyError' import { IndySdkPoolService } from '../ledger' -import { IndySdk, IndySdkSymbol } from '../types' +import { IndySdkSymbol } from '../types' import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' -@injectable() export class IndySdkSovDidRegistrar implements DidRegistrar { public readonly supportedMethods = ['sov'] - private didRepository: DidRepository - private indySdk: IndySdk - private indySdkPoolService: IndySdkPoolService - - public constructor( - didRepository: DidRepository, - indySdkPoolService: IndySdkPoolService, - @inject(IndySdkSymbol) indySdk: IndySdk - ) { - this.didRepository = didRepository - this.indySdk = indySdk - this.indySdkPoolService = indySdkPoolService - } public async create(agentContext: AgentContext, options: IndySdkSovDidCreateOptions): Promise { + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const { alias, role, submitterDid, indyNamespace } = options.options const privateKey = options.secret?.privateKey @@ -70,16 +62,14 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // WalletItemNotFound when it needs to sign ledger transactions using this did. This means we need // to rely directly on the indy SDK, as we don't want to expose a createDid method just for. assertIndySdkWallet(agentContext.wallet) - const [unqualifiedIndyDid, verkey] = await this.indySdk.createAndStoreMyDid(agentContext.wallet.handle, { + const [unqualifiedIndyDid, verkey] = await indySdk.createAndStoreMyDid(agentContext.wallet.handle, { seed: privateKey?.toString(), }) const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` const unqualifiedSubmitterDid = submitterDid.replace('did:sov:', '') - // TODO: it should be possible to pass the pool used for writing to the indy ledger service. - // The easiest way to do this would be to make the submitterDid a fully qualified did, including the indy namespace. - const pool = this.indySdkPoolService.getPoolForNamespace(indyNamespace) + const pool = indySdkPoolService.getPoolForNamespace(indyNamespace) await this.registerPublicDid(agentContext, unqualifiedSubmitterDid, unqualifiedIndyDid, verkey, alias, pool, role) // Create did document @@ -104,7 +94,6 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // Save the did so we know we created it and can issue with it const didRecord = new DidRecord({ - id: qualifiedSovDid, did: qualifiedSovDid, role: DidDocumentRole.Created, tags: { @@ -112,7 +101,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { qualifiedIndyDid, }, }) - await this.didRepository.save(agentContext, didRecord) + await didRepository.save(agentContext, didRecord) return { didDocumentMetadata: { @@ -178,12 +167,15 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { pool: IndySdkPool, role?: NymRole ) { + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + try { agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`) - const request = await this.indySdk.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) + const request = await indySdk.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) - const response = await this.indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`, { response, @@ -214,12 +206,15 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { endpoints: IndyEndpointAttrib, pool: IndySdkPool ): Promise { + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + try { agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, endpoints) - const request = await this.indySdk.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) + const request = await indySdk.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) - const response = await this.indySdkPoolService.submitWriteRequest(agentContext, pool, request, did) + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, did) agentContext.config.logger.debug( `Successfully set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, { diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts index c4d584568c..98007e5166 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts @@ -1,24 +1,14 @@ import type { IndyEndpointAttrib } from './didSovUtil' +import type { IndySdk } from '../types' import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' -import { inject, injectable } from '@aries-framework/core' - import { isIndyError, IndySdkError } from '../error' import { IndySdkPoolService } from '../ledger/IndySdkPoolService' -import { IndySdkSymbol, IndySdk } from '../types' +import { IndySdkSymbol } from '../types' import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' -@injectable() export class IndySdkSovDidResolver implements DidResolver { - private indySdk: IndySdk - private indySdkPoolService: IndySdkPoolService - - public constructor(indyPoolService: IndySdkPoolService, @inject(IndySdkSymbol) indySdk: IndySdk) { - this.indySdk = indySdk - this.indySdkPoolService = indyPoolService - } - public readonly supportedMethods = ['sov'] public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { @@ -50,24 +40,29 @@ export class IndySdkSovDidResolver implements DidResolver { } private async getPublicDid(agentContext: AgentContext, did: string) { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + // Getting the pool for a did also retrieves the DID. We can just use that - const { did: didResponse } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + const { did: didResponse } = await indySdkPoolService.getPoolForDid(agentContext, did) return didResponse } private async getEndpointsForDid(agentContext: AgentContext, did: string) { - const { pool } = await this.indySdkPoolService.getPoolForDid(agentContext, did) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) try { agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.didIndyNamespace}'`) - const request = await this.indySdk.buildGetAttribRequest(null, did, 'endpoint', null, null) + const request = await indySdk.buildGetAttribRequest(null, did, 'endpoint', null, null) agentContext.config.logger.debug( `Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.didIndyNamespace}'` ) - const response = await this.indySdkPoolService.submitReadRequest(pool, request) + const response = await indySdkPoolService.submitReadRequest(pool, request) if (!response.result.data) return {} diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts new file mode 100644 index 0000000000..ab3f31bd4f --- /dev/null +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts @@ -0,0 +1,383 @@ +import type { IndySdkPool } from '../../ledger/IndySdkPool' +import type { Wallet, DidRecord, RecordSavedEvent } from '@aries-framework/core' + +import { + TypedArrayEncoder, + DidRepository, + SigningProviderRegistry, + JsonTransformer, + DidDocumentRole, + EventEmitter, + RepositoryEventTypes, +} from '@aries-framework/core' +import indySdk from 'indy-sdk' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { mockFunction, getAgentConfig, getAgentContext, agentDependencies, mockProperty } from '../../../../core/tests' +import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' +import { IndySdkSymbol } from '../../types' +import { IndySdkWallet } from '../../wallet' +import { IndySdkSovDidRegistrar } from '../IndySdkSovDidRegistrar' + +jest.mock('../../ledger/IndySdkPoolService') +const IndySdkPoolServiceMock = IndySdkPoolService as jest.Mock +const indySdkPoolServiceMock = new IndySdkPoolServiceMock() + +mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ + config: { indyNamespace: 'pool1' }, +} as IndySdkPool) + +const agentConfig = getAgentConfig('IndySdkSovDidRegistrar') + +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const storageService = new InMemoryStorageService() +const eventEmitter = new EventEmitter(agentDependencies, new Subject()) +const didRepository = new DidRepository(storageService, eventEmitter) + +const createDidMock = jest.fn(async () => ['R1xKJw17sUoXhejEpugMYJ', 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu']) +mockProperty(wallet, 'handle', 10) + +const agentContext = getAgentContext({ + wallet, + registerInstances: [ + [DidRepository, didRepository], + [IndySdkPoolService, indySdkPoolServiceMock], + [IndySdkSymbol, { createAndStoreMyDid: createDidMock }], + ], + agentConfig, +}) + +const indySdkSovDidRegistrar = new IndySdkSovDidRegistrar() + +describe('IndySdkSovDidRegistrar', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + it('should return an error state if an invalid private key is provided', async () => { + const result = await indySdkSovDidRegistrar.create(agentContext, { + method: 'sov', + + options: { + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + secret: { + privateKey: TypedArrayEncoder.fromString('invalid'), + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid private key provided', + }, + }) + }) + + it('should return an error state if the wallet is not an indy wallet', async () => { + const agentContext = getAgentContext({ + wallet: {} as unknown as Wallet, + agentConfig, + registerInstances: [ + [DidRepository, didRepository], + [IndySdkPoolService, indySdkPoolServiceMock], + [IndySdkSymbol, indySdk], + ], + }) + + const result = await indySdkSovDidRegistrar.create(agentContext, { + method: 'sov', + + options: { + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + secret: { + privateKey: TypedArrayEncoder.fromString('12345678901234567890123456789012'), + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'unknownError: Expected wallet to be instance of IndySdkWallet, found Object', + }, + }) + }) + + it('should return an error state if the submitter did is not qualified with did:sov', async () => { + const result = await indySdkSovDidRegistrar.create(agentContext, { + method: 'sov', + options: { + submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Submitter did must be a valid did:sov did', + }, + }) + }) + + it('should correctly create a did:sov document without services', async () => { + const privateKey = '96213c3d7fc8d4d6754c712fd969598e' + + const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) + + const result = await indySdkSovDidRegistrar.create(agentContext, { + method: 'sov', + options: { + alias: 'Hello', + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + }, + secret: { + privateKey: TypedArrayEncoder.fromString(privateKey), + }, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // Unqualified created indy did + 'R1xKJw17sUoXhejEpugMYJ', + // Verkey + 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + // Alias + 'Hello', + // Pool + { config: { indyNamespace: 'pool1' } }, + // Role + 'STEWARD' + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: { + qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + }, + didRegistrationMetadata: { + didIndyNamespace: 'pool1', + }, + didState: { + state: 'finished', + did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + verificationMethod: [ + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + }, + ], + authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], + assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], + keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], + }, + secret: { + privateKey, + }, + }, + }) + }) + + it('should correctly create a did:sov document with services', async () => { + const privateKey = '96213c3d7fc8d4d6754c712fd969598e' + + const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) + + const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + const result = await indySdkSovDidRegistrar.create(agentContext, { + method: 'sov', + options: { + alias: 'Hello', + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + endpoints: { + endpoint: 'https://example.com/endpoint', + routingKeys: ['key-1'], + types: ['DIDComm', 'did-communication', 'endpoint'], + }, + }, + secret: { + privateKey: TypedArrayEncoder.fromString(privateKey), + }, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // Unqualified created indy did + 'R1xKJw17sUoXhejEpugMYJ', + // Verkey + 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + // Alias + 'Hello', + // Pool + { config: { indyNamespace: 'pool1' } }, + // Role + 'STEWARD' + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: { + qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + }, + didRegistrationMetadata: { + didIndyNamespace: 'pool1', + }, + didState: { + state: 'finished', + did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + verificationMethod: [ + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + }, + ], + service: [ + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#endpoint', + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#did-communication', + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + priority: 0, + recipientKeys: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], + routingKeys: ['key-1'], + accept: ['didcomm/aip2;env=rfc19'], + }, + { + id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#didcomm-1', + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + routingKeys: ['key-1'], + accept: ['didcomm/v2'], + }, + ], + authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], + assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], + keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], + }, + secret: { + privateKey, + }, + }, + }) + }) + + it('should store the did document', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('did')) + + const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + const saveCalled = jest.fn() + eventEmitter.on>(RepositoryEventTypes.RecordSaved, saveCalled) + + await indySdkSovDidRegistrar.create(agentContext, { + method: 'sov', + options: { + alias: 'Hello', + submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + endpoints: { + endpoint: 'https://example.com/endpoint', + routingKeys: ['key-1'], + types: ['DIDComm', 'did-communication', 'endpoint'], + }, + }, + secret: { + privateKey, + }, + }) + + expect(saveCalled).toHaveBeenCalledTimes(1) + const [saveEvent] = saveCalled.mock.calls[0] + + expect(saveEvent.payload.record).toMatchObject({ + did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', + role: DidDocumentRole.Created, + _tags: { + recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], + qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + }, + didDocument: undefined, + }) + }) + + it('should return an error state when calling update', async () => { + const result = await indySdkSovDidRegistrar.update() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:sov not implemented yet`, + }, + }) + }) + + it('should return an error state when calling deactivate', async () => { + const result = await indySdkSovDidRegistrar.deactivate() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:sov not implemented yet`, + }, + }) + }) +}) diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts new file mode 100644 index 0000000000..c9bb1bbc93 --- /dev/null +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts @@ -0,0 +1,128 @@ +import type { IndySdkPool } from '../../ledger' +import type { IndyEndpointAttrib } from '../didSovUtil' +import type { GetNymResponse } from 'indy-sdk' + +import { SigningProviderRegistry, JsonTransformer } from '@aries-framework/core' +import indySdk from 'indy-sdk' + +import { parseDid } from '../../../../core/src/modules/dids/domain/parse' +import { mockFunction, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' +import { IndySdkSymbol } from '../../types' +import { IndySdkWallet } from '../../wallet' +import { IndySdkSovDidResolver } from '../IndySdkSovDidResolver' + +import didSovR1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json' +import didSovWJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json' + +jest.mock('../../ledger/IndySdkPoolService') +const IndySdkPoolServiceMock = IndySdkPoolService as jest.Mock +const indySdkPoolServiceMock = new IndySdkPoolServiceMock() + +mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ + config: { indyNamespace: 'pool1' }, +} as IndySdkPool) + +const agentConfig = getAgentConfig('IndySdkSovDidResolver') + +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + +const agentContext = getAgentContext({ + wallet, + agentConfig, + registerInstances: [ + [IndySdkPoolService, indySdkPoolServiceMock], + [IndySdkSymbol, indySdk], + ], +}) + +const indySdkSovDidResolver = new IndySdkSovDidResolver() + +describe('IndySdkSovDidResolver', () => { + it('should correctly resolve a did:sov document', async () => { + const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' + + const nymResponse: GetNymResponse = { + did: 'R1xKJw17sUoXhejEpugMYJ', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + role: 'ENDORSER', + } + + const endpoints: IndyEndpointAttrib = { + endpoint: 'https://ssi.com', + profile: 'https://profile.com', + hub: 'https://hub.com', + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) + + const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didSovR1xKJw17sUoXhejEpugMYJFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should resolve a did:sov document with routingKeys and types entries in the attrib', async () => { + const did = 'did:sov:WJz9mHyW9BZksioQnRsrAo' + + const nymResponse: GetNymResponse = { + did: 'WJz9mHyW9BZksioQnRsrAo', + verkey: 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8', + role: 'ENDORSER', + } + + const endpoints: IndyEndpointAttrib = { + endpoint: 'https://agent.com', + types: ['endpoint', 'did-communication', 'DIDComm'], + routingKeys: ['routingKey1', 'routingKey2'], + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) + + const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didSovWJz9mHyW9BZksioQnRsrAoFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { + const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockRejectedValue(new Error('Error retrieving did')) + + const result = await indySdkSovDidResolver.resolve(agentContext, did, parseDid(did)) + + expect(result).toMatchObject({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did 'did:sov:R1xKJw17sUoXhejEpugMYJ': Error: Error retrieving did`, + }, + }) + }) +}) diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json similarity index 100% rename from packages/core/src/modules/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json rename to packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovR1xKJw17sUoXhejEpugMYJ.json diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json similarity index 100% rename from packages/core/src/modules/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json rename to packages/indy-sdk/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json diff --git a/packages/indy-sdk/src/ledger/IndySdkPool.ts b/packages/indy-sdk/src/ledger/IndySdkPool.ts index dfa25feb37..b784600416 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPool.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPool.ts @@ -16,12 +16,19 @@ export interface TransactionAuthorAgreement { } export interface IndySdkPoolConfig { + /** + * Optional id that influences the pool config that is created by the indy-sdk. + * Uses the indyNamespace as the pool identifier if not provided. + */ + id?: string + genesisPath?: string genesisTransactions?: string - id: string + isProduction: boolean indyNamespace: string transactionAuthorAgreement?: TransactionAuthorAgreement + connectOnStartup?: boolean } export class IndySdkPool { @@ -30,7 +37,7 @@ export class IndySdkPool { private fileSystem: FileSystem private poolConfig: IndySdkPoolConfig private _poolHandle?: number - private poolConnected?: Promise + private poolConnected?: Promise public authorAgreement?: AuthorAgreement | null public constructor( @@ -84,7 +91,7 @@ export class IndySdkPool { await this.close() } - await this.indySdk.deletePoolLedgerConfig(this.poolConfig.id) + await this.indySdk.deletePoolLedgerConfig(this.poolConfig.indyNamespace) } public async connect() { @@ -103,7 +110,7 @@ export class IndySdkPool { } private async connectToLedger() { - const poolName = this.poolConfig.id + const poolName = this.poolConfig.id ?? this.poolConfig.indyNamespace const genesisPath = await this.getGenesisPath() if (!genesisPath) { @@ -115,7 +122,7 @@ export class IndySdkPool { try { this._poolHandle = await this.indySdk.openPoolLedger(poolName) - return this._poolHandle + return } catch (error) { if (!isIndyError(error, 'PoolLedgerNotCreatedError')) { throw isIndyError(error) ? new IndySdkError(error) : error @@ -128,7 +135,7 @@ export class IndySdkPool { try { await this.indySdk.createPoolLedgerConfig(poolName, { genesis_txn: genesisPath }) this._poolHandle = await this.indySdk.openPoolLedger(poolName) - return this._poolHandle + return } catch (error) { throw isIndyError(error) ? new IndySdkError(error) : error } @@ -142,7 +149,9 @@ export class IndySdkPool { const response = await this.submitRequest(request) if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { - throw new IndySdkPoolError(`Ledger '${this.id}' rejected read transaction request: ${response.reason}`) + throw new IndySdkPoolError( + `Ledger '${this.didIndyNamespace}' rejected read transaction request: ${response.reason}` + ) } return response as LedgerReadReplyResponse @@ -152,7 +161,9 @@ export class IndySdkPool { const response = await this.submitRequest(request) if (isLedgerRejectResponse(response) || isLedgerReqnackResponse(response)) { - throw new IndySdkPoolError(`Ledger '${this.id}' rejected write transaction request: ${response.reason}`) + throw new IndySdkPoolError( + `Ledger '${this.didIndyNamespace}' rejected write transaction request: ${response.reason}` + ) } return response as LedgerWriteReplyResponse @@ -168,9 +179,8 @@ export class IndySdkPool { } } - if (!this._poolHandle) { - return this.connect() - } + if (!this._poolHandle) await this.connect() + if (!this._poolHandle) throw new IndySdkPoolError('Pool handle not set after connection') return this._poolHandle } @@ -180,7 +190,7 @@ export class IndySdkPool { if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath // Determine the genesisPath - const genesisPath = this.fileSystem.tempPath + `/genesis-${this.poolConfig.id}.txn` + const genesisPath = this.fileSystem.tempPath + `/genesis-${this.poolConfig.id ?? this.poolConfig.indyNamespace}.txn` // Store genesis data if provided if (this.poolConfig.genesisTransactions) { await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index 9d237fd336..16bc0ac6a2 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -1,12 +1,13 @@ -import type { AcceptanceMechanisms, AuthorAgreement, IndySdkPoolConfig } from './IndySdkPool' +import type { AcceptanceMechanisms, AuthorAgreement } from './IndySdkPool' +import type { IndySdk } from '../types' import type { AgentContext } from '@aries-framework/core' import type { GetNymResponse, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' import { CacheModuleConfig, InjectionSymbols, Logger, injectable, inject, FileSystem } from '@aries-framework/core' import { Subject } from 'rxjs' +import { IndySdkModuleConfig } from '../IndySdkModuleConfig' import { IndySdkError, isIndyError } from '../error' -import { IndySdk } from '../types' import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' import { isSelfCertifiedDid } from '../utils/did' import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' @@ -16,7 +17,7 @@ import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundErr export interface CachedDidResponse { nymResponse: GetNymResponse - poolId: string + indyNamespace: string } @injectable() @@ -26,40 +27,25 @@ export class IndySdkPoolService { private indySdk: IndySdk private stop$: Subject private fileSystem: FileSystem + private indySdkModuleConfig: IndySdkModuleConfig public constructor( - indySdk: IndySdk, @inject(InjectionSymbols.Logger) logger: Logger, @inject(InjectionSymbols.Stop$) stop$: Subject, - @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem + @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, + indySdkModuleConfig: IndySdkModuleConfig ) { this.logger = logger - this.indySdk = indySdk + this.indySdk = indySdkModuleConfig.indySdk this.fileSystem = fileSystem this.stop$ = stop$ - } + this.indySdkModuleConfig = indySdkModuleConfig - public setPools(poolConfigs: IndySdkPoolConfig[]) { - this.pools = poolConfigs.map( - (poolConfig) => new IndySdkPool(poolConfig, this.indySdk, this.logger, this.stop$, this.fileSystem) + this.pools = this.indySdkModuleConfig.networks.map( + (network) => new IndySdkPool(network, this.indySdk, this.logger, this.stop$, this.fileSystem) ) } - /** - * Create connections to all ledger pools - */ - public async connectToPools() { - const handleArray: number[] = [] - // Sequentially connect to pools so we don't use up too many resources connecting in parallel - for (const pool of this.pools) { - this.logger.debug(`Connecting to pool: ${pool.id}`) - const poolHandle = await pool.connect() - this.logger.debug(`Finished connection to pool: ${pool.id}`) - handleArray.push(poolHandle) - } - return handleArray - } - /** * Get the most appropriate pool for the given did. The algorithm is based on the approach as described in this document: * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit @@ -78,11 +64,11 @@ export class IndySdkPoolService { const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache const cachedNymResponse = await cache.get(agentContext, `IndySdkPoolService:${did}`) - const pool = this.pools.find((pool) => pool.id === cachedNymResponse?.poolId) + const pool = this.pools.find((pool) => pool.didIndyNamespace === cachedNymResponse?.indyNamespace) // If we have the nym response with associated pool in the cache, we'll use that if (cachedNymResponse && pool) { - this.logger.trace(`Found ledger id '${pool.id}' for did '${did}' in cache`) + this.logger.trace(`Found ledger '${pool.didIndyNamespace}' for did '${did}' in cache`) return { did: cachedNymResponse.nymResponse, pool } } @@ -99,7 +85,7 @@ export class IndySdkPoolService { // one or more of the ledgers returned an unknown error throw new IndySdkPoolError( - `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers`, + `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers. ${rejectedOtherThanNotFound[0].reason}`, { cause: rejectedOtherThanNotFound[0].reason } ) } @@ -126,8 +112,8 @@ export class IndySdkPoolService { await cache.set(agentContext, `IndySdkPoolService:${did}`, { nymResponse: value.did, - poolId: value.pool.id, - }) + indyNamespace: value.pool.didIndyNamespace, + } satisfies CachedDidResponse) return { pool: value.pool, did: value.did } } @@ -164,7 +150,7 @@ export class IndySdkPoolService { const pool = this.pools.find((pool) => pool.didIndyNamespace === indyNamespace) if (!pool) { - throw new IndySdkPoolNotFoundError(`No ledgers found for IndyNamespace '${indyNamespace}'.`) + throw new IndySdkPoolNotFoundError(`No ledgers found for indy namespace '${indyNamespace}'.`) } return pool @@ -290,14 +276,14 @@ export class IndySdkPoolService { private async getDidFromPool(did: string, pool: IndySdkPool): Promise { try { - this.logger.trace(`Get public did '${did}' from ledger '${pool.id}'`) + this.logger.trace(`Get public did '${did}' from ledger '${pool.didIndyNamespace}'`) const request = await this.indySdk.buildGetNymRequest(null, did) - this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.id}'`) + this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.didIndyNamespace}'`) const response = await pool.submitReadRequest(request) const result = await this.indySdk.parseGetNymResponse(response) - this.logger.trace(`Retrieved did '${did}' from ledger '${pool.id}'`, result) + this.logger.trace(`Retrieved did '${did}' from ledger '${pool.didIndyNamespace}'`, result) return { did: result, @@ -305,12 +291,12 @@ export class IndySdkPoolService { response, } } catch (error) { - this.logger.trace(`Error retrieving did '${did}' from ledger '${pool.id}'`, { + this.logger.trace(`Error retrieving did '${did}' from ledger '${pool.didIndyNamespace}'`, { error, did, }) if (isIndyError(error, 'LedgerNotFound')) { - throw new IndySdkPoolNotFoundError(`Did '${did}' not found on ledger ${pool.id}`) + throw new IndySdkPoolNotFoundError(`Did '${did}' not found on ledger ${pool.didIndyNamespace}`) } else { throw isIndyError(error) ? new IndySdkError(error) : error } diff --git a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts index 2ef487e566..ba96c32b48 100644 --- a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts +++ b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts @@ -1,6 +1,5 @@ import type { IndySdkPoolConfig } from '../IndySdkPool' import type { CachedDidResponse } from '../IndySdkPoolService' -import type { AgentContext, Cache } from '@aries-framework/core' import { CacheModuleConfig, @@ -8,46 +7,44 @@ import { SigningProviderRegistry, AriesFrameworkError, } from '@aries-framework/core' +import indySdk from 'indy-sdk' import { Subject } from 'rxjs' -import { getDidResponsesForDid } from '../../../../core/src/modules/ledger/__tests__/didResponses' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { NodeFileSystem } from '../../../../node/src/NodeFileSystem' +import { IndySdkModuleConfig } from '../../IndySdkModuleConfig' import { IndySdkWallet } from '../../wallet/IndySdkWallet' import { IndySdkPoolService } from '../IndySdkPoolService' import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from '../error' +import { getDidResponsesForDid } from './didResponses' + const pools: IndySdkPoolConfig[] = [ { - id: 'sovrinMain', indyNamespace: 'sovrin', isProduction: true, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { - id: 'sovrinBuilder', indyNamespace: 'sovrin:builder', isProduction: false, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { - id: 'sovringStaging', indyNamespace: 'sovrin:staging', isProduction: false, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { - id: 'indicioMain', indyNamespace: 'indicio', isProduction: true, genesisTransactions: 'xxx', transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' }, }, { - id: 'bcovrinTest', indyNamespace: 'bcovrin:test', isProduction: false, genesisTransactions: 'xxx', @@ -55,17 +52,21 @@ const pools: IndySdkPoolConfig[] = [ }, ] -describe('IndySdkPoolService', () => { - const config = getAgentConfig('IndySdkPoolServiceTest', { - indyLedgers: pools, - }) - let agentContext: AgentContext - let wallet: IndySdkWallet - let poolService: IndySdkPoolService - let cache: Cache +const config = getAgentConfig('IndySdkPoolServiceTest') +const cache = new InMemoryLruCache({ limit: 1 }) + +const indySdkModule = new IndySdkModuleConfig({ indySdk, networks: pools }) +const wallet = new IndySdkWallet(indySdk, config.logger, new SigningProviderRegistry([])) + +const agentContext = getAgentContext({ + wallet, + registerInstances: [[CacheModuleConfig, new CacheModuleConfig({ cache })]], +}) + +const poolService = new IndySdkPoolService(config.logger, new Subject(), new NodeFileSystem(), indySdkModule) +describe('IndySdkPoolService', () => { beforeAll(async () => { - wallet = new IndySdkWallet(config.agentDependencies.indy, config.logger, new SigningProviderRegistry([])) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) @@ -74,26 +75,18 @@ describe('IndySdkPoolService', () => { await wallet.delete() }) - beforeEach(async () => { - cache = new InMemoryLruCache({ limit: 200 }) - agentContext = getAgentContext({ - registerInstances: [[CacheModuleConfig, new CacheModuleConfig({ cache })]], - }) - poolService = new IndySdkPoolService( - agentDependencies.indy, - config.logger, - new Subject(), - new NodeFileSystem() - ) - - poolService.setPools(pools) + afterEach(() => { + cache.clear() }) describe('getPoolForDid', () => { it('should throw a IndySdkPoolNotConfiguredError error if no pools are configured on the pool service', async () => { - poolService.setPools([]) + const oldPools = poolService.pools + poolService.pools = [] expect(poolService.getPoolForDid(agentContext, 'some-did')).rejects.toThrow(IndySdkPoolNotConfiguredError) + + poolService.pools = oldPools }) it('should throw a IndySdkPoolError if all ledger requests throw an error other than NotFoundError', async () => { @@ -124,7 +117,7 @@ describe('IndySdkPoolService', () => { const did = 'TL1EaPFCZ8Si5aUrqScBDt' // Only found on one ledger const responses = getDidResponsesForDid(did, pools, { - sovrinMain: '~43X4NhAFqREffK7eWdKgFH', + sovrin: '~43X4NhAFqREffK7eWdKgFH', }) poolService.pools.forEach((pool, index) => { @@ -134,16 +127,16 @@ describe('IndySdkPoolService', () => { const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe('sovrinMain') + expect(pool.config.indyNamespace).toBe('sovrin') }) it('should return the first pool with a self certifying DID if at least one did is self certifying ', async () => { const did = 'did:sov:q7ATwTYbQDgiigVijUAej' // Found on one production and one non production ledger const responses = getDidResponsesForDid(did, pools, { - indicioMain: '~43X4NhAFqREffK7eWdKgFH', - bcovrinTest: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', - sovrinBuilder: '~43X4NhAFqREffK7eWdKgFH', + indicio: '~43X4NhAFqREffK7eWdKgFH', + 'bcovrin:test': '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + 'sovrin:builder': '~43X4NhAFqREffK7eWdKgFH', }) poolService.pools.forEach((pool, index) => { @@ -153,15 +146,15 @@ describe('IndySdkPoolService', () => { const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe('sovrinBuilder') + expect(pool.config.indyNamespace).toBe('sovrin:builder') }) it('should return the production pool if the did was found on one production and one non production ledger and both DIDs are not self certifying', async () => { const did = 'V6ty6ttM3EjuCtosH6sGtW' // Found on one production and one non production ledger const responses = getDidResponsesForDid(did, pools, { - indicioMain: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', - sovrinBuilder: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + indicio: '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', + 'sovrin:builder': '43X4NhAFqREffK7eWdKgFH43X4NhAFqREffK7eWdKgFH', }) poolService.pools.forEach((pool, index) => { @@ -171,15 +164,15 @@ describe('IndySdkPoolService', () => { const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe('indicioMain') + expect(pool.config.indyNamespace).toBe('indicio') }) it('should return the pool with the self certified did if the did was found on two production ledgers where one did is self certified', async () => { const did = 'VsKV7grR1BUE29mG2Fm2kX' // Found on two production ledgers. Sovrin is self certified const responses = getDidResponsesForDid(did, pools, { - sovrinMain: '~43X4NhAFqREffK7eWdKgFH', - indicioMain: 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn', + sovrin: '~43X4NhAFqREffK7eWdKgFH', + indicio: 'kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn', }) poolService.pools.forEach((pool, index) => { @@ -189,16 +182,16 @@ describe('IndySdkPoolService', () => { const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe('sovrinMain') + expect(pool.config.indyNamespace).toBe('sovrin') }) it('should return the first pool with a self certified did if the did was found on three non production ledgers where two DIDs are self certified', async () => { const did = 'HEi9QViXNThGQaDsQ3ptcw' // Found on two non production ledgers. Sovrin is self certified const responses = getDidResponsesForDid(did, pools, { - sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', - sovrinStaging: '~M9kv2Ez61cur7X39DXWh8W', - bcovrinTest: '3SeuRm3uYuQDYmHeuMLu1xNHozNTtzS3kbZRFMMCWrX4', + 'sovrin:builder': '~M9kv2Ez61cur7X39DXWh8W', + 'sovrin:staging': '~M9kv2Ez61cur7X39DXWh8W', + 'bcovrin:test': '3SeuRm3uYuQDYmHeuMLu1xNHozNTtzS3kbZRFMMCWrX4', }) poolService.pools.forEach((pool, index) => { @@ -208,7 +201,7 @@ describe('IndySdkPoolService', () => { const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe('sovrinBuilder') + expect(pool.config.indyNamespace).toBe('sovrin:builder') }) it('should return the pool from the cache if the did was found in the cache', async () => { @@ -222,20 +215,20 @@ describe('IndySdkPoolService', () => { role: 'ENDORSER', verkey: '~M9kv2Ez61cur7X39DXWh8W', }, - poolId: expectedPool.id, + indyNamespace: expectedPool.indyNamespace, } await cache.set(agentContext, `IndySdkPoolService:${did}`, didResponse) const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe(pool.id) + expect(pool.config.indyNamespace).toBe(pool.didIndyNamespace) }) - it('should set the poolId in the cache if the did was not found in the cache, but resolved later on', async () => { + it('should set the indyNamespace in the cache if the did was not found in the cache, but resolved later on', async () => { const did = 'HEi9QViXNThGQaDsQ3ptcw' // Found on one ledger const responses = getDidResponsesForDid(did, pools, { - sovrinBuilder: '~M9kv2Ez61cur7X39DXWh8W', + 'sovrin:builder': '~M9kv2Ez61cur7X39DXWh8W', }) poolService.pools.forEach((pool, index) => { @@ -245,7 +238,7 @@ describe('IndySdkPoolService', () => { const { pool } = await poolService.getPoolForDid(agentContext, did) - expect(pool.config.id).toBe('sovrinBuilder') + expect(pool.config.indyNamespace).toBe('sovrin:builder') expect(pool.config.indyNamespace).toBe('sovrin:builder') expect(await cache.get(agentContext, `IndySdkPoolService:${did}`)).toEqual({ @@ -254,22 +247,25 @@ describe('IndySdkPoolService', () => { verkey: '~M9kv2Ez61cur7X39DXWh8W', role: '0', }, - poolId: 'sovrinBuilder', + indyNamespace: 'sovrin:builder', }) }) }) describe('getPoolForNamespace', () => { it('should throw a IndySdkPoolNotConfiguredError error if no pools are configured on the pool service', async () => { - poolService.setPools([]) + const oldPools = poolService.pools + poolService.pools = [] expect(() => poolService.getPoolForNamespace()).toThrow(IndySdkPoolNotConfiguredError) + + poolService.pools = oldPools }) it('should return the first pool if indyNamespace is not provided', async () => { const expectedPool = pools[0] - expect(poolService.getPoolForNamespace().id).toEqual(expectedPool.id) + expect(poolService.getPoolForNamespace().didIndyNamespace).toEqual(expectedPool.indyNamespace) }) it('should throw a IndySdkPoolNotFoundError error if any of the pools did not have the provided indyNamespace', async () => { @@ -296,7 +292,7 @@ describe('IndySdkPoolService', () => { const pool = poolService.getPoolForNamespace(indyNameSpace) - expect(pool.id).toEqual(expectedPool.id) + expect(pool.didIndyNamespace).toEqual(expectedPool.indyNamespace) }) }) diff --git a/packages/core/src/modules/ledger/__tests__/didResponses.ts b/packages/indy-sdk/src/ledger/__tests__/didResponses.ts similarity index 94% rename from packages/core/src/modules/ledger/__tests__/didResponses.ts rename to packages/indy-sdk/src/ledger/__tests__/didResponses.ts index bde086e073..4d3dac6596 100644 --- a/packages/core/src/modules/ledger/__tests__/didResponses.ts +++ b/packages/indy-sdk/src/ledger/__tests__/didResponses.ts @@ -1,4 +1,4 @@ -import type { IndyPoolConfig } from '../IndyPool' +import type { IndySdkPoolConfig } from '../IndySdkPool' import type * as Indy from 'indy-sdk' // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -42,11 +42,11 @@ export function getDidResponse({ did, verkey }: { did: string; verkey: string }) export function getDidResponsesForDid( did: string, - pools: IndyPoolConfig[], + pools: IndySdkPoolConfig[], responses: { [key: string]: string | undefined } ) { return pools.map((pool) => { - const verkey = responses[pool.id] + const verkey = responses[pool.indyNamespace] if (verkey) { return () => Promise.resolve(getDidResponse({ did, verkey })) diff --git a/packages/indy-sdk/src/storage/IndySdkStorageService.ts b/packages/indy-sdk/src/storage/IndySdkStorageService.ts index 48f3022154..bfcb740d79 100644 --- a/packages/indy-sdk/src/storage/IndySdkStorageService.ts +++ b/packages/indy-sdk/src/storage/IndySdkStorageService.ts @@ -139,6 +139,8 @@ export class IndySdkStorageService implements StorageServi public async save(agentContext: AgentContext, record: T) { assertIndySdkWallet(agentContext.wallet) + record.updatedAt = new Date() + const value = JsonTransformer.serialize(record) const tags = this.transformFromRecordTagValues(record.getTags()) as Record @@ -158,6 +160,8 @@ export class IndySdkStorageService implements StorageServi public async update(agentContext: AgentContext, record: T): Promise { assertIndySdkWallet(agentContext.wallet) + record.updatedAt = new Date() + const value = JsonTransformer.serialize(record) const tags = this.transformFromRecordTagValues(record.getTags()) as Record diff --git a/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts b/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts index 7a8855c9d5..baa7207d7f 100644 --- a/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts +++ b/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts @@ -1,30 +1,29 @@ import type { IndySdk } from '../../types' -import type { AgentContext, TagsBase } from '@aries-framework/core' +import type { TagsBase } from '@aries-framework/core' -import { SigningProviderRegistry, RecordDuplicateError, RecordNotFoundError } from '@aries-framework/core' +import { RecordDuplicateError, RecordNotFoundError, SigningProviderRegistry } from '@aries-framework/core' +import * as indySdk from 'indy-sdk' import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { IndySdkWallet } from '../../wallet/IndySdkWallet' import { IndySdkStorageService } from '../IndySdkStorageService' -describe('IndySdkStorageService', () => { - let wallet: IndySdkWallet - let indy: IndySdk - let storageService: IndySdkStorageService - let agentContext: AgentContext +const agentConfig = getAgentConfig('IndySdkStorageServiceTest') +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + +const agentContext = getAgentContext({ + wallet, + agentConfig, +}) +const storageService = new IndySdkStorageService(indySdk) +const startDate = Date.now() + +describe('IndySdkStorageService', () => { beforeEach(async () => { - indy = agentDependencies.indy - const agentConfig = getAgentConfig('IndySdkStorageServiceTest') - wallet = new IndySdkWallet(indy, agentConfig.logger, new SigningProviderRegistry([])) - agentContext = getAgentContext({ - wallet, - agentConfig, - }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) - storageService = new IndySdkStorageService(indy) }) afterEach(async () => { @@ -57,7 +56,7 @@ describe('IndySdkStorageService', () => { }, }) - const retrieveRecord = await indy.getWalletRecord(wallet.handle, record.type, record.id, { + const retrieveRecord = await indySdk.getWalletRecord(wallet.handle, record.type, record.id, { retrieveType: true, retrieveTags: true, }) @@ -74,7 +73,7 @@ describe('IndySdkStorageService', () => { }) it('should correctly transform tag values from string after retrieving', async () => { - await indy.addWalletRecord(wallet.handle, TestRecord.type, 'some-id', '{}', { + await indySdk.addWalletRecord(wallet.handle, TestRecord.type, 'some-id', '{}', { someBoolean: '1', someOtherBoolean: '0', someStringValue: 'string', @@ -111,6 +110,12 @@ describe('IndySdkStorageService', () => { expect(record).toEqual(found) }) + + it('After a save the record should have update the updatedAt property', async () => { + const time = startDate + const record = await insertRecord({ id: 'test-updatedAt' }) + expect(record.updatedAt?.getTime()).toBeGreaterThan(time) + }) }) describe('getById()', () => { @@ -149,6 +154,18 @@ describe('IndySdkStorageService', () => { const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) expect(retrievedRecord).toEqual(record) }) + + it('After a record has been updated it should have updated the updatedAT property', async () => { + const time = startDate + const record = await insertRecord({ id: 'test-id' }) + + record.replaceTags({ ...record.getTags(), foo: 'bar' }) + record.foo = 'foobaz' + await storageService.update(agentContext, record) + + const retrievedRecord = await storageService.getById(agentContext, TestRecord, record.id) + expect(retrievedRecord.createdAt.getTime()).toBeGreaterThan(time) + }) }) describe('delete()', () => { @@ -237,13 +254,13 @@ describe('IndySdkStorageService', () => { it('correctly transforms an advanced query into a valid WQL query', async () => { const indySpy = jest.fn() - const storageServiceWithoutIndy = new IndySdkStorageService({ + const storageServiceWithoutIndySdk = new IndySdkStorageService({ openWalletSearch: indySpy, fetchWalletSearchNextRecords: jest.fn(() => ({ records: undefined })), closeWalletSearch: jest.fn(), } as unknown as IndySdk) - await storageServiceWithoutIndy.findByQuery(agentContext, TestRecord, { + await storageServiceWithoutIndySdk.findByQuery(agentContext, TestRecord, { $and: [ { $or: [{ myTag: true }, { myTag: false }], diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index 66d75e8933..043e079c05 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -447,7 +447,7 @@ export class IndySdkWallet implements Wallet { } } - public async createDid(didConfig?: DidConfig): Promise { + private async createDid(didConfig?: DidConfig): Promise { try { const [did, verkey] = await this.indySdk.createAndStoreMyDid(this.handle, didConfig || {}) diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts index 1bb5447031..c80ea47b4a 100644 --- a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -7,9 +7,9 @@ import { TypedArrayEncoder, KeyDerivationMethod, } from '@aries-framework/core' +import indySdk from 'indy-sdk' import testLogger from '../../../../core/tests/logger' -import { agentDependencies } from '../../../../node/src' import { IndySdkWallet } from '../IndySdkWallet' // use raw key derivation method to speed up wallet creating / opening / closing between tests @@ -35,7 +35,7 @@ describe('IndySdkWallet', () => { const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { - indySdkWallet = new IndySdkWallet(agentDependencies.indy, testLogger, new SigningProviderRegistry([])) + indySdkWallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([])) await indySdkWallet.createAndOpen(walletConfig) }) @@ -118,7 +118,7 @@ describe('IndySdkWallet with custom Master Secret Id', () => { let indySdkWallet: IndySdkWallet beforeEach(async () => { - indySdkWallet = new IndySdkWallet(agentDependencies.indy, testLogger, new SigningProviderRegistry([])) + indySdkWallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([])) await indySdkWallet.createAndOpen(walletConfigWithMasterSecretId) }) diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index c6b7ced0f1..a0548c0223 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -1,39 +1,26 @@ import { Agent } from '@aries-framework/core' -import indySdk from 'indy-sdk' -import { Subject } from 'rxjs' import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' import { IndySdkModule } from '../src' import { IndySdkAnonCredsRegistry } from '../src/anoncreds/services/IndySdkAnonCredsRegistry' -import { IndySdkPoolService } from '../src/ledger/IndySdkPoolService' -const agentConfig = getAgentConfig('IndySdkAnonCredsRegistry') +import { getIndySdkModuleConfig } from './setupIndySdkModule' -const indySdkPoolService = new IndySdkPoolService( - indySdk, - agentConfig.logger, - new Subject(), - new agentConfig.agentDependencies.FileSystem() -) +const agentConfig = getAgentConfig('IndySdkAnonCredsRegistry') const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, modules: { - indySdk: new IndySdkModule({ - indySdk, - }), + indySdk: new IndySdkModule(getIndySdkModuleConfig()), }, }) -agent.dependencyManager.registerInstance(IndySdkPoolService, indySdkPoolService) -indySdkPoolService.setPools(agentConfig.indyLedgers) const indySdkAnonCredsRegistry = new IndySdkAnonCredsRegistry() describe('IndySdkAnonCredsRegistry', () => { beforeAll(async () => { await agent.initialize() - await indySdkPoolService.connectToPools() }) afterAll(async () => { @@ -41,7 +28,8 @@ describe('IndySdkAnonCredsRegistry', () => { await agent.wallet.delete() }) - test('it works! :)', async () => { + // One test as the credential definition depends on the schema + test('register and resolve a schema and credential definition', async () => { const dynamicVersion = `1.${Math.random() * 100}` const schemaResult = await indySdkAnonCredsRegistry.registerSchema(agent.context, { @@ -52,7 +40,7 @@ describe('IndySdkAnonCredsRegistry', () => { version: dynamicVersion, }, options: { - didIndyNamespace: agentConfig.indyLedgers[0].indyNamespace, + didIndyNamespace: 'pool:localtest', }, }) diff --git a/packages/core/tests/postgres.e2e.test.ts b/packages/indy-sdk/tests/postgres.e2e.test.ts similarity index 73% rename from packages/core/tests/postgres.e2e.test.ts rename to packages/indy-sdk/tests/postgres.e2e.test.ts index eedffe43b5..a59359f9d8 100644 --- a/packages/core/tests/postgres.e2e.test.ts +++ b/packages/indy-sdk/tests/postgres.e2e.test.ts @@ -1,30 +1,39 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' -import type { IndyPostgresStorageConfig } from '../../node/src' -import type { ConnectionRecord } from '../src/modules/connections' +import type { ConnectionRecord } from '../../core/src/modules/connections' +import type { IndySdkPostgresStorageConfig } from '../../node/src' import { Subject } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { loadPostgresPlugin, WalletScheme } from '../../node/src' -import { Agent } from '../src/agent/Agent' -import { HandshakeProtocol } from '../src/modules/connections' - -import { waitForBasicMessage, getPostgresAgentOptions } from './helpers' - -const alicePostgresAgentOptions = getPostgresAgentOptions('AgentsAlice', { - endpoints: ['rxjs:alice'], -}) -const bobPostgresAgentOptions = getPostgresAgentOptions('AgentsBob', { - endpoints: ['rxjs:bob'], -}) +import { Agent } from '../../core/src/agent/Agent' +import { HandshakeProtocol } from '../../core/src/modules/connections' +import { waitForBasicMessage, getPostgresAgentOptions } from '../../core/tests/helpers' +import { loadIndySdkPostgresPlugin, IndySdkPostgresWalletScheme } from '../../node/src' + +import { getIndySdkModules } from './setupIndySdkModule' + +const alicePostgresAgentOptions = getPostgresAgentOptions( + 'AgentsAlice', + { + endpoints: ['rxjs:alice'], + }, + getIndySdkModules() +) + +const bobPostgresAgentOptions = getPostgresAgentOptions( + 'AgentsBob', + { + endpoints: ['rxjs:bob'], + }, + getIndySdkModules() +) describe('postgres agents', () => { let aliceAgent: Agent let bobAgent: Agent let aliceConnection: ConnectionRecord - let bobConnection: ConnectionRecord afterAll(async () => { await bobAgent.shutdown() @@ -42,11 +51,11 @@ describe('postgres agents', () => { 'rxjs:bob': bobMessages, } - const storageConfig: IndyPostgresStorageConfig = { + const storageConfig: IndySdkPostgresStorageConfig = { type: 'postgres_storage', config: { url: 'localhost:5432', - wallet_scheme: WalletScheme.DatabasePerWallet, + wallet_scheme: IndySdkPostgresWalletScheme.DatabasePerWallet, }, credentials: { account: 'postgres', @@ -57,7 +66,7 @@ describe('postgres agents', () => { } // loading the postgres wallet plugin - loadPostgresPlugin(storageConfig.config, storageConfig.credentials) + loadIndySdkPostgresPlugin(storageConfig.config, storageConfig.credentials) aliceAgent = new Agent(alicePostgresAgentOptions) aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) @@ -76,13 +85,10 @@ describe('postgres agents', () => { const { connectionRecord: bobConnectionAtBobAlice } = await bobAgent.oob.receiveInvitation( aliceBobOutOfBandRecord.outOfBandInvitation ) - bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice!.id) + await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice!.id) const [aliceConnectionAtAliceBob] = await aliceAgent.connections.findAllByOutOfBandId(aliceBobOutOfBandRecord.id) aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob!.id) - - expect(aliceConnection).toBeConnectedWith(bobConnection) - expect(bobConnection).toBeConnectedWith(aliceConnection) }) test('send a message to connection', async () => { diff --git a/packages/indy-sdk/tests/setupIndySdkModule.ts b/packages/indy-sdk/tests/setupIndySdkModule.ts new file mode 100644 index 0000000000..c0e8ee1313 --- /dev/null +++ b/packages/indy-sdk/tests/setupIndySdkModule.ts @@ -0,0 +1,29 @@ +import { DidsModule, KeyDidRegistrar, KeyDidResolver, utils } from '@aries-framework/core' +import indySdk from 'indy-sdk' + +import { genesisPath, taaVersion, taaAcceptanceMechanism } from '../../core/tests/helpers' +import { IndySdkModule, IndySdkModuleConfig, IndySdkSovDidRegistrar, IndySdkSovDidResolver } from '../src' + +export { indySdk } + +export const getIndySdkModuleConfig = () => + new IndySdkModuleConfig({ + indySdk, + networks: [ + { + id: `localhost-${utils.uuid()}`, + isProduction: false, + genesisPath, + indyNamespace: 'pool:localtest', + transactionAuthorAgreement: { version: taaVersion, acceptanceMechanism: taaAcceptanceMechanism }, + }, + ], + }) + +export const getIndySdkModules = () => ({ + indySdk: new IndySdkModule(getIndySdkModuleConfig()), + dids: new DidsModule({ + registrars: [new IndySdkSovDidRegistrar(), new KeyDidRegistrar()], + resolvers: [new IndySdkSovDidResolver(), new KeyDidResolver()], + }), +}) diff --git a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts b/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts new file mode 100644 index 0000000000..de121b462d --- /dev/null +++ b/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts @@ -0,0 +1,125 @@ +import type { IndySdkSovDidCreateOptions } from '../src/dids/IndySdkSovDidRegistrar' + +import { Agent, TypedArrayEncoder, convertPublicKeyToX25519, JsonTransformer } from '@aries-framework/core' +import { generateKeyPairFromSeed } from '@stablelib/ed25519' + +import { getAgentOptions } from '../../core/tests/helpers' +import { indyDidFromPublicKeyBase58 } from '../src/utils/did' + +import { getIndySdkModules } from './setupIndySdkModule' + +const agentOptions = getAgentOptions('Faber Dids Registrar', {}, getIndySdkModules()) + +describe('dids', () => { + let agent: Agent> + + beforeAll(async () => { + agent = new Agent(agentOptions) + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should create a did:sov did', async () => { + // Generate a seed and the indy did. This allows us to create a new did every time + // but still check if the created output document is as expected. + const privateKey = TypedArrayEncoder.fromString( + Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + ) + + const publicKeyEd25519 = generateKeyPairFromSeed(privateKey).publicKey + const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(publicKeyEd25519)) + const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) + const indyDid = indyDidFromPublicKeyBase58(ed25519PublicKeyBase58) + + const did = await agent.dids.create({ + method: 'sov', + options: { + submitterDid: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', + alias: 'Alias', + endpoints: { + endpoint: 'https://example.com/endpoint', + types: ['DIDComm', 'did-communication', 'endpoint'], + routingKeys: ['a-routing-key'], + }, + }, + secret: { + privateKey, + }, + }) + + expect(JsonTransformer.toJSON(did)).toMatchObject({ + didDocumentMetadata: { + qualifiedIndyDid: `did:indy:pool:localtest:${indyDid}`, + }, + didRegistrationMetadata: { + didIndyNamespace: 'pool:localtest', + }, + didState: { + state: 'finished', + did: `did:sov:${indyDid}`, + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + id: `did:sov:${indyDid}#key-1`, + type: 'Ed25519VerificationKey2018', + controller: `did:sov:${indyDid}`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + { + id: `did:sov:${indyDid}#key-agreement-1`, + type: 'X25519KeyAgreementKey2019', + controller: `did:sov:${indyDid}`, + publicKeyBase58: x25519PublicKeyBase58, + }, + ], + service: [ + { + id: `did:sov:${indyDid}#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + accept: ['didcomm/aip2;env=rfc19'], + id: `did:sov:${indyDid}#did-communication`, + priority: 0, + recipientKeys: [`did:sov:${indyDid}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + }, + { + accept: ['didcomm/v2'], + id: `did:sov:${indyDid}#didcomm-1`, + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + }, + ], + authentication: [`did:sov:${indyDid}#key-1`], + assertionMethod: [`did:sov:${indyDid}#key-1`], + keyAgreement: [`did:sov:${indyDid}#key-agreement-1`], + capabilityInvocation: undefined, + capabilityDelegation: undefined, + id: `did:sov:${indyDid}`, + }, + secret: { + privateKey: privateKey.toString(), + }, + }, + }) + }) +}) diff --git a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts new file mode 100644 index 0000000000..dfa3281214 --- /dev/null +++ b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts @@ -0,0 +1,74 @@ +import type { IndySdkSovDidCreateOptions } from '../src/dids/IndySdkSovDidRegistrar' + +import { Agent, AriesFrameworkError, JsonTransformer } from '@aries-framework/core' + +import { getAgentOptions } from '../../core/tests/helpers' + +import { getIndySdkModules } from './setupIndySdkModule' + +const agent = new Agent(getAgentOptions('Indy SDK Sov DID resolver', {}, getIndySdkModules())) + +describe('Indy SDK Sov DID resolver', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should resolve a did:sov did', async () => { + const createResult = await agent.dids.create({ + method: 'sov', + options: { + submitterDid: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', + alias: 'Alias', + role: 'TRUSTEE', + }, + }) + + // Terrible, but the did can't be immediately resolved, so we need to wait a bit + await new Promise((res) => setTimeout(res, 1000)) + + if (!createResult.didState.did) throw new AriesFrameworkError('Unable to register did') + const didResult = await agent.dids.resolve(createResult.didState.did) + + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: createResult.didState.did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: createResult.didState.did, + id: `${createResult.didState.did}#key-1`, + publicKeyBase58: expect.any(String), + }, + { + controller: createResult.didState.did, + type: 'X25519KeyAgreementKey2019', + id: `${createResult.didState.did}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${createResult.didState.did}#key-1`], + assertionMethod: [`${createResult.didState.did}#key-1`], + keyAgreement: [`${createResult.didState.did}#key-agreement-1`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) +}) diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index fa1c9d3e39..088c8da018 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -26,10 +26,10 @@ "dependencies": { "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", - "@hyperledger/indy-vdr-shared": "^0.1.0-dev.4" + "@hyperledger/indy-vdr-shared": "^0.1.0-dev.6" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.4", + "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.6", "@stablelib/ed25519": "^1.0.2", "rimraf": "^4.0.7", "rxjs": "^7.2.0", diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts index cd6b7244bf..44dc54ff8a 100644 --- a/packages/indy-vdr/src/IndyVdrModule.ts +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -1,5 +1,5 @@ import type { IndyVdrModuleConfigOptions } from './IndyVdrModuleConfig' -import type { DependencyManager, Module } from '@aries-framework/core' +import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' import { IndyVdrModuleConfig } from './IndyVdrModuleConfig' import { IndyVdrPoolService } from './pool/IndyVdrPoolService' @@ -32,4 +32,14 @@ export class IndyVdrModule implements Module { // Services dependencyManager.registerSingleton(IndyVdrPoolService) } + + public async initialize(agentContext: AgentContext): Promise { + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + + for (const pool of indyVdrPoolService.pools) { + if (pool.config.connectOnStartup) { + await pool.connect() + } + } + } } diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index ba105104fa..209f0aac81 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -320,9 +320,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { schemaId: `${schemaMetadata.indyLedgerSeqNo}`, type: 'CL', tag: options.credentialDefinition.tag, - value: { - primary: options.credentialDefinition.value, - }, + value: options.credentialDefinition.value, }, }) @@ -338,7 +336,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { credentialDefinitionState: { credentialDefinition: options.credentialDefinition, state: 'failed', - reason: `didNotFound: unable to resolve did did:sov${options.credentialDefinition.issuerId}: ${didResult.didResolutionMetadata.message}`, + reason: `didNotFound: unable to resolve did did:sov:${options.credentialDefinition.issuerId}: ${didResult.didResolutionMetadata.message}`, }, } } diff --git a/packages/indy-vdr/src/pool/IndyVdrPool.ts b/packages/indy-vdr/src/pool/IndyVdrPool.ts index 99ba0d1b06..e5dfaade1a 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPool.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPool.ts @@ -1,4 +1,4 @@ -import type { Logger, AgentContext, Key } from '@aries-framework/core' +import type { AgentContext, Key } from '@aries-framework/core' import type { IndyVdrRequest, IndyVdrPool as indyVdrPool } from '@hyperledger/indy-vdr-shared' import { TypedArrayEncoder } from '@aries-framework/core' @@ -35,16 +35,15 @@ export interface IndyVdrPoolConfig { isProduction: boolean indyNamespace: string transactionAuthorAgreement?: TransactionAuthorAgreement + connectOnStartup?: boolean } export class IndyVdrPool { private _pool?: indyVdrPool - private logger: Logger private poolConfig: IndyVdrPoolConfig public authorAgreement?: AuthorAgreement | null - public constructor(poolConfig: IndyVdrPoolConfig, logger: Logger) { - this.logger = logger + public constructor(poolConfig: IndyVdrPoolConfig) { this.poolConfig = poolConfig } @@ -56,26 +55,27 @@ export class IndyVdrPool { return this.poolConfig } - public async connect() { + public connect() { + if (this._pool) { + throw new IndyVdrError('Cannot connect to pool, already connected.') + } + this._pool = new PoolCreate({ parameters: { transactions: this.config.genesisTransactions, }, }) - - return this.pool.handle } private get pool(): indyVdrPool { - if (!this._pool) { - throw new IndyVdrError('Pool is not connected. Make sure to call .connect() first') - } + if (!this._pool) this.connect() + if (!this._pool) throw new IndyVdrError('Pool is not connected.') return this._pool } public close() { - if (!this.pool) { + if (!this._pool) { throw new IndyVdrError("Can't close pool. Pool is not connected") } diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index 3fa6177465..1ad1b0f80a 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -28,22 +28,7 @@ export class IndyVdrPoolService { this.logger = logger this.indyVdrModuleConfig = indyVdrModuleConfig - this.pools = this.indyVdrModuleConfig.networks.map((poolConfig) => new IndyVdrPool(poolConfig, this.logger)) - } - - /** - * Create connections to all ledger pools - */ - public async connectToPools() { - const handleArray: number[] = [] - // Sequentially connect to pools so we don't use up too many resources connecting in parallel - for (const pool of this.pools) { - this.logger.debug(`Connecting to pool: ${pool.indyNamespace}`) - const poolHandle = await pool.connect() - this.logger.debug(`Finished connection to pool: ${pool.indyNamespace}`) - handleArray.push(poolHandle) - } - return handleArray + this.pools = this.indyVdrModuleConfig.networks.map((poolConfig) => new IndyVdrPool(poolConfig)) } /** @@ -102,7 +87,7 @@ export class IndyVdrPoolService { // one or more of the ledgers returned an unknown error throw new IndyVdrError( - `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers`, + `Unknown error retrieving did '${did}' from '${rejectedOtherThanNotFound.length}' of '${pools.length}' ledgers. ${rejectedOtherThanNotFound[0].reason}`, { cause: rejectedOtherThanNotFound[0].reason } ) } @@ -165,7 +150,7 @@ export class IndyVdrPoolService { const pool = this.pools.find((pool) => pool.indyNamespace === indyNamespace) if (!pool) { - throw new IndyVdrError(`No ledgers found for IndyNamespace '${indyNamespace}'.`) + throw new IndyVdrError(`No ledgers found for indy namespace '${indyNamespace}'.`) } return pool diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index f7d3bbc9fa..74676ef88b 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -1,6 +1,9 @@ -import { Agent } from '@aries-framework/core' +import { Agent, DidsModule } from '@aries-framework/core' import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' +import { IndySdkModule } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { IndyVdrSovDidResolver } from '../src' import { IndyVdrAnonCredsRegistry } from '../src/anoncreds/IndyVdrAnonCredsRegistry' import { IndyVdrPoolService } from '../src/pool' @@ -16,6 +19,14 @@ const indyVdrAnonCredsRegistry = new IndyVdrAnonCredsRegistry() const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, + modules: { + indySdk: new IndySdkModule({ + indySdk, + }), + dids: new DidsModule({ + resolvers: [new IndyVdrSovDidResolver()], + }), + }, }) agent.dependencyManager.registerInstance(IndyVdrPoolService, indyVdrPoolService) @@ -23,7 +34,6 @@ agent.dependencyManager.registerInstance(IndyVdrPoolService, indyVdrPoolService) describe('IndyVdrAnonCredsRegistry', () => { beforeAll(async () => { await agent.initialize() - await indyVdrPoolService.connectToPools() }) afterAll(async () => { @@ -159,18 +169,16 @@ describe('IndyVdrAnonCredsRegistry', () => { type: 'CL', value: { primary: { - primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', - r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', - master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', - }, - rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', }, }, }, diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index d148744502..ec6724d576 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -10,14 +10,15 @@ import { DidCommV1Service, DidCommV2Service, DidDocumentService, - IndyWallet, } from '@aries-framework/core' import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' import { Subject } from 'rxjs' -import { IndyStorageService } from '../../core/src/storage/IndyStorageService' +import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' +import { IndySdkWallet } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrIndyDidRegistrar } from '../src/dids/IndyVdrIndyDidRegistrar' import { IndyVdrIndyDidResolver } from '../src/dids/IndyVdrIndyDidResolver' import { indyDidFromNamespaceAndInitialKey } from '../src/dids/didIndyUtil' @@ -27,7 +28,7 @@ import { DID_INDY_REGEX } from '../src/utils/did' import { indyVdrModuleConfig } from './helpers' const logger = testLogger -const wallet = new IndyWallet(agentDependencies, logger, new SigningProviderRegistry([])) +const wallet = new IndySdkWallet(indySdk, logger, new SigningProviderRegistry([])) const agentConfig = getAgentConfig('IndyVdrIndyDidRegistrar E2E', { logger }) @@ -43,7 +44,7 @@ const agentContext = getAgentContext({ registerInstances: [ [InjectionSymbols.Stop$, new Subject()], [InjectionSymbols.AgentDependencies, agentDependencies], - [InjectionSymbols.StorageService, new IndyStorageService(agentDependencies)], + [InjectionSymbols.StorageService, new InMemoryStorageService()], [IndyVdrPoolService, new IndyVdrPoolService(logger, indyVdrModuleConfig)], [CacheModuleConfig, new CacheModuleConfig({ cache })], ], @@ -53,11 +54,7 @@ const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolSer describe('Indy VDR registrar E2E', () => { beforeAll(async () => { - await indyVdrPoolService.connectToPools() - - if (agentConfig.walletConfig) { - await wallet.createAndOpen(agentConfig.walletConfig) - } + await wallet.createAndOpen(agentConfig.walletConfig) signerKey = await wallet.createKey({ privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), diff --git a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts index a40d4f72b4..bc0e5f4ea8 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts @@ -2,7 +2,6 @@ import type { Key } from '@aries-framework/core' import { TypedArrayEncoder, - IndyWallet, CacheModuleConfig, InMemoryLruCache, JsonTransformer, @@ -11,8 +10,10 @@ import { } from '@aries-framework/core' import { parseDid } from '../../core/src/modules/dids/domain/parse' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' +import { IndySdkWallet } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrSovDidResolver } from '../src/dids' import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' @@ -20,7 +21,7 @@ import { indyDidFromPublicKeyBase58 } from '../src/utils/did' import { createDidOnLedger, indyVdrModuleConfig } from './helpers' const logger = testLogger -const wallet = new IndyWallet(agentDependencies, logger, new SigningProviderRegistry([])) +const wallet = new IndySdkWallet(indySdk, logger, new SigningProviderRegistry([])) const agentConfig = getAgentConfig('IndyVdrResolver E2E', { logger }) const cache = new InMemoryLruCache({ limit: 200 }) @@ -41,11 +42,7 @@ const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolSer describe('indy-vdr DID Resolver E2E', () => { beforeAll(async () => { - await indyVdrPoolService.connectToPools() - - if (agentConfig.walletConfig) { - await wallet.createAndOpen(agentConfig.walletConfig) - } + await wallet.createAndOpen(agentConfig.walletConfig) signerKey = await wallet.createKey({ privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index ee1faad9dc..151b226a70 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -1,10 +1,12 @@ import type { Key } from '@aries-framework/core' -import { TypedArrayEncoder, IndyWallet, KeyType, SigningProviderRegistry } from '@aries-framework/core' +import { TypedArrayEncoder, KeyType, SigningProviderRegistry } from '@aries-framework/core' import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from '@hyperledger/indy-vdr-shared' -import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' import testLogger from '../../core/tests/logger' +import { IndySdkWallet } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrPool } from '../src/pool' import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' @@ -12,8 +14,7 @@ import { indyDidFromPublicKeyBase58 } from '../src/utils/did' import { indyVdrModuleConfig } from './helpers' const indyVdrPoolService = new IndyVdrPoolService(testLogger, indyVdrModuleConfig) -const wallet = new IndyWallet(agentDependencies, testLogger, new SigningProviderRegistry([])) - +const wallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([])) const agentConfig = getAgentConfig('IndyVdrPoolService') const agentContext = getAgentContext({ wallet, agentConfig }) @@ -28,11 +29,7 @@ let signerKey: Key describe('IndyVdrPoolService', () => { beforeAll(async () => { - await indyVdrPoolService.connectToPools() - - if (agentConfig.walletConfig) { - await wallet.createAndOpen(agentConfig.walletConfig) - } + await wallet.createAndOpen(agentConfig.walletConfig) signerKey = await wallet.createKey({ privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), diff --git a/packages/node/bin/is-indy-installed.js b/packages/node/bin/is-indy-installed.js deleted file mode 100755 index 59704d4de7..0000000000 --- a/packages/node/bin/is-indy-installed.js +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env node -/* eslint-disable no-console, @typescript-eslint/no-var-requires, no-undef */ - -const { createWallet, deleteWallet } = require('indy-sdk') - -const uuid = Math.random() * 10000 -const id = `test-wallet-id-${uuid}` - -createWallet({ id }, { key: id }) - .then(() => deleteWallet({ id }, { key: id })) - .then(() => { - console.log('Libindy was installed correctly') - }) - .catch((e) => { - console.log('Libindy was installed correctly, but an error did occur') - console.error(e) - }) diff --git a/packages/node/package.json b/packages/node/package.json index 30ffadd1f6..e82409ea98 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -4,8 +4,7 @@ "types": "build/index", "version": "0.3.3", "files": [ - "build", - "bin" + "build" ], "license": "Apache-2.0", "publishConfig": { @@ -17,9 +16,6 @@ "url": "https://github.com/hyperledger/aries-framework-javascript", "directory": "packages/node" }, - "bin": { - "is-indy-installed": "bin/is-indy-installed.js" - }, "scripts": { "build": "yarn run clean && yarn run compile", "clean": "rimraf ./build", @@ -32,7 +28,6 @@ "@types/express": "^4.17.15", "express": "^4.17.1", "ffi-napi": "^4.0.3", - "indy-sdk": "^1.16.0-dev-1636", "node-fetch": "^2.6.1", "ref-napi": "^3.0.3", "ws": "^7.5.3" diff --git a/packages/node/src/PostgresPlugin.ts b/packages/node/src/PostgresPlugin.ts index 4ad7dc5f37..2bcac4aae2 100644 --- a/packages/node/src/PostgresPlugin.ts +++ b/packages/node/src/PostgresPlugin.ts @@ -70,32 +70,35 @@ type NativeIndyPostgres = { let indyPostgresStorage: NativeIndyPostgres | undefined -export interface WalletStorageConfig { +export interface IndySdkPostgresWalletStorageConfig { url: string - wallet_scheme: WalletScheme + wallet_scheme: IndySdkPostgresWalletScheme path?: string } -export interface WalletStorageCredentials { +export interface IndySdkPostgresWalletStorageCredentials { account: string password: string admin_account: string admin_password: string } -export enum WalletScheme { +export enum IndySdkPostgresWalletScheme { DatabasePerWallet = 'DatabasePerWallet', MultiWalletSingleTable = 'MultiWalletSingleTable', MultiWalletSingleTableSharedPool = 'MultiWalletSingleTableSharedPool', } -export interface IndyPostgresStorageConfig { +export interface IndySdkPostgresStorageConfig { type: 'postgres_storage' - config: WalletStorageConfig - credentials: WalletStorageCredentials + config: IndySdkPostgresWalletStorageConfig + credentials: IndySdkPostgresWalletStorageCredentials } -export function loadPostgresPlugin(config: WalletStorageConfig, credentials: WalletStorageCredentials) { +export function loadIndySdkPostgresPlugin( + config: IndySdkPostgresWalletStorageConfig, + credentials: IndySdkPostgresWalletStorageCredentials +) { if (!indyPostgresStorage) { indyPostgresStorage = getLibrary() } diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 5e58035b32..fbe7ed0452 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -1,12 +1,11 @@ import type { AgentDependencies } from '@aries-framework/core' import { EventEmitter } from 'events' -import * as indy from 'indy-sdk' import fetch from 'node-fetch' import WebSocket from 'ws' import { NodeFileSystem } from './NodeFileSystem' -import { IndyPostgresStorageConfig, loadPostgresPlugin, WalletScheme } from './PostgresPlugin' +import { IndySdkPostgresStorageConfig, loadIndySdkPostgresPlugin, IndySdkPostgresWalletScheme } from './PostgresPlugin' import { HttpInboundTransport } from './transport/HttpInboundTransport' import { WsInboundTransport } from './transport/WsInboundTransport' @@ -15,14 +14,13 @@ const agentDependencies: AgentDependencies = { fetch, EventEmitterClass: EventEmitter, WebSocketClass: WebSocket, - indy, } export { agentDependencies, HttpInboundTransport, WsInboundTransport, - loadPostgresPlugin, - IndyPostgresStorageConfig, - WalletScheme, + loadIndySdkPostgresPlugin, + IndySdkPostgresStorageConfig, + IndySdkPostgresWalletScheme, } diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index 20b2898c84..2afc542a49 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -5,29 +5,28 @@ import nock, { cleanAll, enableNetConnect } from 'nock' import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader' -import { getAgentOptions } from '../../core/tests/helpers' +import { getAgentOptions, indySdk } from '../../core/tests' +import { IndySdkModule } from '../../indy-sdk/src' import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' import { acquireAccessTokenResponse, credentialRequestResponse, getMetadataResponse } from './fixtures' +const modules = { + openId4VcClient: new OpenId4VcClientModule(), + w3cVc: new W3cVcModule({ + documentLoader: customDocumentLoader, + }), + indySdk: new IndySdkModule({ + indySdk, + }), +} + describe('OpenId4VcClient', () => { - let agent: Agent<{ - openId4VcClient: OpenId4VcClientModule - w3cVc: W3cVcModule - }> + let agent: Agent beforeEach(async () => { - const agentOptions = getAgentOptions( - 'OpenId4VcClient Agent', - {}, - { - openId4VcClient: new OpenId4VcClientModule(), - w3cVc: new W3cVcModule({ - documentLoader: customDocumentLoader, - }), - } - ) + const agentOptions = getAgentOptions('OpenId4VcClient Agent', {}, modules) agent = new Agent(agentOptions) await agent.initialize() diff --git a/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts index bd3f071a01..4a21491d36 100644 --- a/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts +++ b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts @@ -1,17 +1,13 @@ -import type { AgentConfig, AgentContext, Repository } from '@aries-framework/core' +import type { AgentConfig, AgentContext, Repository, Wallet } from '@aries-framework/core' import type { QuestionAnswerStateChangedEvent, ValidResponse } from '@aries-framework/question-answer' -import { - EventEmitter, - IndyWallet, - SigningProviderRegistry, - InboundMessageContext, - DidExchangeState, -} from '@aries-framework/core' +import { EventEmitter, SigningProviderRegistry, InboundMessageContext, DidExchangeState } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../core/tests/helpers' +import { IndySdkWallet } from '../../../indy-sdk/src' +import { indySdk } from '../../../indy-sdk/tests/setupIndySdkModule' import { QuestionAnswerRecord, @@ -34,7 +30,7 @@ describe('QuestionAnswerService', () => { state: DidExchangeState.Completed, }) - let wallet: IndyWallet + let wallet: Wallet let agentConfig: AgentConfig let questionAnswerRepository: Repository let questionAnswerService: QuestionAnswerService @@ -65,7 +61,7 @@ describe('QuestionAnswerService', () => { beforeAll(async () => { agentConfig = getAgentConfig('QuestionAnswerServiceTest') - wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, new SigningProviderRegistry([])) + wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) diff --git a/packages/question-answer/tests/question-answer.e2e.test.ts b/packages/question-answer/tests/question-answer.e2e.test.ts index c2c35d8c2b..b15d71efe6 100644 --- a/packages/question-answer/tests/question-answer.e2e.test.ts +++ b/packages/question-answer/tests/question-answer.e2e.test.ts @@ -1,26 +1,27 @@ -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { ConnectionRecord } from '@aries-framework/core' import { Agent } from '@aries-framework/core' -import { Subject } from 'rxjs' -import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' -import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { getAgentOptions, makeConnection } from '../../core/tests/helpers' -import testLogger from '../../core/tests/logger' +import { indySdk, setupSubjectTransports, testLogger, getAgentOptions, makeConnection } from '../../core/tests' +import { IndySdkModule } from '../../indy-sdk/src' import { QuestionAnswerModule, QuestionAnswerRole, QuestionAnswerState } from '@aries-framework/question-answer' import { waitForQuestionAnswerRecord } from './helpers' +const modules = { + questionAnswer: new QuestionAnswerModule(), + indySdk: new IndySdkModule({ + indySdk, + }), +} + const bobAgentOptions = getAgentOptions( 'Bob Question Answer', { endpoints: ['rxjs:bob'], }, - { - questionAnswer: new QuestionAnswerModule(), - } + modules ) const aliceAgentOptions = getAgentOptions( @@ -28,37 +29,20 @@ const aliceAgentOptions = getAgentOptions( { endpoints: ['rxjs:alice'], }, - { - questionAnswer: new QuestionAnswerModule(), - } + modules ) describe('Question Answer', () => { - let bobAgent: Agent<{ - questionAnswer: QuestionAnswerModule - }> - let aliceAgent: Agent<{ - questionAnswer: QuestionAnswerModule - }> + let bobAgent: Agent + let aliceAgent: Agent let aliceConnection: ConnectionRecord beforeEach(async () => { - const bobMessages = new Subject() - const aliceMessages = new Subject() - const subjectMap = { - 'rxjs:bob': bobMessages, - 'rxjs:alice': aliceMessages, - } - bobAgent = new Agent(bobAgentOptions) - bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) - bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - await bobAgent.initialize() - aliceAgent = new Agent(aliceAgentOptions) + setupSubjectTransports([bobAgent, aliceAgent]) - aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) - aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await bobAgent.initialize() await aliceAgent.initialize() ;[aliceConnection] = await makeConnection(aliceAgent, bobAgent) }) diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 1da533811d..653c19627e 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -30,8 +30,7 @@ "@types/react-native": "^0.64.10" }, "devDependencies": { - "@types/indy-sdk-react-native": "npm:@types/indy-sdk@1.16.26", - "indy-sdk-react-native": "^0.3.1", + "@types/react-native": "^0.64.10", "react": "17.0.1", "react-native": "0.64.2", "react-native-fs": "^2.18.0", @@ -40,7 +39,6 @@ "typescript": "~4.9.4" }, "peerDependencies": { - "indy-sdk-react-native": "^0.3.1", "react-native-fs": "^2.18.0", "react-native-get-random-values": "^1.7.0" } diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 4b7e0a3eb9..ea76cafbe0 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -4,10 +4,6 @@ import '@azure/core-asynciterator-polyfill' import type { AgentDependencies } from '@aries-framework/core' import { EventEmitter } from 'events' -// Eslint complains indy-sdk-react-native has no default export -// But that's not true -// eslint-disable-next-line import/default -import indy from 'indy-sdk-react-native' import { ReactNativeFileSystem } from './ReactNativeFileSystem' @@ -19,7 +15,6 @@ const agentDependencies: AgentDependencies = { fetch, EventEmitterClass: EventEmitter, WebSocketClass: WebSocket, - indy, } export { agentDependencies } diff --git a/packages/tenants/src/__tests__/TenantAgent.test.ts b/packages/tenants/src/__tests__/TenantAgent.test.ts index ce599ef4bf..1c3bb05cc3 100644 --- a/packages/tenants/src/__tests__/TenantAgent.test.ts +++ b/packages/tenants/src/__tests__/TenantAgent.test.ts @@ -1,6 +1,8 @@ import { Agent, AgentContext } from '@aries-framework/core' +import { indySdk } from '../../../core/tests' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../core/tests/helpers' +import { IndySdkModule } from '../../../indy-sdk/src' import { TenantAgent } from '../TenantAgent' describe('TenantAgent', () => { @@ -14,6 +16,9 @@ describe('TenantAgent', () => { }, }, dependencies: agentDependencies, + modules: { + indySdk: new IndySdkModule({ indySdk }), + }, }) const tenantDependencyManager = agent.dependencyManager.createChild() diff --git a/packages/tenants/src/__tests__/TenantsApi.test.ts b/packages/tenants/src/__tests__/TenantsApi.test.ts index e2c5c28fed..213e0cfda3 100644 --- a/packages/tenants/src/__tests__/TenantsApi.test.ts +++ b/packages/tenants/src/__tests__/TenantsApi.test.ts @@ -1,6 +1,7 @@ import { Agent, AgentContext, InjectionSymbols } from '@aries-framework/core' -import { getAgentContext, getAgentOptions, mockFunction } from '../../../core/tests/helpers' +import { indySdk, getAgentContext, getAgentOptions, mockFunction } from '../../../core/tests' +import { IndySdkModule } from '../../../indy-sdk/src' import { TenantAgent } from '../TenantAgent' import { TenantsApi } from '../TenantsApi' import { TenantAgentContextProvider } from '../context/TenantAgentContextProvider' @@ -15,7 +16,7 @@ const AgentContextProviderMock = TenantAgentContextProvider as jest.Mock { - let bobAgent: Agent<{ - dummy: DummyModule - }> - let aliceAgent: Agent<{ - dummy: DummyModule - }> + let bobAgent: Agent + let aliceAgent: Agent let aliceConnection: ConnectionRecord beforeEach(async () => { diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index e1fc3f2f60..339080b404 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -18,7 +18,10 @@ interface StorageRecord { } @injectable() -export class InMemoryStorageService implements StorageService { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export class InMemoryStorageService = BaseRecord> + implements StorageService +{ public records: { [id: string]: StorageRecord } public constructor(records: { [id: string]: StorageRecord } = {}) { diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts index b7d4233738..199d57a314 100644 --- a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -1,5 +1,6 @@ import type { SubjectMessage } from './transport/SubjectInboundTransport' +import indySdk from 'indy-sdk' import { Subject } from 'rxjs' import { getAgentOptions, makeConnection, waitForBasicMessage } from '../packages/core/tests/helpers' @@ -9,9 +10,6 @@ import { Agent, DependencyManager, InjectionSymbols } from '@aries-framework/cor import { IndySdkModule, IndySdkStorageService, IndySdkWallet } from '@aries-framework/indy-sdk' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' - -import { agentDependencies } from '@aries-framework/node' - import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' // FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved @@ -37,34 +35,40 @@ describe.skip('E2E Askar-Indy SDK Wallet Subject tests', () => { senderDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) senderDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) senderAgent = new Agent( - { - ...getAgentOptions('E2E Wallet Subject Sender Indy', { endpoints: ['rxjs:sender'] }), - modules: { indySdk: new IndySdkModule({ indySdk: agentDependencies.indy }) }, - }, + getAgentOptions( + 'E2E Wallet Subject Sender Indy', + { endpoints: ['rxjs:sender'] }, + { indySdk: new IndySdkModule({ indySdk }) } + ), senderDependencyManager ) // Recipient is an Agent using Askar Wallet - recipientAgent = new Agent({ - ...getAgentOptions('E2E Wallet Subject Recipient Askar', { endpoints: ['rxjs:recipient'] }), - modules: { askar: new AskarModule() }, - }) + recipientAgent = new Agent( + getAgentOptions( + 'E2E Wallet Subject Recipient Askar', + { endpoints: ['rxjs:recipient'] }, + { askar: new AskarModule() } + ) + ) await e2eWalletTest(senderAgent, recipientAgent) }) test('Wallet Subject flow - Askar Sender / Askar Recipient ', async () => { // Sender is an Agent using Askar Wallet - senderAgent = new Agent({ - ...getAgentOptions('E2E Wallet Subject Sender Askar', { endpoints: ['rxjs:sender'] }), - modules: { askar: new AskarModule() }, - }) + senderAgent = new Agent( + getAgentOptions('E2E Wallet Subject Sender Askar', { endpoints: ['rxjs:sender'] }, { askar: new AskarModule() }) + ) // Recipient is an Agent using Askar Wallet - recipientAgent = new Agent({ - ...getAgentOptions('E2E Wallet Subject Recipient Askar', { endpoints: ['rxjs:recipient'] }), - modules: { askar: new AskarModule() }, - }) + recipientAgent = new Agent( + getAgentOptions( + 'E2E Wallet Subject Recipient Askar', + { endpoints: ['rxjs:recipient'] }, + { askar: new AskarModule() } + ) + ) await e2eWalletTest(senderAgent, recipientAgent) }) @@ -75,10 +79,11 @@ describe.skip('E2E Askar-Indy SDK Wallet Subject tests', () => { senderDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) senderDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) senderAgent = new Agent( - { - ...getAgentOptions('E2E Wallet Subject Sender Indy', { endpoints: ['rxjs:sender'] }), - modules: { indySdk: new IndySdkModule({ indySdk: agentDependencies.indy }) }, - }, + getAgentOptions( + 'E2E Wallet Subject Sender Indy', + { endpoints: ['rxjs:sender'] }, + { indySdk: new IndySdkModule({ indySdk }) } + ), senderDependencyManager ) @@ -87,10 +92,11 @@ describe.skip('E2E Askar-Indy SDK Wallet Subject tests', () => { recipientDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) recipientDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) recipientAgent = new Agent( - { - ...getAgentOptions('E2E Wallet Subject Recipient Indy', { endpoints: ['rxjs:recipient'] }), - modules: { indySdk: new IndySdkModule({ indySdk: agentDependencies.indy }) }, - }, + getAgentOptions( + 'E2E Wallet Subject Recipient Indy', + { endpoints: ['rxjs:recipient'] }, + { indySdk: new IndySdkModule({ indySdk }) } + ), recipientDependencyManager ) diff --git a/tests/e2e-http.test.ts b/tests/e2e-http.test.ts index 4bd2396d41..04bc30ea17 100644 --- a/tests/e2e-http.test.ts +++ b/tests/e2e-http.test.ts @@ -1,3 +1,6 @@ +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' + +import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' @@ -5,29 +8,45 @@ import { e2eTest } from './e2e-test' import { HttpOutboundTransport, Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { HttpInboundTransport } from '@aries-framework/node' -const recipientAgentOptions = getAgentOptions('E2E HTTP Recipient', { - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) +const recipientAgentOptions = getAgentOptions( + 'E2E HTTP Recipient', + { + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) const mediatorPort = 3000 -const mediatorAgentOptions = getAgentOptions('E2E HTTP Mediator', { - endpoints: [`http://localhost:${mediatorPort}`], - autoAcceptMediationRequests: true, -}) +const mediatorAgentOptions = getAgentOptions( + 'E2E HTTP Mediator', + { + endpoints: [`http://localhost:${mediatorPort}`], + autoAcceptMediationRequests: true, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) const senderPort = 3001 -const senderAgentOptions = getAgentOptions('E2E HTTP Sender', { - endpoints: [`http://localhost:${senderPort}`], - mediatorPollingInterval: 1000, - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) +const senderAgentOptions = getAgentOptions( + 'E2E HTTP Sender', + { + endpoints: [`http://localhost:${senderPort}`], + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) describe('E2E HTTP tests', () => { - let recipientAgent: Agent - let mediatorAgent: Agent - let senderAgent: Agent + let recipientAgent: AnonCredsTestsAgent + let mediatorAgent: AnonCredsTestsAgent + let senderAgent: AnonCredsTestsAgent beforeEach(async () => { recipientAgent = new Agent(recipientAgentOptions) diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 6f550435fc..6ec26ef417 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -1,35 +1,53 @@ import type { SubjectMessage } from './transport/SubjectInboundTransport' +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { Subject } from 'rxjs' +import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' -const recipientAgentOptions = getAgentOptions('E2E Subject Recipient', { - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) -const mediatorAgentOptions = getAgentOptions('E2E Subject Mediator', { - endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, -}) -const senderAgentOptions = getAgentOptions('E2E Subject Sender', { - endpoints: ['rxjs:sender'], - mediatorPollingInterval: 1000, - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) +const recipientAgentOptions = getAgentOptions( + 'E2E Subject Recipient', + { + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) +const mediatorAgentOptions = getAgentOptions( + 'E2E Subject Mediator', + { + endpoints: ['rxjs:mediator'], + autoAcceptMediationRequests: true, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) +const senderAgentOptions = getAgentOptions( + 'E2E Subject Sender', + { + endpoints: ['rxjs:sender'], + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) describe('E2E Subject tests', () => { - let recipientAgent: Agent - let mediatorAgent: Agent - let senderAgent: Agent + let recipientAgent: AnonCredsTestsAgent + let mediatorAgent: AnonCredsTestsAgent + let senderAgent: AnonCredsTestsAgent beforeEach(async () => { recipientAgent = new Agent(recipientAgentOptions) diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index 0f4c07e6da..b1bee59014 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -1,19 +1,37 @@ -import type { Agent } from '@aries-framework/core' +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { V1CredentialPreview } from '../packages/anoncreds/src/protocols/credentials/v1' +import { + issueLegacyAnonCredsCredential, + presentLegacyAnonCredsProof, + prepareForAnonCredsIssuance, +} from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { sleep } from '../packages/core/src/utils/sleep' -import { issueCredential, makeConnection, prepareForIssuance, presentProof } from '../packages/core/tests/helpers' +import { setupEventReplaySubjects } from '../packages/core/tests' +import { makeConnection } from '../packages/core/tests/helpers' -import { V1CredentialPreview, CredentialState, MediationState, ProofState } from '@aries-framework/core' +import { + CredentialState, + MediationState, + ProofState, + CredentialEventTypes, + ProofEventTypes, +} from '@aries-framework/core' export async function e2eTest({ mediatorAgent, recipientAgent, senderAgent, }: { - mediatorAgent: Agent - recipientAgent: Agent - senderAgent: Agent + mediatorAgent: AnonCredsTestsAgent + recipientAgent: AnonCredsTestsAgent + senderAgent: AnonCredsTestsAgent }) { + const [senderReplay, recipientReplay] = setupEventReplaySubjects( + [senderAgent, recipientAgent], + [CredentialEventTypes.CredentialStateChanged, ProofEventTypes.ProofStateChanged] + ) + // Make connection between mediator and recipient const [mediatorRecipientConnection, recipientMediatorConnection] = await makeConnection(mediatorAgent, recipientAgent) expect(recipientMediatorConnection).toBeConnectedWith(mediatorRecipientConnection) @@ -33,13 +51,20 @@ export async function e2eTest({ expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) // Issue credential from sender to recipient - const { definition } = await prepareForIssuance(senderAgent, ['name', 'age', 'dateOfBirth']) - const { holderCredential, issuerCredential } = await issueCredential({ + const { credentialDefinition } = await prepareForAnonCredsIssuance(senderAgent, { + attributeNames: ['name', 'age', 'dateOfBirth'], + // TODO: update to dynamic created did + issuerId: senderAgent.publicDid?.did as string, + }) + const { holderCredentialExchangeRecord, issuerCredentialExchangeRecord } = await issueLegacyAnonCredsCredential({ issuerAgent: senderAgent, + issuerReplay: senderReplay, holderAgent: recipientAgent, - issuerConnectionId: senderRecipientConnection.id, - credentialTemplate: { - credentialDefinitionId: definition.id, + holderReplay: recipientReplay, + + issuerHolderConnectionId: senderRecipientConnection.id, + offer: { + credentialDefinitionId: credentialDefinition.credentialDefinitionId, attributes: V1CredentialPreview.fromRecord({ name: 'John', age: '25', @@ -49,39 +74,46 @@ export async function e2eTest({ }, }) - expect(holderCredential.state).toBe(CredentialState.Done) - expect(issuerCredential.state).toBe(CredentialState.Done) + expect(holderCredentialExchangeRecord.state).toBe(CredentialState.Done) + expect(issuerCredentialExchangeRecord.state).toBe(CredentialState.Done) // Present Proof from recipient to sender - const definitionRestriction = [ - { - credentialDefinitionId: definition.id, - }, - ] - const { holderProof, verifierProof } = await presentProof({ + const { holderProofExchangeRecord, verifierProofExchangeRecord } = await presentLegacyAnonCredsProof({ verifierAgent: senderAgent, + verifierReplay: senderReplay, + holderAgent: recipientAgent, - verifierConnectionId: senderRecipientConnection.id, - presentationTemplate: { + holderReplay: recipientReplay, + + verifierHolderConnectionId: senderRecipientConnection.id, + request: { attributes: { name: { name: 'name', - restrictions: definitionRestriction, + restrictions: [ + { + cred_def_id: credentialDefinition.credentialDefinitionId, + }, + ], }, }, predicates: { olderThan21: { name: 'age', - restrictions: definitionRestriction, - predicateType: '<=', - predicateValue: 20000712, + restrictions: [ + { + cred_def_id: credentialDefinition.credentialDefinitionId, + }, + ], + p_type: '<=', + p_value: 20000712, }, }, }, }) - expect(holderProof.state).toBe(ProofState.Done) - expect(verifierProof.state).toBe(ProofState.Done) + expect(holderProofExchangeRecord.state).toBe(ProofState.Done) + expect(verifierProofExchangeRecord.state).toBe(ProofState.Done) // We want to stop the mediator polling before the agent is shutdown. await recipientAgent.mediationRecipient.stopMessagePickup() diff --git a/tests/e2e-ws-pickup-v2.test.ts b/tests/e2e-ws-pickup-v2.test.ts index 45c641c7d7..16ace3cd90 100644 --- a/tests/e2e-ws-pickup-v2.test.ts +++ b/tests/e2e-ws-pickup-v2.test.ts @@ -1,34 +1,55 @@ +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' + +import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { getAgentOptions } from '../packages/core/tests/helpers' +import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' + import { e2eTest } from './e2e-test' -import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' -const recipientOptions = getAgentOptions('E2E WS Pickup V2 Recipient ', { - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, -}) +const recipientOptions = getAgentOptions( + 'E2E WS Pickup V2 Recipient ', + { + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) // FIXME: port numbers should not depend on availability from other test suites that use web sockets const mediatorPort = 4100 -const mediatorOptions = getAgentOptions('E2E WS Pickup V2 Mediator', { - endpoints: [`ws://localhost:${mediatorPort}`], - autoAcceptMediationRequests: true, -}) +const mediatorOptions = getAgentOptions( + 'E2E WS Pickup V2 Mediator', + { + endpoints: [`ws://localhost:${mediatorPort}`], + autoAcceptMediationRequests: true, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) const senderPort = 4101 -const senderOptions = getAgentOptions('E2E WS Pickup V2 Sender', { - endpoints: [`ws://localhost:${senderPort}`], - mediatorPollingInterval: 1000, - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, -}) +const senderOptions = getAgentOptions( + 'E2E WS Pickup V2 Sender', + { + endpoints: [`ws://localhost:${senderPort}`], + mediatorPollingInterval: 1000, + + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) describe('E2E WS Pickup V2 tests', () => { - let recipientAgent: Agent - let mediatorAgent: Agent - let senderAgent: Agent + let recipientAgent: AnonCredsTestsAgent + let mediatorAgent: AnonCredsTestsAgent + let senderAgent: AnonCredsTestsAgent beforeEach(async () => { recipientAgent = new Agent(recipientOptions) diff --git a/tests/e2e-ws.test.ts b/tests/e2e-ws.test.ts index e0bd5f27ab..942ea92971 100644 --- a/tests/e2e-ws.test.ts +++ b/tests/e2e-ws.test.ts @@ -1,3 +1,6 @@ +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' + +import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' @@ -5,29 +8,45 @@ import { e2eTest } from './e2e-test' import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' -const recipientAgentOptions = getAgentOptions('E2E WS Recipient ', { - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) +const recipientAgentOptions = getAgentOptions( + 'E2E WS Recipient ', + { + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) const mediatorPort = 4000 -const mediatorAgentOptions = getAgentOptions('E2E WS Mediator', { - endpoints: [`ws://localhost:${mediatorPort}`], - autoAcceptMediationRequests: true, -}) +const mediatorAgentOptions = getAgentOptions( + 'E2E WS Mediator', + { + endpoints: [`ws://localhost:${mediatorPort}`], + autoAcceptMediationRequests: true, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) const senderPort = 4001 -const senderAgentOptions = getAgentOptions('E2E WS Sender', { - endpoints: [`ws://localhost:${senderPort}`], - mediatorPollingInterval: 1000, - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, -}) +const senderAgentOptions = getAgentOptions( + 'E2E WS Sender', + { + endpoints: [`ws://localhost:${senderPort}`], + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }) +) describe('E2E WS tests', () => { - let recipientAgent: Agent - let mediatorAgent: Agent - let senderAgent: Agent + let recipientAgent: AnonCredsTestsAgent + let mediatorAgent: AnonCredsTestsAgent + let senderAgent: AnonCredsTestsAgent beforeEach(async () => { recipientAgent = new Agent(recipientAgentOptions) diff --git a/yarn.lock b/yarn.lock index 0a86c85b57..1c48eeb5a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -858,12 +858,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.5": - version "0.1.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.5.tgz#71b6dbcfab72f826bcead2b79dafe47fc8f1567c" - integrity sha512-BX/OxQjTMoCAJP4fgJEcGct1ZnNYgybO+VLD5LyzHW4nmTFOJo3TXy5IYHAJv61b/uNUQ/2GMYmPKLSLOVExNw== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.6": + version "0.1.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.6.tgz#db90e9de4a05e1446132048c07f54503afee4272" + integrity sha512-0BKTEVf1ovkGZGEsMK1jj545jIX48nYQwjKakMmKnSFrb62mtS7VkZ+5kQWp8fGzcTjr3h6Lo/G7TaMIDgQ6Ww== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.5" + "@hyperledger/anoncreds-shared" "0.1.0-dev.6" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -871,17 +871,17 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.5", "@hyperledger/anoncreds-shared@^0.1.0-dev.5": - version "0.1.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.5.tgz#653a8ec1ac83eae3af8aabb7fa5609bb0c3453b2" - integrity sha512-NPbjZd7WJN/eKtHtYcOy+E9Ebh0YkZ7bre59zWD3w66aiehZrSLbL5+pjY9shrSIN1h05t0XnvT1JZKTtXgqcQ== +"@hyperledger/anoncreds-shared@0.1.0-dev.6", "@hyperledger/anoncreds-shared@^0.1.0-dev.6": + version "0.1.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.6.tgz#2e6afb4641cc25daef4074a32990ec078d2fd94e" + integrity sha512-4YZ2kzhOOrGRL//n/Qe/A+yyGsbnHqojQW6vGEZQpZ9bf4ir+QaZLRHijgXFmffMA+SRONfdlnxhLJ6/b6ZaMg== -"@hyperledger/aries-askar-nodejs@^0.1.0-dev.1": - version "0.1.0-dev.1" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.1.tgz#b384d422de48f0ce5918e1612d2ca32ebd160520" - integrity sha512-XrRskQ0PaNAerItvfxKkS8YaVg+iuImguoqfyQ4ZSaePKZQnTqZpkxo6faKS+GlsaubRXz/6yz3YndVRIxPO+w== +"@hyperledger/aries-askar-nodejs@^0.1.0-dev.3": + version "0.1.0-dev.3" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.3.tgz#19ecff41f81525efea8212a3ad6b8c3db11950c4" + integrity sha512-9hnCNWxIRkLP793P4DuZAJRWfxf1v6NdQyEgoMdNletcP7KAf/YfBqySTYGqA6TIiMu/abNrmq+WsHkK0yyZ+g== dependencies: - "@hyperledger/aries-askar-shared" "0.1.0-dev.1" + "@hyperledger/aries-askar-shared" "0.1.0-dev.3" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" node-cache "^5.1.2" @@ -889,29 +889,29 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.1.0-dev.1", "@hyperledger/aries-askar-shared@^0.1.0-dev.1": - version "0.1.0-dev.1" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.1.tgz#4e4e494c3a44c7c82f7b95ad4f06149f2a3a9b6c" - integrity sha512-Pt525M6CvnE3N6jxMpSqLy7RpOsc4oqa2Q+hc2UdCHuSYwmM/aeqt6wiA5dpghvl8g/78lCi1Dz74pzp7Dmm3w== +"@hyperledger/aries-askar-shared@0.1.0-dev.3", "@hyperledger/aries-askar-shared@^0.1.0-dev.3": + version "0.1.0-dev.3" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.3.tgz#2056db8c0671ec4b1e926e1491fdca9357ede633" + integrity sha512-LIRyCg2PK6wN483Bdzq4eJmQ2LNCCRq2g7GF4yv+H+V04ky7hdeoJbSKN8lYr/OQn1tS6ALx9p2ArvAt7pTfVw== dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.4": - version "0.1.0-dev.4" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.4.tgz#b5d2090b30c4a51e4e4f15a024054aada0d3550e" - integrity sha512-SwvcoOONhxD9LaX7vunNi1KFKmDb8wmutkBI+Hl6JMX3R+0QgpyQx5M3cfp+V34fBS8pqzKbq9lQmo+pDu3IWg== +"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.6": + version "0.1.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.6.tgz#28946107feb6c641839de843cc7ddca9eef01f8d" + integrity sha512-jtFRkfjiveKIfeyTx9qDAUXLtpFaiZlUGN2ts2zX8QvY6XGZEKc6LvhMPzLW5kLW34u6ldEqjG4mjyAawUKRsA== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.4" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.6" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" ref-array-di "^1.2.2" ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.4", "@hyperledger/indy-vdr-shared@^0.1.0-dev.4": - version "0.1.0-dev.4" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.4.tgz#ad9ff18ea285cf3c8ba0b4a5bff03c02f57898e4" - integrity sha512-M6AnLQNryEqcWiH8oNNI/ovkFOykFg7zlO4oM+1xMbHbNzAe6ShBYQDB189zTQAG4RUkuA8yiLHt90g/q6N8dg== +"@hyperledger/indy-vdr-shared@0.1.0-dev.6", "@hyperledger/indy-vdr-shared@^0.1.0-dev.6": + version "0.1.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.6.tgz#345b6ff60d29d74615ee882e225af674315a0bda" + integrity sha512-MIUdm3zIwKfFZmZSwbAPiZHEZE0HhsIPjeEWiu//Z1m9GDDSNhEyxsHuVN17pE0pcxwbuCQrIaK3v4Tc6x0jJw== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -2331,7 +2331,7 @@ dependencies: "@stablelib/int" "^1.0.1" -"@stablelib/ed25519@^1.0.2": +"@stablelib/ed25519@^1.0.2", "@stablelib/ed25519@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@stablelib/ed25519/-/ed25519-1.0.3.tgz#f8fdeb6f77114897c887bb6a3138d659d3f35996" integrity sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg== @@ -2534,10 +2534,10 @@ dependencies: "@types/node" "*" -"@types/indy-sdk-react-native@npm:@types/indy-sdk@1.16.24", "@types/indy-sdk@1.16.24": - version "1.16.24" - resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.24.tgz#1b8e33e8fd2a095a29cb06b76146ed14d1477cdd" - integrity sha512-5YliU8lqahihz46MPpiu1ZWNkG2c/lm9SI+Fp3DUV2HrGbuAPxI8dYg2CP6avuD5kfCYr6Y5+TaqeOH/aID0FQ== +"@types/indy-sdk@1.16.26", "@types/indy-sdk@^1.16.26": + version "1.16.26" + resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.26.tgz#871f82c3f7d241d649aff5eb6800048890efb8f8" + integrity sha512-KlnjsVsX/7yTmyyIlHWcytlBHoQ1vPGeiLnLv5y1vDftL6OQ5V+hebfAr7d3roMEsjCTH3qKkklwGcj1qS90YA== dependencies: buffer "^6.0.0" @@ -3534,7 +3534,7 @@ bindings@^1.3.1: dependencies: file-uri-to-path "1.0.0" -bn.js@^5.2.0, bn.js@^5.2.1: +bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== @@ -3656,7 +3656,7 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^6.0.0, buffer@^6.0.2, buffer@^6.0.3: +buffer@^6.0.0, buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== @@ -6178,14 +6178,7 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indy-sdk-react-native@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/indy-sdk-react-native/-/indy-sdk-react-native-0.3.0.tgz#37b20476bf1207d3dea7b66dba65bf44ed0c903a" - integrity sha512-3qaB4R7QDNQRI9ijpSvMaow/HlZYMB2LdJlRtbhefmrjQYwpz9oSqB595NPKajBIoIxzgDaUdBkK7kmwMY90Xg== - dependencies: - buffer "^6.0.2" - -indy-sdk@^1.16.0-dev-1636: +indy-sdk@^1.16.0-dev-1636, indy-sdk@^1.16.0-dev-1655: version "1.16.0-dev-1655" resolved "https://registry.yarnpkg.com/indy-sdk/-/indy-sdk-1.16.0-dev-1655.tgz#098c38df4a6eb4e13f89c0b86ebe9636944b71e0" integrity sha512-MSWRY8rdnGAegs4v4AnzE6CT9O/3JBMUiE45I0Ihj2DMuH+XS1EJZUQEJsyis6aOQzRavv/xVtaBC8o+6azKuw== @@ -10029,7 +10022,7 @@ rxjs@^6.6.0: dependencies: tslib "^1.9.0" -rxjs@^7.2.0: +rxjs@^7.2.0, rxjs@^7.8.0: version "7.8.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== From edf392fdf561cf8f6a83ecb2a9e08308827de8fc Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 20 Feb 2023 12:14:21 +0100 Subject: [PATCH 054/139] refactor: remove master secret id from wallet (#1320) Signed-off-by: Timo Glastra --- .../src/services/AnonCredsRsHolderService.ts | 4 +- .../legacy-indy-format-services.test.ts | 20 +++++- .../v1-connectionless-proofs.e2e.test.ts | 5 ++ .../anoncreds/tests/legacyAnonCredsSetup.ts | 6 ++ packages/askar/src/wallet/AskarWallet.ts | 12 ---- .../src/wallet/__tests__/AskarWallet.test.ts | 8 --- .../v2-connectionless-credentials.e2e.test.ts | 6 ++ ...f.credentials.propose-offerED25519.test.ts | 6 ++ .../v2-indy-connectionless-proofs.e2e.test.ts | 5 ++ packages/core/src/types.ts | 1 - .../services/IndySdkHolderService.ts | 33 ++++++++-- packages/indy-sdk/src/wallet/IndySdkWallet.ts | 62 ------------------- .../wallet/__tests__/IndySdkWallet.test.ts | 33 ---------- tests/e2e-test.ts | 3 + 14 files changed, 81 insertions(+), 123 deletions(-) diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index 4a34024851..b4c1e02f53 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -199,7 +199,9 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { if (!linkSecretRecord) { // No default link secret - throw new AnonCredsRsError('No default link secret has been found') + throw new AnonCredsRsError( + 'No link secret provided to createCredentialRequest and no default link secret has been found' + ) } const { credentialRequest, credentialRequestMetadata } = CredentialRequest.create({ diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 33a306617a..850e9c9a0f 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -6,13 +6,16 @@ import { CredentialPreviewAttribute, ProofExchangeRecord, ProofState, + EventEmitter, } from '@aries-framework/core' import * as indySdk from 'indy-sdk' +import { Subject } from 'rxjs' -import { getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { IndySdkHolderService, IndySdkIssuerService, + IndySdkStorageService, IndySdkVerifierService, IndySdkWallet, } from '../../../../indy-sdk/src' @@ -20,6 +23,7 @@ import { IndySdkRevocationService } from '../../../../indy-sdk/src/anoncreds/ser import { indyDidFromPublicKeyBase58 } from '../../../../indy-sdk/src/utils/did' import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' import { AnonCredsModuleConfig } from '../../AnonCredsModuleConfig' +import { AnonCredsLinkSecretRecord, AnonCredsLinkSecretRepository } from '../../repository' import { AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, @@ -40,6 +44,9 @@ const anonCredsVerifierService = new IndySdkVerifierService(indySdk) const anonCredsHolderService = new IndySdkHolderService(anonCredsRevocationService, indySdk) const anonCredsIssuerService = new IndySdkIssuerService(indySdk) const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const storageService = new IndySdkStorageService(indySdk) +const eventEmitter = new EventEmitter(agentDependencies, new Subject()) +const anonCredsLinkSecretRepository = new AnonCredsLinkSecretRepository(storageService, eventEmitter) const agentContext = getAgentContext({ registerInstances: [ [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], @@ -47,6 +54,7 @@ const agentContext = getAgentContext({ [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], [AnonCredsRegistryService, new AnonCredsRegistryService()], [AnonCredsModuleConfig, anonCredsModuleConfig], + [AnonCredsLinkSecretRepository, anonCredsLinkSecretRepository], ], agentConfig, wallet, @@ -71,6 +79,16 @@ describe('Legacy indy format services', () => { const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) const indyDid = indyDidFromPublicKeyBase58(key.publicKeyBase58) + // Create link secret + await anonCredsHolderService.createLinkSecret(agentContext, { + linkSecretId: 'link-secret-id', + }) + const anonCredsLinkSecret = new AnonCredsLinkSecretRecord({ + linkSecretId: 'link-secret-id', + }) + anonCredsLinkSecret.setTag('isDefault', true) + await anonCredsLinkSecretRepository.save(agentContext, anonCredsLinkSecret) + const schema = await anonCredsIssuerService.createSchema(agentContext, { attrNames: ['name', 'age'], issuerId: indyDid, diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index df66a6217d..be1b7c1deb 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -332,6 +332,11 @@ describe('V1 Proofs - Connectionless - Indy', () => { expect(faberConnection.isReady).toBe(true) expect(aliceConnection.isReady).toBe(true) + await aliceAgent.modules.anoncreds.createLinkSecret({ + linkSecretId: 'default', + setAsDefault: true, + }) + await issueLegacyAnonCredsCredential({ issuerAgent: faberAgent, issuerReplay: faberReplay, diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index e5a1082b64..9b6da7fd32 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -329,6 +329,12 @@ export async function setupAnonCredsTests< await holderAgent.initialize() if (verifierAgent) await verifierAgent.initialize() + // Create default link secret for holder + await holderAgent.modules.anoncreds.createLinkSecret({ + linkSecretId: 'default', + setAsDefault: true, + }) + const { credentialDefinition, schema } = await prepareForAnonCredsIssuance(issuerAgent, { attributeNames, // TODO: replace with more dynamic / generic value We should create a did using the dids module diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 02564c4d0a..0fc11a3237 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -110,16 +110,6 @@ export class AskarWallet implements Wallet { return this._session } - public get masterSecretId() { - if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) { - throw new AriesFrameworkError( - 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' - ) - } - - return this.walletConfig?.masterSecretId ?? this.walletConfig.id - } - /** * Dispose method is called when an agent context is disposed. */ @@ -156,8 +146,6 @@ export class AskarWallet implements Wallet { }) this.walletConfig = walletConfig this._session = await this._store.openSession() - - // TODO: Master Secret creation (now part of IndyCredx/AnonCreds) } catch (error) { // FIXME: Askar should throw a Duplicate error code, but is currently returning Encryption // And if we provide the very same wallet key, it will open it without any error diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 40dea0cbc8..e5ac122786 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -49,10 +49,6 @@ describe('AskarWallet basic operations', () => { await askarWallet.delete() }) - test('Get the Master Secret', () => { - expect(askarWallet.masterSecretId).toEqual('Wallet: AskarWalletTest') - }) - test('Get the wallet store', () => { expect(askarWallet.store).toEqual(expect.any(Store)) }) @@ -124,10 +120,6 @@ describe('AskarWallet basic operations', () => { }) await expect(askarWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) }) - - test('masterSecretId is equal to wallet ID by default', async () => { - expect(askarWallet.masterSecretId).toEqual(walletConfig.id) - }) }) describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index 95c48a3ee9..cb2fd2b0c1 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -66,6 +66,12 @@ describe('V2 Connectionless Credentials', () => { aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await aliceAgent.initialize() + // Create link secret for alice + await aliceAgent.modules.anoncreds.createLinkSecret({ + linkSecretId: 'default', + setAsDefault: true, + }) + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { issuerId: faberAgent.publicDid?.did as string, attributeNames: ['name', 'age'], diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 0c7bd46567..114cf1cf2d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -168,6 +168,12 @@ describe('V2 Credentials - JSON-LD - Ed25519', () => { await aliceAgent.initialize() ;[, { id: aliceConnectionId }] = await makeConnection(faberAgent, aliceAgent) + // Create link secret for alice + await aliceAgent.modules.anoncreds.createLinkSecret({ + linkSecretId: 'default', + setAsDefault: true, + }) + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'profile_picture', 'x-ray'], issuerId: faberAgent.publicDid?.did as string, diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index 0482ed4a86..5b343f3471 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -322,6 +322,11 @@ describe('V2 Connectionless Proofs - Indy', () => { ) agents = [aliceAgent, faberAgent, mediatorAgent] + await aliceAgent.modules.anoncreds.createLinkSecret({ + linkSecretId: 'default', + setAsDefault: true, + }) + const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'image_0', 'image_1'], issuerId: faberAgent.publicDid?.did as string, diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index f621d4393e..4cd29a4ba2 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -22,7 +22,6 @@ export interface WalletConfig { key: string keyDerivationMethod?: KeyDerivationMethod storage?: WalletStorageConfig - masterSecretId?: string } export interface WalletConfigRekey { diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index d5e82deea7..394ed39198 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -25,7 +25,8 @@ import type { IndyProofRequest, } from 'indy-sdk' -import { injectable, inject, utils } from '@aries-framework/core' +import { AnonCredsLinkSecretRepository } from '@aries-framework/anoncreds' +import { AriesFrameworkError, injectable, inject, utils } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' @@ -80,6 +81,8 @@ export class IndySdkHolderService implements AnonCredsHolderService { assertIndySdkWallet(agentContext.wallet) + const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) + try { agentContext.config.logger.debug('Creating Indy Proof') const indyRevocationStates: RevStates = await this.indyRevocationService.createRevocationState( @@ -114,11 +117,19 @@ export class IndySdkHolderService implements AnonCredsHolderService { indySchemas[schemaId] = indySdkSchemaFromAnonCreds(schemaId, schema, seqNoMap[schemaId]) } + const linkSecretRecord = await linkSecretRepository.findDefault(agentContext) + if (!linkSecretRecord) { + // No default link secret + throw new AriesFrameworkError( + 'No default link secret found. Indy SDK requires a default link secret to be created before creating a proof.' + ) + } + const indyProof = await this.indySdk.proverCreateProof( agentContext.wallet.handle, proofRequest as IndyProofRequest, this.parseSelectedCredentials(selectedCredentials), - agentContext.wallet.masterSecretId, + linkSecretRecord.linkSecretId, indySchemas, indyCredentialDefinitions, indyRevocationStates @@ -201,10 +212,24 @@ export class IndySdkHolderService implements AnonCredsHolderService { ): Promise { assertIndySdkWallet(agentContext.wallet) + const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) + // We just generate a prover did like string, as it's not used for anything and we don't need // to prove ownership of the did. It's deprecated in AnonCreds v1, but kept for backwards compatibility const proverDid = generateLegacyProverDidLikeString() + // If a link secret is specified, use it. Otherwise, attempt to use default link secret + const linkSecretRecord = options.linkSecretId + ? await linkSecretRepository.getByLinkSecretId(agentContext, options.linkSecretId) + : await linkSecretRepository.findDefault(agentContext) + + if (!linkSecretRecord) { + // No default link secret + throw new AriesFrameworkError( + 'No link secret provided to createCredentialRequest and no default link secret has been found' + ) + } + try { const result = await this.indySdk.proverCreateCredentialReq( agentContext.wallet.handle, @@ -213,9 +238,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { // NOTE: Is it safe to use the cred_def_id from the offer? I think so. You can't create a request // for a cred def that is not in the offer indySdkCredentialDefinitionFromAnonCreds(options.credentialOffer.cred_def_id, options.credentialDefinition), - // FIXME: we need to remove the masterSecret from the wallet, as it is AnonCreds specific - // Issue: https://github.com/hyperledger/aries-framework-javascript/issues/1198 - agentContext.wallet.masterSecretId + linkSecretRecord.linkSecretId ) return { diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index 043e079c05..16561bbcf1 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -81,16 +81,6 @@ export class IndySdkWallet implements Wallet { return this.walletHandle } - public get masterSecretId() { - if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) { - throw new AriesFrameworkError( - 'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.' - ) - } - - return this.walletConfig?.masterSecretId ?? this.walletConfig.id - } - /** * Dispose method is called when an agent context is disposed. */ @@ -155,15 +145,8 @@ export class IndySdkWallet implements Wallet { await this.indySdk.createWallet(this.walletStorageConfig(walletConfig), this.walletCredentials(walletConfig)) this.walletConfig = walletConfig - // We usually want to create master secret only once, therefore, we can to do so when creating a wallet. await this.open(walletConfig) - - // We need to open wallet before creating master secret because we need wallet handle here. - await this.createMasterSecret(this.handle, this.masterSecretId) } catch (error) { - // If an error ocurred while creating the master secret, we should close the wallet - if (this.isInitialized) await this.close() - if (isIndyError(error, 'WalletAlreadyExistsError')) { const errorMessage = `Wallet '${walletConfig.id}' already exists` this.logger.debug(errorMessage) @@ -394,51 +377,6 @@ export class IndySdkWallet implements Wallet { } } - /** - * Create master secret with specified id in currently opened wallet. - * - * If a master secret by this id already exists in the current wallet, the method - * will return without doing anything. - * - * @throws {WalletError} if an error occurs - */ - private async createMasterSecret(walletHandle: number, masterSecretId: string): Promise { - this.logger.debug(`Creating master secret with id '${masterSecretId}' in wallet with handle '${walletHandle}'`) - - try { - await this.indySdk.proverCreateMasterSecret(walletHandle, masterSecretId) - - return masterSecretId - } catch (error) { - if (isIndyError(error, 'AnoncredsMasterSecretDuplicateNameError')) { - // master secret id is the same as the master secret id passed in the create function - // so if it already exists we can just assign it. - this.logger.debug( - `Master secret with id '${masterSecretId}' already exists in wallet with handle '${walletHandle}'`, - { - indyError: 'AnoncredsMasterSecretDuplicateNameError', - } - ) - - return masterSecretId - } else { - if (!isIndyError(error)) { - throw new AriesFrameworkError('Attempted to throw Indy error, but it was not an Indy error') - } - - this.logger.error(`Error creating master secret with id ${masterSecretId}`, { - indyError: error.indyName, - error, - }) - - throw new WalletError( - `Error creating master secret with id ${masterSecretId} in wallet with handle '${walletHandle}'`, - { cause: error } - ) - } - } - } - public async initPublicDid(didConfig: DidConfig) { const { did, verkey } = await this.createDid(didConfig) this.publicDidInfo = { diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts index c80ea47b4a..dff7241b85 100644 --- a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -20,14 +20,6 @@ const walletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } -const walletConfigWithMasterSecretId: WalletConfig = { - id: 'Wallet: WalletTestWithMasterSecretId', - // generated using indy.generateWalletKey - key: 'CwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrb', - keyDerivationMethod: KeyDerivationMethod.Raw, - masterSecretId: 'customMasterSecretId', -} - describe('IndySdkWallet', () => { let indySdkWallet: IndySdkWallet @@ -51,10 +43,6 @@ describe('IndySdkWallet', () => { }) }) - test('Get the Master Secret', () => { - expect(indySdkWallet.masterSecretId).toEqual('Wallet: IndySdkWalletTest') - }) - test('Get the wallet handle', () => { expect(indySdkWallet.handle).toEqual(expect.any(Number)) }) @@ -108,25 +96,4 @@ describe('IndySdkWallet', () => { }) await expect(indySdkWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true) }) - - test('masterSecretId is equal to wallet ID by default', async () => { - expect(indySdkWallet.masterSecretId).toEqual(walletConfig.id) - }) -}) - -describe('IndySdkWallet with custom Master Secret Id', () => { - let indySdkWallet: IndySdkWallet - - beforeEach(async () => { - indySdkWallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([])) - await indySdkWallet.createAndOpen(walletConfigWithMasterSecretId) - }) - - afterEach(async () => { - await indySdkWallet.delete() - }) - - test('masterSecretId is set by config', async () => { - expect(indySdkWallet.masterSecretId).toEqual(walletConfigWithMasterSecretId.masterSecretId) - }) }) diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index b1bee59014..34960d8d8f 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -50,6 +50,9 @@ export async function e2eTest({ const [recipientSenderConnection, senderRecipientConnection] = await makeConnection(recipientAgent, senderAgent) expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) + // Create link secret with default options. This should create a default link secret. + await recipientAgent.modules.anoncreds.createLinkSecret() + // Issue credential from sender to recipient const { credentialDefinition } = await prepareForAnonCredsIssuance(senderAgent, { attributeNames: ['name', 'age', 'dateOfBirth'], From c0e5339edfa32df92f23fb9c920796b4b59adf52 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 20 Feb 2023 09:04:20 -0300 Subject: [PATCH 055/139] fix: seed and private key validation and return type in registrars (#1324) Signed-off-by: Ariel Gentile --- packages/askar/src/wallet/AskarWallet.ts | 12 ++++++++- .../src/wallet/__tests__/AskarWallet.test.ts | 2 +- .../tests/bbs-signing-provider.e2e.test.ts | 4 +-- packages/core/src/crypto/index.ts | 1 + packages/core/src/crypto/keyUtils.ts | 27 +++++++++++++++++++ .../dids/__tests__/dids-registrar.e2e.test.ts | 8 +++--- .../dids/methods/key/KeyDidRegistrar.ts | 26 ++---------------- .../key/__tests__/KeyDidRegistrar.test.ts | 9 ++++--- .../dids/methods/peer/PeerDidRegistrar.ts | 26 ++---------------- .../peer/__tests__/PeerDidRegistrar.test.ts | 9 ++++--- packages/indy-sdk/package.json | 2 +- .../src/dids/IndySdkSovDidRegistrar.ts | 6 ++--- .../__tests__/IndySdkSovDidRegistrar.test.ts | 8 +++--- packages/indy-sdk/src/wallet/IndySdkWallet.ts | 12 ++++++++- .../tests/sov-did-registrar.e2e.test.ts | 2 +- .../src/dids/IndyVdrIndyDidRegistrar.ts | 26 ++---------------- 16 files changed, 84 insertions(+), 96 deletions(-) create mode 100644 packages/core/src/crypto/keyUtils.ts diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 0fc11a3237..b11c5f9371 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -14,6 +14,8 @@ import type { import type { Session } from '@hyperledger/aries-askar-shared' import { + isValidSeed, + isValidPrivateKey, JsonTransformer, RecordNotFoundError, RecordDuplicateError, @@ -344,7 +346,15 @@ export class AskarWallet implements Wallet { public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { if (seed && privateKey) { - throw new AriesFrameworkError('Only one of seed and privateKey can be set') + throw new WalletError('Only one of seed and privateKey can be set') + } + + if (seed && !isValidSeed(seed, keyType)) { + throw new WalletError('Invalid seed provided') + } + + if (privateKey && !isValidPrivateKey(privateKey, keyType)) { + throw new WalletError('Invalid private key provided') } if (keyTypeSupportedByAskar(keyType)) { diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index e5ac122786..37d28b14b6 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -36,7 +36,7 @@ const walletConfig: WalletConfig = { describe('AskarWallet basic operations', () => { let askarWallet: AskarWallet - const seed = TypedArrayEncoder.fromString('sample-seed') + const seed = TypedArrayEncoder.fromString('sample-seed-min-of-32-bytes-long') const privateKey = TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67') const message = TypedArrayEncoder.fromString('sample-message') diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index e956c633d7..67e2112e96 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -26,7 +26,7 @@ const walletConfig: WalletConfig = { describeSkipNode17And18('BBS Signing Provider', () => { let wallet: Wallet - const seed = TypedArrayEncoder.fromString('sample-seed') + const seed = TypedArrayEncoder.fromString('sample-seed-min-of-32-bytes-long') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { @@ -41,7 +41,7 @@ describeSkipNode17And18('BBS Signing Provider', () => { test('Create bls12381g2 keypair', async () => { await expect(wallet.createKey({ seed, keyType: KeyType.Bls12381g2 })).resolves.toMatchObject({ publicKeyBase58: - 't54oLBmhhRcDLUyWTvfYRWw8VRXRy1p43pVm62hrpShrYPuHe9WNAgS33DPfeTK6xK7iPrtJDwCHZjYgbFYDVTJHxXex9xt2XEGF8D356jBT1HtqNeucv3YsPLfTWcLcpFA', + '25TvGExLTWRTgn9h2wZuohrQmmLafXiacY4dhv66wcbY8pLbuNTBRMTgWVcPKh2wsEyrRPmnhLdc4C7LEcJ2seoxzBkoydJEdQD8aqg5dw8wesBTS9Twg8EjuFG1WPRAiERd', keyType: KeyType.Bls12381g2, }) }) diff --git a/packages/core/src/crypto/index.ts b/packages/core/src/crypto/index.ts index 449f3e537f..45f8e62972 100644 --- a/packages/core/src/crypto/index.ts +++ b/packages/core/src/crypto/index.ts @@ -2,6 +2,7 @@ export { Jwk } from './JwkTypes' export { JwsService } from './JwsService' export * from './jwtUtils' +export * from './keyUtils' export { KeyType } from './KeyType' export { Key } from './Key' diff --git a/packages/core/src/crypto/keyUtils.ts b/packages/core/src/crypto/keyUtils.ts new file mode 100644 index 0000000000..9022d2095d --- /dev/null +++ b/packages/core/src/crypto/keyUtils.ts @@ -0,0 +1,27 @@ +import { Buffer } from '../utils' + +import { KeyType } from './KeyType' + +export function isValidSeed(seed: Buffer, keyType: KeyType): boolean { + const minimumSeedLength: Record = { + [KeyType.Ed25519]: 32, + [KeyType.X25519]: 32, + [KeyType.Bls12381g1]: 32, + [KeyType.Bls12381g2]: 32, + [KeyType.Bls12381g1g2]: 32, + } + + return Buffer.isBuffer(seed) && seed.length >= minimumSeedLength[keyType] +} + +export function isValidPrivateKey(privateKey: Buffer, keyType: KeyType): boolean { + const privateKeyLength: Record = { + [KeyType.Ed25519]: 32, + [KeyType.X25519]: 32, + [KeyType.Bls12381g1]: 32, + [KeyType.Bls12381g2]: 32, + [KeyType.Bls12381g1g2]: 32, + } + + return Buffer.isBuffer(privateKey) && privateKey.length === privateKeyLength[keyType] +} diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index 70aa731310..590933f882 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -90,12 +90,14 @@ describe('dids', () => { ], id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', }, - secret: { privateKey: '96213c3d7fc8d4d6754c7a0fd969598e' }, + secret: { privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e') }, }, }) }) it('should create a did:peer did', async () => { + const privateKey = TypedArrayEncoder.fromString('e008ef10b7c163114b3857542b3736eb') + const did = await agent.dids.create({ method: 'peer', options: { @@ -103,7 +105,7 @@ describe('dids', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - privateKey: TypedArrayEncoder.fromString('e008ef10b7c163114b3857542b3736eb'), + privateKey, }, }) @@ -153,7 +155,7 @@ describe('dids', () => { ], id: 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', }, - secret: { privateKey: 'e008ef10b7c163114b3857542b3736eb' }, + secret: { privateKey }, }, }) }) diff --git a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts index 645deee3a1..d4c8c239c9 100644 --- a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts @@ -30,28 +30,6 @@ export class KeyDidRegistrar implements DidRegistrar { } } - if (seed && (typeof seed !== 'object' || seed.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid seed provided', - }, - } - } - - if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid private key provided', - }, - } - } - try { const key = await agentContext.wallet.createKey({ keyType, @@ -81,8 +59,8 @@ export class KeyDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed?.toString(), - privateKey: options.secret?.privateKey?.toString(), + seed: options.secret?.seed, + privateKey: options.secret?.privateKey, }, }, } diff --git a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts index 9f084a5b8d..d3bf481409 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts @@ -5,6 +5,7 @@ import { KeyType } from '../../../../../crypto' import { Key } from '../../../../../crypto/Key' import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { WalletError } from '../../../../../wallet/error' import { DidDocumentRole } from '../../../domain/DidDocumentRole' import { DidRepository } from '../../../repository/DidRepository' import { KeyDidRegistrar } from '../KeyDidRegistrar' @@ -53,7 +54,7 @@ describe('DidRegistrar', () => { did: 'did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU', didDocument: didKeyz6MksLeFixture, secret: { - privateKey: '96213c3d7fc8d4d6754c712fd969598e', + privateKey, }, }, }) @@ -78,10 +79,10 @@ describe('DidRegistrar', () => { }) }) - it('should return an error state if an invalid private key is provided', async () => { + it('should return an error state if a key creation error is thrown', async () => { + mockFunction(walletMock.createKey).mockRejectedValueOnce(new WalletError('Invalid private key provided')) const result = await keyDidRegistrar.create(agentContext, { method: 'key', - options: { keyType: KeyType.Ed25519, }, @@ -95,7 +96,7 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid private key provided', + reason: expect.stringContaining('Invalid private key provided'), }, }) }) diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts index 83b171e978..fcae22119e 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts @@ -41,28 +41,6 @@ export class PeerDidRegistrar implements DidRegistrar { } } - if (seed && (typeof seed !== 'object' || seed.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid seed provided', - }, - } - } - - if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid private key provided', - }, - } - } - const key = await agentContext.wallet.createKey({ keyType, seed, @@ -119,8 +97,8 @@ export class PeerDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed?.toString(), - privateKey: options.secret?.privateKey?.toString(), + seed: options.secret?.seed, + privateKey: options.secret?.privateKey, }, }, } diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts index b974cff303..50b862f772 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts @@ -5,6 +5,7 @@ import { KeyType } from '../../../../../crypto' import { Key } from '../../../../../crypto/Key' import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { WalletError } from '../../../../../wallet/error' import { DidCommV1Service, DidDocumentBuilder } from '../../../domain' import { DidDocumentRole } from '../../../domain/DidDocumentRole' import { getEd25519VerificationMethod } from '../../../domain/key-type/ed25519' @@ -54,7 +55,7 @@ describe('DidRegistrar', () => { did: 'did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU', didDocument: didPeer0z6MksLeFixture, secret: { - privateKey: '96213c3d7fc8d4d6754c712fd969598e', + privateKey, }, }, }) @@ -79,7 +80,9 @@ describe('DidRegistrar', () => { }) }) - it('should return an error state if an invalid private key is provided', async () => { + it('should return an error state if a key creation error is thrown', async () => { + mockFunction(walletMock.createKey).mockRejectedValueOnce(new WalletError('Invalid private key provided')) + const result = await peerDidRegistrar.create(agentContext, { method: 'peer', options: { @@ -96,7 +99,7 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid private key provided', + reason: expect.stringContaining('Invalid private key provided'), }, }) }) diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 8e6acc74c4..12d019bdae 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -27,13 +27,13 @@ "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", "@types/indy-sdk": "1.16.26", + "@stablelib/ed25519": "^1.0.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@stablelib/ed25519": "^1.0.3", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts index 7f7cf38ebd..4c88f84427 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -13,7 +13,7 @@ import type { } from '@aries-framework/core' import type { NymRole } from 'indy-sdk' -import { DidDocumentRole, DidRecord, DidRepository } from '@aries-framework/core' +import { KeyType, isValidPrivateKey, DidDocumentRole, DidRecord, DidRepository } from '@aries-framework/core' import { IndySdkError } from '../error' import { isIndyError } from '../error/indyError' @@ -34,7 +34,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { const { alias, role, submitterDid, indyNamespace } = options.options const privateKey = options.secret?.privateKey - if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { + if (privateKey && !isValidPrivateKey(privateKey, KeyType.Ed25519)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -120,7 +120,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - privateKey: options.secret?.privateKey?.toString(), + privateKey: options.secret?.privateKey, }, }, } diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts index ab3f31bd4f..29ea34618c 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts @@ -131,7 +131,7 @@ describe('IndySdkSovDidRegistrar', () => { }) it('should correctly create a did:sov document without services', async () => { - const privateKey = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) @@ -144,7 +144,7 @@ describe('IndySdkSovDidRegistrar', () => { role: 'STEWARD', }, secret: { - privateKey: TypedArrayEncoder.fromString(privateKey), + privateKey, }, }) @@ -206,7 +206,7 @@ describe('IndySdkSovDidRegistrar', () => { }) it('should correctly create a did:sov document with services', async () => { - const privateKey = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) @@ -227,7 +227,7 @@ describe('IndySdkSovDidRegistrar', () => { }, }, secret: { - privateKey: TypedArrayEncoder.fromString(privateKey), + privateKey, }, }) diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index 16561bbcf1..d65b95ada2 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -33,6 +33,8 @@ import { Key, SigningProviderRegistry, TypedArrayEncoder, + isValidSeed, + isValidPrivateKey, } from '@aries-framework/core' import { inject, injectable } from 'tsyringe' @@ -415,7 +417,15 @@ export class IndySdkWallet implements Wallet { public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { if (seed && privateKey) { - throw new AriesFrameworkError('Only one of seed and privateKey can be set') + throw new WalletError('Only one of seed and privateKey can be set') + } + + if (seed && !isValidSeed(seed, keyType)) { + throw new WalletError('Invalid seed provided') + } + + if (privateKey && !isValidPrivateKey(privateKey, keyType)) { + throw new WalletError('Invalid private key provided') } // Ed25519 is supported natively in Indy wallet diff --git a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts b/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts index de121b462d..eef3b5c8dd 100644 --- a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts +++ b/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts @@ -117,7 +117,7 @@ describe('dids', () => { id: `did:sov:${indyDid}`, }, secret: { - privateKey: privateKey.toString(), + privateKey, }, }, }) diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index cf41881cad..50735a67eb 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -44,28 +44,6 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { const seed = options.secret?.seed const privateKey = options.secret?.privateKey - if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid privateKey provided', - }, - } - } - - if (seed && (typeof seed !== 'object' || seed.length !== 32)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid seed provided', - }, - } - } - const { alias, role, submitterDid, submitterVerkey, services, useEndpointAttrib } = options.options let verkey = options.options.verkey let did = options.did @@ -202,8 +180,8 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed?.toString(), - privateKey: options.secret?.privateKey?.toString(), + seed: options.secret?.seed, + privateKey: options.secret?.privateKey, }, }, } From 64e20f1d694e4cbb38aca244e02980a12c7647df Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 20 Feb 2023 17:07:30 +0100 Subject: [PATCH 056/139] fix!: don't emit legacy did:sov prefix for new protocols (#1245) Signed-off-by: Timo Glastra --- .../v1/messages/V1CredentialAckMessage.ts | 2 ++ .../V1CredentialProblemReportMessage.ts | 2 ++ .../v1/messages/V1IssueCredentialMessage.ts | 2 ++ .../v1/messages/V1OfferCredentialMessage.ts | 2 ++ .../v1/messages/V1ProposeCredentialMessage.ts | 2 ++ .../v1/messages/V1RequestCredentialMessage.ts | 2 ++ .../v1/messages/V1PresentationAckMessage.ts | 2 ++ .../v1/messages/V1PresentationMessage.ts | 1 + .../V1PresentationProblemReportMessage.ts | 2 ++ .../messages/V1ProposePresentationMessage.ts | 1 + .../messages/V1RequestPresentationMessage.ts | 2 ++ packages/core/src/agent/AgentConfig.ts | 4 +-- packages/core/src/agent/AgentMessage.ts | 21 ++++++++++++-- packages/core/src/agent/EnvelopeService.ts | 6 ++-- .../src/agent/__tests__/AgentMessage.test.ts | 29 +++++++++++++++---- .../basic-messages/messages/BasicMessage.ts | 2 ++ .../ConnectionInvitationMessage.test.ts | 4 +-- .../messages/ConnectionInvitationMessage.ts | 12 ++++++-- .../ConnectionProblemReportMessage.ts | 2 ++ .../messages/ConnectionRequestMessage.ts | 2 ++ .../messages/ConnectionResponseMessage.ts | 2 ++ .../connections/messages/TrustPingMessage.ts | 2 ++ .../messages/TrustPingResponseMessage.ts | 2 ++ .../routing/messages/ForwardMessage.ts | 2 ++ packages/core/src/types.ts | 2 +- 25 files changed, 96 insertions(+), 16 deletions(-) diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts index db9bba955d..540123584b 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialAckMessage.ts @@ -8,6 +8,8 @@ export type V1CredentialAckMessageOptions = AckMessageOptions * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0015-acks/README.md#explicit-acks */ export class V1CredentialAckMessage extends AckMessage { + public readonly allowDidSovPrefix = true + /** * Create new CredentialAckMessage instance. * @param options diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts index 7b3a3c4c06..5e36d71381 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1CredentialProblemReportMessage.ts @@ -8,6 +8,8 @@ export type V1CredentialProblemReportMessageOptions = ProblemReportMessageOption * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md */ export class V1CredentialProblemReportMessage extends ProblemReportMessage { + public readonly allowDidSovPrefix = true + /** * Create new CredentialProblemReportMessage instance. * @param options diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts index a8727a355a..96f99da1c4 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts @@ -14,6 +14,8 @@ export interface V1IssueCredentialMessageOptions { } export class V1IssueCredentialMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + public constructor(options: V1IssueCredentialMessageOptions) { super() diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts index abb58f9aa1..b8d8c33e0c 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts @@ -22,6 +22,8 @@ export interface V1OfferCredentialMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md#offer-credential */ export class V1OfferCredentialMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + public constructor(options: V1OfferCredentialMessageOptions) { super() diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts index 7c50693cad..e6362c807b 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts @@ -32,6 +32,8 @@ export interface V1ProposeCredentialMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0036-issue-credential/README.md#propose-credential */ export class V1ProposeCredentialMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + public constructor(options: V1ProposeCredentialMessageOptions) { super() diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts index 2814e6ebbf..5b55e8f206 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts @@ -14,6 +14,8 @@ export interface V1RequestCredentialMessageOptions { } export class V1RequestCredentialMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + public constructor(options: V1RequestCredentialMessageOptions) { super() diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts index 743fdba4fa..6c44d9db82 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationAckMessage.ts @@ -3,6 +3,8 @@ import type { AckMessageOptions } from '@aries-framework/core' import { AckMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' export class V1PresentationAckMessage extends AckMessage { + public readonly allowDidSovPrefix = true + public constructor(options: AckMessageOptions) { super(options) } diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts index e27309111c..2b645e227d 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts @@ -20,6 +20,7 @@ export interface V1PresentationMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#presentation */ export class V1PresentationMessage extends AgentMessage { + public readonly allowDidSovPrefix = true public constructor(options: V1PresentationMessageOptions) { super() diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts index baa7d1935e..479aade010 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationProblemReportMessage.ts @@ -8,6 +8,8 @@ export type V1PresentationProblemReportMessageOptions = ProblemReportMessageOpti * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md */ export class V1PresentationProblemReportMessage extends ProblemReportMessage { + public readonly allowDidSovPrefix = true + /** * Create new PresentationProblemReportMessage instance. * @param options description of error and multiple optional fields for reporting problem diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts index 12d94f73fa..d54a3f8d52 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts @@ -16,6 +16,7 @@ export interface V1ProposePresentationMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#propose-presentation */ export class V1ProposePresentationMessage extends AgentMessage { + public readonly allowDidSovPrefix = true public constructor(options: V1ProposePresentationMessageOptions) { super() diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts index e49dfd9aaa..7486a469f4 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts @@ -18,6 +18,8 @@ export const INDY_PROOF_REQUEST_ATTACHMENT_ID = 'libindy-request-presentation-0' * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#request-presentation */ export class V1RequestPresentationMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + public constructor(options: V1RequestPresentationMessageOptions) { super() diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 09f348652d..b234ed4efd 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -161,8 +161,8 @@ export class AgentConfig { return this.initConfig.clearDefaultMediator ?? false } - public get useLegacyDidSovPrefix() { - return this.initConfig.useLegacyDidSovPrefix ?? false + public get useDidSovPrefixWhereAllowed() { + return this.initConfig.useDidSovPrefixWhereAllowed ?? false } /** diff --git a/packages/core/src/agent/AgentMessage.ts b/packages/core/src/agent/AgentMessage.ts index 07cf8ee9db..9f081f3d18 100644 --- a/packages/core/src/agent/AgentMessage.ts +++ b/packages/core/src/agent/AgentMessage.ts @@ -1,6 +1,8 @@ import type { ParsedMessageType } from '../utils/messageType' import type { Constructor } from '../utils/mixins' +import { Exclude } from 'class-transformer' + import { AckDecorated } from '../decorators/ack/AckDecoratorExtension' import { AttachmentDecorated } from '../decorators/attachment/AttachmentExtension' import { L10nDecorated } from '../decorators/l10n/L10nDecoratorExtension' @@ -20,10 +22,25 @@ const Decorated = ThreadDecorated( ) export class AgentMessage extends Decorated { - public toJSON({ useLegacyDidSovPrefix = false }: { useLegacyDidSovPrefix?: boolean } = {}): Record { + /** + * Whether the protocol RFC was initially written using the legacy did:prefix instead of the + * new https://didcomm.org message type prefix. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0348-transition-msg-type-to-https/README.md + */ + @Exclude() + public readonly allowDidSovPrefix: boolean = false + + public toJSON({ useDidSovPrefixWhereAllowed }: { useDidSovPrefixWhereAllowed?: boolean } = {}): Record< + string, + unknown + > { const json = JsonTransformer.toJSON(this) - if (useLegacyDidSovPrefix) { + // If we have `useDidSovPrefixWhereAllowed` enabled, we want to replace the new https://didcomm.org prefix with the legacy did:sov prefix. + // However, we only do this if the protocol RFC was initially written with the did:sov message type prefix + // See https://github.com/hyperledger/aries-rfcs/blob/main/features/0348-transition-msg-type-to-https/README.md + if (this.allowDidSovPrefix && useDidSovPrefixWhereAllowed) { replaceNewDidCommPrefixWithLegacyDidSovOnMessage(json) } diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index df72e3d983..37b341cd6f 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -33,7 +33,7 @@ export class EnvelopeService { const senderKeyBase58 = senderKey && senderKey.publicKeyBase58 // pass whether we want to use legacy did sov prefix - const message = payload.toJSON({ useLegacyDidSovPrefix: agentContext.config.useLegacyDidSovPrefix }) + const message = payload.toJSON({ useDidSovPrefixWhereAllowed: agentContext.config.useDidSovPrefixWhereAllowed }) this.logger.debug(`Pack outbound message ${message['@type']}`) @@ -49,7 +49,9 @@ export class EnvelopeService { recipientKeysBase58 = [routingKeyBase58] this.logger.debug('Forward message created', forwardMessage) - const forwardJson = forwardMessage.toJSON({ useLegacyDidSovPrefix: agentContext.config.useLegacyDidSovPrefix }) + const forwardJson = forwardMessage.toJSON({ + useDidSovPrefixWhereAllowed: agentContext.config.useDidSovPrefixWhereAllowed, + }) // Forward messages are anon packed encryptedMessage = await agentContext.wallet.pack(forwardJson, [routingKeyBase58], undefined) diff --git a/packages/core/src/agent/__tests__/AgentMessage.test.ts b/packages/core/src/agent/__tests__/AgentMessage.test.ts index ec9f8e3d7a..da71db84f7 100644 --- a/packages/core/src/agent/__tests__/AgentMessage.test.ts +++ b/packages/core/src/agent/__tests__/AgentMessage.test.ts @@ -10,16 +10,35 @@ class CustomProtocolMessage extends AgentMessage { public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/message') } +class LegacyDidSovPrefixMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + + @IsValidMessageType(LegacyDidSovPrefixMessage.type) + public readonly type = LegacyDidSovPrefixMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/another-message') +} + describe('AgentMessage', () => { describe('toJSON', () => { - it('should only use did:sov message prefix if useLegacyDidSovPrefix is true', () => { + it('should only use did:sov message prefix if useDidSovPrefixWhereAllowed and allowDidSovPrefix are both true', () => { const message = new TestMessage() + const legacyPrefixMessage = new LegacyDidSovPrefixMessage() + + // useDidSovPrefixWhereAllowed & allowDidSovPrefix are both false + let testMessageJson = message.toJSON() + expect(testMessageJson['@type']).toBe('https://didcomm.org/connections/1.0/invitation') + + // useDidSovPrefixWhereAllowed is true, but allowDidSovPrefix is false + testMessageJson = message.toJSON({ useDidSovPrefixWhereAllowed: true }) + expect(testMessageJson['@type']).toBe('https://didcomm.org/connections/1.0/invitation') - const jsonDidComm = message.toJSON() - expect(jsonDidComm['@type']).toBe('https://didcomm.org/connections/1.0/invitation') + // useDidSovPrefixWhereAllowed is false, but allowDidSovPrefix is true + testMessageJson = legacyPrefixMessage.toJSON() + expect(testMessageJson['@type']).toBe('https://didcomm.org/fake-protocol/1.5/another-message') - const jsonSov = message.toJSON({ useLegacyDidSovPrefix: true }) - expect(jsonSov['@type']).toBe('did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation') + // useDidSovPrefixWhereAllowed & allowDidSovPrefix are both true + testMessageJson = legacyPrefixMessage.toJSON({ useDidSovPrefixWhereAllowed: true }) + expect(testMessageJson['@type']).toBe('did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/fake-protocol/1.5/another-message') }) }) diff --git a/packages/core/src/modules/basic-messages/messages/BasicMessage.ts b/packages/core/src/modules/basic-messages/messages/BasicMessage.ts index cdae9d3fa6..7dd48b5dde 100644 --- a/packages/core/src/modules/basic-messages/messages/BasicMessage.ts +++ b/packages/core/src/modules/basic-messages/messages/BasicMessage.ts @@ -6,6 +6,8 @@ import { IsValidMessageType, parseMessageType } from '../../../utils/messageType import { DateParser } from '../../../utils/transformers' export class BasicMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new BasicMessage instance. * sentTime will be assigned to new Date if not passed, id will be assigned to uuid/v4 if not passed diff --git a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts index 839419b48f..188083309a 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts @@ -65,7 +65,7 @@ describe('ConnectionInvitationMessage', () => { expect(invitationUrl).toBe(`${domain}?c_i=${JsonEncoder.toBase64URL(json)}`) }) - it('should use did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation as type if useLegacyDidSovPrefix is set to true', async () => { + it('should use did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation as type if useDidSovPrefixWhereAllowed is set to true', async () => { const invitation = new ConnectionInvitationMessage({ id: '04a2c382-999e-4de9-a1d2-9dec0b2fa5e4', recipientKeys: ['recipientKeyOne', 'recipientKeyTwo'], @@ -86,7 +86,7 @@ describe('ConnectionInvitationMessage', () => { const invitationUrl = invitation.toUrl({ domain: 'https://example.com', - useLegacyDidSovPrefix: true, + useDidSovPrefixWhereAllowed: true, }) const parsedUrl = parseUrl(invitationUrl).query diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index ffbdf744d5..30cf5f1499 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -33,6 +33,8 @@ export interface DIDInvitationOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md#0-invitation-to-connect */ export class ConnectionInvitationMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new ConnectionInvitationMessage instance. * @param options @@ -107,8 +109,14 @@ export class ConnectionInvitationMessage extends AgentMessage { * @param domain domain name to use for invitation url * @returns invitation url with base64 encoded invitation */ - public toUrl({ domain, useLegacyDidSovPrefix = false }: { domain: string; useLegacyDidSovPrefix?: boolean }) { - const invitationJson = this.toJSON({ useLegacyDidSovPrefix }) + public toUrl({ + domain, + useDidSovPrefixWhereAllowed = false, + }: { + domain: string + useDidSovPrefixWhereAllowed?: boolean + }) { + const invitationJson = this.toJSON({ useDidSovPrefixWhereAllowed }) const encodedInvitation = JsonEncoder.toBase64URL(invitationJson) const invitationUrl = `${domain}?c_i=${encodedInvitation}` diff --git a/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts b/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts index 3aaee94062..d6d84dcb0f 100644 --- a/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionProblemReportMessage.ts @@ -9,6 +9,8 @@ export type ConnectionProblemReportMessageOptions = ProblemReportMessageOptions * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0035-report-problem/README.md */ export class ConnectionProblemReportMessage extends ProblemReportMessage { + public readonly allowDidSovPrefix = true + /** * Create new ConnectionProblemReportMessage instance. * @param options diff --git a/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts b/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts index e86912966d..46a33fa221 100644 --- a/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionRequestMessage.ts @@ -21,6 +21,8 @@ export interface ConnectionRequestMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md#1-connection-request */ export class ConnectionRequestMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new ConnectionRequestMessage instance. * @param options diff --git a/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts b/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts index 91a3a48498..08e22a166b 100644 --- a/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionResponseMessage.ts @@ -17,6 +17,8 @@ export interface ConnectionResponseMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md#2-connection-response */ export class ConnectionResponseMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new ConnectionResponseMessage instance. * @param options diff --git a/packages/core/src/modules/connections/messages/TrustPingMessage.ts b/packages/core/src/modules/connections/messages/TrustPingMessage.ts index 036635eed6..a8719f0b34 100644 --- a/packages/core/src/modules/connections/messages/TrustPingMessage.ts +++ b/packages/core/src/modules/connections/messages/TrustPingMessage.ts @@ -19,6 +19,8 @@ export interface TrustPingMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0048-trust-ping/README.md#messages */ export class TrustPingMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new TrustPingMessage instance. * responseRequested will be true if not passed diff --git a/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts b/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts index 23ace28316..9fa465a836 100644 --- a/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts +++ b/packages/core/src/modules/connections/messages/TrustPingResponseMessage.ts @@ -18,6 +18,8 @@ export interface TrustPingResponseMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0048-trust-ping/README.md#messages */ export class TrustPingResponseMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new TrustPingResponseMessage instance. * responseRequested will be true if not passed diff --git a/packages/core/src/modules/routing/messages/ForwardMessage.ts b/packages/core/src/modules/routing/messages/ForwardMessage.ts index 9b4d6d658c..ea1e45a50c 100644 --- a/packages/core/src/modules/routing/messages/ForwardMessage.ts +++ b/packages/core/src/modules/routing/messages/ForwardMessage.ts @@ -15,6 +15,8 @@ export interface ForwardMessageOptions { * @see https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0094-cross-domain-messaging/README.md#corerouting10forward */ export class ForwardMessage extends AgentMessage { + public readonly allowDidSovPrefix = true + /** * Create new ForwardMessage instance. * diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 4cd29a4ba2..c80de6c997 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -57,7 +57,7 @@ export interface InitConfig { logger?: Logger didCommMimeType?: DidCommMimeType useDidKeyInProtocols?: boolean - useLegacyDidSovPrefix?: boolean + useDidSovPrefixWhereAllowed?: boolean connectionImageUrl?: string autoUpdateStorageOnStartup?: boolean From fb7ee5048c33d5335cd9f07cad3dffc60dee7376 Mon Sep 17 00:00:00 2001 From: Victor Anene <62852943+Vickysomtee@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:39:14 +0100 Subject: [PATCH 057/139] feat: IndyVdrAnonCredsRegistry revocation methods (#1328) Signed-off-by: Victor Anene --- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 202 ++++++++++++++++-- .../indy-vdr/src/anoncreds/utils/transform.ts | 39 ++++ .../indy-vdr-anoncreds-registry.e2e.test.ts | 187 ++++++++++++++-- 3 files changed, 390 insertions(+), 38 deletions(-) create mode 100644 packages/indy-vdr/src/anoncreds/utils/transform.ts diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 209f0aac81..0f492edcb2 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -18,6 +18,8 @@ import { GetCredentialDefinitionRequest, CredentialDefinitionRequest, GetTransactionRequest, + GetRevocationRegistryDeltaRequest, + GetRevocationRegistryDefinitionRequest, } from '@hyperledger/indy-vdr-shared' import { IndyVdrPoolService } from '../pool' @@ -25,10 +27,12 @@ import { IndyVdrPoolService } from '../pool' import { didFromSchemaId, didFromCredentialDefinitionId, + didFromRevocationRegistryDefinitionId, getLegacySchemaId, getLegacyCredentialDefinitionId, indyVdrAnonCredsRegistryIdentifierRegex, } from './utils/identifiers' +import { anonCredsRevocationStatusListFromIndyVdr } from './utils/transform' export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { public readonly supportedIdentifier = indyVdrAnonCredsRegistryIdentifierRegex @@ -390,31 +394,191 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } } + public async getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise { + try { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) + + const pool = await indySdkPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug( + `Using ledger '${pool.indyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` + ) + + const request = new GetRevocationRegistryDefinitionRequest({ + submitterDid: did, + revocationRegistryId: revocationRegistryDefinitionId, + }) + + agentContext.config.logger.trace( + `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` + ) + + const response = await pool.submitReadRequest(request) + + if (!response.result.data) { + agentContext.config.logger.error( + `Error retrieving revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, + { + revocationRegistryDefinitionId, + } + ) + + return { + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve revocation registry definition`, + }, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } + } + + const revocationRegistryDefinition = { + issuerId: did, + revocDefType: response.result.data?.revocDefType, + value: { + maxCredNum: response.result.data?.value.maxCredNum, + tailsHash: response.result.data?.value.tailsHash, + tailsLocation: response.result.data?.value.tailsLocation, + publicKeys: { + accumKey: { + z: response.result.data?.value.publicKeys.accumKey.z, + }, + }, + }, + tag: response.result.data?.tag, + credDefId: response.result.data?.credDefId, + } + + return { + revocationRegistryDefinitionId, + revocationRegistryDefinition, + revocationRegistryDefinitionMetadata: { + issuanceType: response.result.data?.value.issuanceType, + didIndyNamespace: pool.indyNamespace, + }, + resolutionMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error( + `Error retrieving revocation registry definition '${revocationRegistryDefinitionId}' from ledger`, + { + error, + revocationRegistryDefinitionId, + } + ) + + return { + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve revocation registry definition: ${error.message}`, + }, + revocationRegistryDefinitionId, + revocationRegistryDefinitionMetadata: {}, + } + } + } + public async getRevocationStatusList( agentContext: AgentContext, revocationRegistryId: string, timestamp: number ): Promise { - return { - resolutionMetadata: { - error: 'Not Implemented', - message: `Revocation list not yet implemented `, - }, - revocationStatusListMetadata: {}, - } - } + try { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + const did = didFromRevocationRegistryDefinitionId(revocationRegistryId) - public async getRevocationRegistryDefinition( - agentContext: AgentContext, - revocationRegistryDefinitionId: string - ): Promise { - return { - resolutionMetadata: { - error: 'Not Implemented', - message: `Revocation registry definition not yet implemented`, - }, - revocationRegistryDefinitionId, - revocationRegistryDefinitionMetadata: {}, + const pool = await indySdkPoolService.getPoolForDid(agentContext, did) + + agentContext.config.logger.debug( + `Using ledger '${pool.indyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` + ) + + const request = new GetRevocationRegistryDeltaRequest({ + submitterDid: did, + revocationRegistryId, + toTs: timestamp, + }) + + agentContext.config.logger.trace( + `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` + ) + + const response = await pool.submitReadRequest(request) + + agentContext.config.logger.debug( + `Got revocation registry deltas '${revocationRegistryId}' until timestamp ${timestamp} from ledger` + ) + + const { revocationRegistryDefinition, resolutionMetadata, revocationRegistryDefinitionMetadata } = + await this.getRevocationRegistryDefinition(agentContext, revocationRegistryId) + + if ( + !revocationRegistryDefinition || + !revocationRegistryDefinitionMetadata.issuanceType || + typeof revocationRegistryDefinitionMetadata.issuanceType !== 'string' + ) { + return { + resolutionMetadata: { + error: `error resolving revocation registry definition with id ${revocationRegistryId}: ${resolutionMetadata.error} ${resolutionMetadata.message}`, + }, + revocationStatusListMetadata: { + didIndyNamespace: pool.indyNamespace, + }, + } + } + + const isIssuanceByDefault = revocationRegistryDefinitionMetadata.issuanceType === 'ISSUANCE_BY_DEFAULT' + + if (!response.result.data) { + return { + resolutionMetadata: { + error: 'notFound', + message: `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation`, + }, + revocationStatusListMetadata: {}, + } + } + + const revocationRegistryDelta = { + accum: response.result.data?.value.accum_to.value.accum, + issued: response.result.data?.value.issued, + revoked: response.result.data?.value.revoked, + } + + return { + resolutionMetadata: {}, + revocationStatusList: anonCredsRevocationStatusListFromIndyVdr( + revocationRegistryId, + revocationRegistryDefinition, + revocationRegistryDelta, + response.result.data.value.accum_to.txnTime, + isIssuanceByDefault + ), + revocationStatusListMetadata: { + didIndyNamespace: pool.indyNamespace, + }, + } + } catch (error) { + agentContext.config.logger.error( + `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation?"`, + { + error, + revocationRegistryId: revocationRegistryId, + } + ) + + return { + resolutionMetadata: { + error: 'notFound', + message: `Error retrieving revocation registry delta '${revocationRegistryId}' from ledger, potentially revocation interval ends before revocation registry creation: ${error.message}`, + }, + revocationStatusListMetadata: {}, + } } } diff --git a/packages/indy-vdr/src/anoncreds/utils/transform.ts b/packages/indy-vdr/src/anoncreds/utils/transform.ts new file mode 100644 index 0000000000..b0b0bd5fd6 --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/utils/transform.ts @@ -0,0 +1,39 @@ +import type { AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition } from '@aries-framework/anoncreds' + +export function anonCredsRevocationStatusListFromIndyVdr( + revocationRegistryDefinitionId: string, + revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition, + delta: RevocRegDelta, + timestamp: number, + isIssuanceByDefault: boolean +): AnonCredsRevocationStatusList { + // 0 means unrevoked, 1 means revoked + const defaultState = isIssuanceByDefault ? 0 : 1 + + // Fill with default value + const revocationList = new Array(revocationRegistryDefinition.value.maxCredNum).fill(defaultState) + + // Set all `issuer` indexes to 0 (not revoked) + for (const issued of delta.issued ?? []) { + revocationList[issued] = 0 + } + + // Set all `revoked` indexes to 1 (revoked) + for (const revoked of delta.revoked ?? []) { + revocationList[revoked] = 1 + } + + return { + issuerId: revocationRegistryDefinition.issuerId, + currentAccumulator: delta.accum, + revRegId: revocationRegistryDefinitionId, + revocationList, + timestamp, + } +} + +interface RevocRegDelta { + accum: string + issued: number[] + revoked: number[] +} diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index 74676ef88b..dee2fbc4d0 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -1,4 +1,5 @@ -import { Agent, DidsModule } from '@aries-framework/core' +import { Agent, DidsModule, Key, KeyType } from '@aries-framework/core' +import { RevocationRegistryDefinitionRequest, RevocationRegistryEntryRequest } from '@hyperledger/indy-vdr-shared' import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' @@ -13,7 +14,10 @@ const agentConfig = getAgentConfig('IndyVdrAnonCredsRegistry') // TODO: update to module once available const indyVdrPoolService = new IndyVdrPoolService(agentConfig.logger, indyVdrModuleConfig) +const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') +// Verkey for the publicDidSeed +const signingKey = Key.fromPublicKeyBase58('FMGcFuU3QwAQLywxvmEnSorQT3NwU9wgDMMTaDFtvswm', KeyType.Ed25519) const indyVdrAnonCredsRegistry = new IndyVdrAnonCredsRegistry() const agent = new Agent({ @@ -106,16 +110,32 @@ describe('IndyVdrAnonCredsRegistry', () => { type: 'CL', value: { primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', + s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', + age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', }, rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', + z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', + }, + revocation: { + g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + g_dash: + '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + htilde: + '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h_cap: + '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', }, }, }, @@ -136,16 +156,32 @@ describe('IndyVdrAnonCredsRegistry', () => { type: 'CL', value: { primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', + s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', + age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', }, rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', + z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', + }, + revocation: { + g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + g_dash: + '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + htilde: + '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h_cap: + '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', }, }, }, @@ -169,16 +205,32 @@ describe('IndyVdrAnonCredsRegistry', () => { type: 'CL', value: { primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', + s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', + age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', }, rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', + z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', + }, + revocation: { + g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + g_dash: + '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + htilde: + '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h_cap: + '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', }, }, }, @@ -187,5 +239,102 @@ describe('IndyVdrAnonCredsRegistry', () => { }, resolutionMetadata: {}, }) + + // We don't support creating a revocation registry using AFJ yet, so we directly use indy-vdr to create the revocation registry + const revocationRegistryDefinitionId = `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` + const revocationRegistryRequest = new RevocationRegistryDefinitionRequest({ + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + revocationRegistryDefinitionV1: { + credDefId: credentialDefinitionResponse.credentialDefinitionId, + id: revocationRegistryDefinitionId, + revocDefType: 'CL_ACCUM', + tag: 'tag', + value: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + maxCredNum: 100, + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + }, + ver: '1.0', + }, + }) + + // After this call, the revocation registry should now be resolvable + await pool.submitWriteRequest(agent.context, revocationRegistryRequest, signingKey) + + // Also create a revocation registry entry + const revocationEntryRequest = new RevocationRegistryEntryRequest({ + revocationRegistryDefinitionId, + revocationRegistryDefinitionType: 'CL_ACCUM', + revocationRegistryEntry: { + ver: '1.0', + value: { + accum: '1', + }, + }, + submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + }) + + // After this call we can query the revocation registry entries (using timestamp now) + const response = await pool.submitWriteRequest(agent.context, revocationEntryRequest, signingKey) + + const revocationRegistryDefintion = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( + agent.context, + revocationRegistryDefinitionId + ) + + expect(revocationRegistryDefintion).toMatchObject({ + revocationRegistryDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag`, + revocationRegistryDefinition: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + revocDefType: 'CL_ACCUM', + value: { + maxCredNum: 100, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + }, + tag: 'tag', + credDefId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + }, + revocationRegistryDefinitionMetadata: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + const revocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( + agent.context, + revocationRegistryDefinitionId, + response.result.txnMetadata.txnTime + ) + + expect(revocationStatusList).toMatchObject({ + resolutionMetadata: {}, + revocationStatusList: { + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + currentAccumulator: '1', + revRegId: `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag`, + revocationList: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + timestamp: response.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { didIndyNamespace: 'pool:localtest' }, + }) }) }) From 1c6aeae31ac57e83f4059f3dba35ccb1ca36926e Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 21 Feb 2023 19:18:19 -0300 Subject: [PATCH 058/139] fix(askar): anoncrypt messages unpacking (#1332) Signed-off-by: Ariel Gentile --- .../anoncreds/tests/legacyAnonCredsSetup.ts | 68 +++++- packages/askar/src/wallet/AskarWallet.ts | 8 +- .../e2e-askar-indy-sdk-wallet-subject.test.ts | 199 +++++++----------- 3 files changed, 149 insertions(+), 126 deletions(-) diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 9b6da7fd32..32e248f586 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -28,10 +28,16 @@ import { } from '@aries-framework/core' import { randomUUID } from 'crypto' +import { AnonCredsRsModule } from '../../anoncreds-rs/src' +import { AskarModule } from '../../askar/src' +import { uuid } from '../../core/src/utils/uuid' import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests' import { getAgentOptions, makeConnection, + genesisTransactions, + taaVersion, + taaAcceptanceMechanism, waitForCredentialRecordSubject, waitForProofExchangeRecordSubject, } from '../../core/tests/helpers' @@ -43,6 +49,7 @@ import { IndySdkSovDidResolver, } from '../../indy-sdk/src' import { getIndySdkModuleConfig } from '../../indy-sdk/tests/setupIndySdkModule' +import { IndyVdrAnonCredsRegistry, IndyVdrSovDidResolver, IndyVdrModule } from '../../indy-vdr/src' import { V1CredentialProtocol, V1ProofProtocol, @@ -52,7 +59,9 @@ import { } from '../src' // Helper type to get the type of the agents (with the custom modules) for the credential tests -export type AnonCredsTestsAgent = Agent> +export type AnonCredsTestsAgent = + | Agent> + | Agent> export const getLegacyAnonCredsModules = ({ autoAcceptCredentials, @@ -97,6 +106,63 @@ export const getLegacyAnonCredsModules = ({ return modules } +export const getAskarAnonCredsIndyModules = ({ + autoAcceptCredentials, + autoAcceptProofs, +}: { autoAcceptCredentials?: AutoAcceptCredential; autoAcceptProofs?: AutoAcceptProof } = {}) => { + const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() + const legacyIndyProofFormatService = new LegacyIndyProofFormatService() + + const indyNetworkConfig = { + id: `localhost-${uuid()}`, + isProduction: false, + genesisTransactions, + indyNamespace: 'pool:localtest', + transactionAuthorAgreement: { version: taaVersion, acceptanceMechanism: taaAcceptanceMechanism }, + } + + const modules = { + credentials: new CredentialsModule({ + autoAcceptCredentials, + credentialProtocols: [ + new V1CredentialProtocol({ + indyCredentialFormat: legacyIndyCredentialFormatService, + }), + new V2CredentialProtocol({ + credentialFormats: [legacyIndyCredentialFormatService], + }), + ], + }), + proofs: new ProofsModule({ + autoAcceptProofs, + proofProtocols: [ + new V1ProofProtocol({ + indyProofFormat: legacyIndyProofFormatService, + }), + new V2ProofProtocol({ + proofFormats: [legacyIndyProofFormatService], + }), + ], + }), + anoncreds: new AnonCredsModule({ + registries: [new IndyVdrAnonCredsRegistry()], + }), + anoncredsRs: new AnonCredsRsModule(), + indyVdr: new IndyVdrModule({ + networks: [indyNetworkConfig], + }), + dids: new DidsModule({ + resolvers: [new IndyVdrSovDidResolver()], // TODO: Support Registrar for tests + }), + askar: new AskarModule(), + cache: new CacheModule({ + cache: new InMemoryLruCache({ limit: 100 }), + }), + } as const + + return modules +} + export async function presentLegacyAnonCredsProof({ verifierAgent, verifierReplay, diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index b11c5f9371..46b004006e 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -583,9 +583,7 @@ export class AskarWallet implements Wallet { const protectedJson = JsonEncoder.fromBase64(messagePackage.protected) const alg = protectedJson.alg - const isAuthcrypt = alg === 'Authcrypt' - - if (!isAuthcrypt && alg != 'Anoncrypt') { + if (!['Anoncrypt', 'Authcrypt'].includes(alg)) { throw new WalletError(`Unsupported pack algorithm: ${alg}`) } @@ -645,6 +643,8 @@ export class AskarWallet implements Wallet { message: recipient.encrypted_key, nonce: recipient.iv, }) + } else { + payloadKey = CryptoBox.sealOpen({ ciphertext: recipient.encrypted_key, recipientKey: recip_x }) } break } @@ -653,7 +653,7 @@ export class AskarWallet implements Wallet { throw new WalletError('No corresponding recipient key found') } - if (!senderKey && isAuthcrypt) { + if (!senderKey && alg === 'Authcrypt') { throw new WalletError('Sender public key not provided for Authcrypt') } diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts index 199d57a314..d59263dfe2 100644 --- a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -1,141 +1,98 @@ import type { SubjectMessage } from './transport/SubjectInboundTransport' +import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnonCredsSetup' -import indySdk from 'indy-sdk' import { Subject } from 'rxjs' -import { getAgentOptions, makeConnection, waitForBasicMessage } from '../packages/core/tests/helpers' +import { + getAskarAnonCredsIndyModules, + getLegacyAnonCredsModules, +} from '../packages/anoncreds/tests/legacyAnonCredsSetup' +import { getAgentOptions } from '../packages/core/tests/helpers' -import { AskarModule } from '@aries-framework/askar' -import { Agent, DependencyManager, InjectionSymbols } from '@aries-framework/core' -import { IndySdkModule, IndySdkStorageService, IndySdkWallet } from '@aries-framework/indy-sdk' +import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { e2eTest } from './e2e-test' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' -// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved -describe.skip('E2E Askar-Indy SDK Wallet Subject tests', () => { - let recipientAgent: Agent - let senderAgent: Agent - - afterEach(async () => { - if (recipientAgent) { - await recipientAgent.shutdown() - await recipientAgent.wallet.delete() - } - - if (senderAgent) { - await senderAgent.shutdown() - await senderAgent.wallet.delete() - } +const recipientAgentOptions = getAgentOptions( + 'E2E Askar Subject Recipient', + { + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getAskarAnonCredsIndyModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) - - test('Wallet Subject flow - Indy Sender / Askar Receiver ', async () => { - // Sender is an Agent using Indy SDK Wallet - const senderDependencyManager = new DependencyManager() - senderDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - senderDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) - senderAgent = new Agent( - getAgentOptions( - 'E2E Wallet Subject Sender Indy', - { endpoints: ['rxjs:sender'] }, - { indySdk: new IndySdkModule({ indySdk }) } - ), - senderDependencyManager - ) - - // Recipient is an Agent using Askar Wallet - recipientAgent = new Agent( - getAgentOptions( - 'E2E Wallet Subject Recipient Askar', - { endpoints: ['rxjs:recipient'] }, - { askar: new AskarModule() } - ) - ) - - await e2eWalletTest(senderAgent, recipientAgent) +) +const mediatorAgentOptions = getAgentOptions( + 'E2E Askar Subject Mediator', + { + endpoints: ['rxjs:mediator'], + autoAcceptMediationRequests: true, + }, + getAskarAnonCredsIndyModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) - - test('Wallet Subject flow - Askar Sender / Askar Recipient ', async () => { - // Sender is an Agent using Askar Wallet - senderAgent = new Agent( - getAgentOptions('E2E Wallet Subject Sender Askar', { endpoints: ['rxjs:sender'] }, { askar: new AskarModule() }) - ) - - // Recipient is an Agent using Askar Wallet - recipientAgent = new Agent( - getAgentOptions( - 'E2E Wallet Subject Recipient Askar', - { endpoints: ['rxjs:recipient'] }, - { askar: new AskarModule() } - ) - ) - - await e2eWalletTest(senderAgent, recipientAgent) +) +const senderAgentOptions = getAgentOptions( + 'E2E Indy SDK Subject Sender', + { + endpoints: ['rxjs:sender'], + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }, + getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, }) +) - test('Wallet Subject flow - Indy Sender / Indy Recipient ', async () => { - // Sender is an Agent using Indy SDK Wallet - const senderDependencyManager = new DependencyManager() - senderDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - senderDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) - senderAgent = new Agent( - getAgentOptions( - 'E2E Wallet Subject Sender Indy', - { endpoints: ['rxjs:sender'] }, - { indySdk: new IndySdkModule({ indySdk }) } - ), - senderDependencyManager - ) +describe.skip('E2E Askar-AnonCredsRS-IndyVDR Subject tests', () => { + let recipientAgent: AnonCredsTestsAgent + let mediatorAgent: AnonCredsTestsAgent + let senderAgent: AnonCredsTestsAgent - // Recipient is an Agent using Indy Wallet - const recipientDependencyManager = new DependencyManager() - recipientDependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) - recipientDependencyManager.registerSingleton(InjectionSymbols.StorageService, IndySdkStorageService) - recipientAgent = new Agent( - getAgentOptions( - 'E2E Wallet Subject Recipient Indy', - { endpoints: ['rxjs:recipient'] }, - { indySdk: new IndySdkModule({ indySdk }) } - ), - recipientDependencyManager - ) - - await e2eWalletTest(senderAgent, recipientAgent) + beforeEach(async () => { + recipientAgent = new Agent(recipientAgentOptions) + mediatorAgent = new Agent(mediatorAgentOptions) + senderAgent = new Agent(senderAgentOptions) }) -}) - -export async function e2eWalletTest(senderAgent: Agent, recipientAgent: Agent) { - const recipientMessages = new Subject() - const senderMessages = new Subject() - - const subjectMap = { - 'rxjs:recipient': recipientMessages, - 'rxjs:sender': senderMessages, - } - - // Recipient Setup - recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - recipientAgent.registerInboundTransport(new SubjectInboundTransport(recipientMessages)) - await recipientAgent.initialize() - // Sender Setup - senderAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) - senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) - await senderAgent.initialize() + afterEach(async () => { + await recipientAgent.shutdown() + await recipientAgent.wallet.delete() + await mediatorAgent.shutdown() + await mediatorAgent.wallet.delete() + await senderAgent.shutdown() + await senderAgent.wallet.delete() + }) - // Make connection between sender and recipient - const [recipientSenderConnection, senderRecipientConnection] = await makeConnection(recipientAgent, senderAgent) - expect(recipientSenderConnection).toBeConnectedWith(senderRecipientConnection) + test('Full Subject flow (connect, request mediation, issue, verify)', async () => { + const mediatorMessages = new Subject() + const senderMessages = new Subject() - // Sender sends a basic message and Recipient waits for it - await senderAgent.basicMessages.sendMessage(senderRecipientConnection.id, 'Hello') - await waitForBasicMessage(recipientAgent, { - content: 'Hello', - }) + const subjectMap = { + 'rxjs:mediator': mediatorMessages, + 'rxjs:sender': senderMessages, + } - // Recipient sends a basic message and Sender waits for it - await recipientAgent.basicMessages.sendMessage(recipientSenderConnection.id, 'How are you?') - await waitForBasicMessage(senderAgent, { - content: 'How are you?', + // Recipient Setup + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await recipientAgent.initialize() + + // Mediator Setup + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + await mediatorAgent.initialize() + + // Sender Setup + senderAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + senderAgent.registerInboundTransport(new SubjectInboundTransport(senderMessages)) + await senderAgent.initialize() + + await e2eTest({ + mediatorAgent, + senderAgent, + recipientAgent, + }) }) -} +}) From 518e5e4dfb59f9c0457bfd233409e9f4b3c429ee Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 22 Feb 2023 04:36:33 -0300 Subject: [PATCH 059/139] fix: expose indy pool configs and action menu messages (#1333) Signed-off-by: Ariel Gentile --- packages/action-menu/src/index.ts | 3 ++- packages/indy-sdk/src/index.ts | 3 +++ packages/indy-vdr/src/index.ts | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/action-menu/src/index.ts b/packages/action-menu/src/index.ts index 204d9dc359..97c9a70ea7 100644 --- a/packages/action-menu/src/index.ts +++ b/packages/action-menu/src/index.ts @@ -5,4 +5,5 @@ export * from './ActionMenuEvents' export * from './ActionMenuRole' export * from './ActionMenuState' export * from './models' -export * from './repository/ActionMenuRecord' +export * from './repository' +export * from './messages' diff --git a/packages/indy-sdk/src/index.ts b/packages/indy-sdk/src/index.ts index ea099b7cf4..64ed474b75 100644 --- a/packages/indy-sdk/src/index.ts +++ b/packages/indy-sdk/src/index.ts @@ -7,6 +7,9 @@ export { IndySdkSovDidResolver, } from './dids' +// Ledger +export { IndySdkPoolConfig } from './ledger' + // Wallet export { IndySdkWallet } from './wallet' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index fb687ae77f..44f4dd3e24 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -1,4 +1,5 @@ export { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from './dids' +export { IndyVdrPoolConfig } from './pool' export * from './IndyVdrModule' export * from './IndyVdrModuleConfig' export * from './anoncreds' From d5e34ffdbc2d960a8efe76249201dabc1450b7b4 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 24 Feb 2023 06:19:32 -0300 Subject: [PATCH 060/139] test(indy-sdk): wait before resolving ledger objects (#1340) Signed-off-by: Ariel Gentile --- .../indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index a0548c0223..186d1d9a15 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -62,6 +62,9 @@ describe('IndySdkAnonCredsRegistry', () => { }, }) + // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + const schemaResponse = await indySdkAnonCredsRegistry.getSchema( agent.context, `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}` @@ -139,6 +142,9 @@ describe('IndySdkAnonCredsRegistry', () => { registrationMetadata: {}, }) + // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + const credentialDefinitionResponse = await indySdkAnonCredsRegistry.getCredentialDefinition( agent.context, credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId as string From cac2ec3082a49ecefc60644f34d10f82f3a69ec9 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Fri, 24 Feb 2023 11:11:28 +0100 Subject: [PATCH 061/139] refactor(core)!: remove deprecated injectionContainer prop (#1344) Signed-off-by: martin auer --- packages/core/src/agent/BaseAgent.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index e5e59bd7f4..6707bef9fc 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -1,5 +1,5 @@ import type { AgentConfig } from './AgentConfig' -import type { AgentApi, EmptyModuleMap, ModulesMap, WithoutDefaultModules, CustomOrDefaultApi } from './AgentModules' +import type { AgentApi, CustomOrDefaultApi, EmptyModuleMap, ModulesMap, WithoutDefaultModules } from './AgentModules' import type { TransportSession } from './TransportService' import type { Logger } from '../logger' import type { CredentialsModule } from '../modules/credentials' @@ -191,13 +191,6 @@ export abstract class BaseAgent Date: Fri, 24 Feb 2023 13:01:47 +0200 Subject: [PATCH 062/139] fix: create new socket if socket state is 'closing' (#1337) Signed-off-by: Niall Shaw --- packages/core/src/transport/WsOutboundTransport.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index 1c248036da..68c882fa2b 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -75,7 +75,7 @@ export class WsOutboundTransport implements OutboundTransport { // If we already have a socket connection use it let socket = this.transportTable.get(socketId) - if (!socket) { + if (!socket || socket.readyState === this.WebSocketClass.CLOSING) { if (!endpoint) { throw new AriesFrameworkError(`Missing endpoint. I don't know how and where to send the message.`) } From d38ecb14cb58f1eb78e01c91699bb990d805dc08 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 24 Feb 2023 09:12:40 -0300 Subject: [PATCH 063/139] fix(anoncreds): include prover_did for legacy indy (#1342) Signed-off-by: Ariel Gentile --- DEVREADME.md | 2 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 4 ++++ .../src/formats/LegacyIndyCredentialFormatService.ts | 7 +++++++ .../formats/__tests__/legacy-indy-format-services.test.ts | 5 +++++ packages/anoncreds/src/index.ts | 1 + .../src/anoncreds => anoncreds/src}/utils/proverDid.ts | 0 .../src/anoncreds/services/IndySdkHolderService.ts | 3 +-- .../src/anoncreds/services/IndySdkIssuerService.ts | 2 +- 8 files changed, 20 insertions(+), 4 deletions(-) rename packages/{indy-sdk/src/anoncreds => anoncreds/src}/utils/proverDid.ts (100%) diff --git a/DEVREADME.md b/DEVREADME.md index 73550b193e..98849e0997 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -85,7 +85,7 @@ GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=00 Locally, you might want to run the tests without postgres tests. You can do that by ignoring the tests: ```sh -yarn test --testPathIgnorePatterns ./packages/core/tests/postgres.e2e.test.ts -u +yarn test --testPathIgnorePatterns ./packages/indy-sdk/tests/postgres.e2e.test.ts -u ``` In case you run into trouble running the tests, e.g. complaining about snapshots not being up-to-date, you can try and remove the data stored for the indy-client. On a Unix system with default setup you achieve this by running: diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index b201b8c31e..d3b53ed6fe 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -1,3 +1,4 @@ +import type { AnonCredsCredentialRequest } from '@aries-framework/anoncreds' import type { Wallet } from '@aries-framework/core' import { @@ -225,6 +226,9 @@ describe('Legacy indy format services using anoncreds-rs', () => { }, }) + // Make sure the request contains a prover_did field + expect((requestAttachment.getDataAsJson() as AnonCredsCredentialRequest).prover_did).toBeDefined() + // Issuer processes and accepts request await legacyIndyCredentialFormatService.processRequest(agentContext, { credentialRecord: issuerCredentialRecord, diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index af8ee049c8..33aba1e7bc 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -53,6 +53,7 @@ import { createAndLinkAttachmentsToPreview, } from '../utils/credential' import { AnonCredsCredentialMetadataKey, AnonCredsCredentialRequestMetadataKey } from '../utils/metadata' +import { generateLegacyProverDidLikeString } from '../utils/proverDid' const INDY_CRED_ABSTRACT = 'hlindy/cred-abstract@v2.0' const INDY_CRED_REQUEST = 'hlindy/cred-req@v2.0' @@ -244,6 +245,12 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic linkSecretId: credentialFormats?.indy?.linkSecretId, }) + if (!credentialRequest.prover_did) { + // We just generate a prover did like string, as it's not used for anything and we don't need + // to prove ownership of the did. It's deprecated in AnonCreds v1, but kept for backwards compatibility + credentialRequest.prover_did = generateLegacyProverDidLikeString() + } + credentialRecord.metadata.set( AnonCredsCredentialRequestMetadataKey, credentialRequestMetadata diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 850e9c9a0f..2a8e628097 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -1,3 +1,5 @@ +import type { AnonCredsCredentialRequest } from '../../models' + import { CredentialState, CredentialExchangeRecord, @@ -187,6 +189,9 @@ describe('Legacy indy format services', () => { offerAttachment, }) + // Make sure the request contains a prover_did field + expect((requestAttachment.getDataAsJson() as AnonCredsCredentialRequest).prover_did).toBeDefined() + // Issuer processes and accepts request await indyCredentialFormatService.processRequest(agentContext, { credentialRecord: issuerCredentialRecord, diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 11e113699c..eb942ec9ef 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -9,3 +9,4 @@ export { AnonCredsModule } from './AnonCredsModule' export { AnonCredsModuleConfig, AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' export { AnonCredsApi } from './AnonCredsApi' export * from './AnonCredsApiOptions' +export { generateLegacyProverDidLikeString } from './utils/proverDid' diff --git a/packages/indy-sdk/src/anoncreds/utils/proverDid.ts b/packages/anoncreds/src/utils/proverDid.ts similarity index 100% rename from packages/indy-sdk/src/anoncreds/utils/proverDid.ts rename to packages/anoncreds/src/utils/proverDid.ts diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 394ed39198..e00718c62c 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -25,14 +25,13 @@ import type { IndyProofRequest, } from 'indy-sdk' -import { AnonCredsLinkSecretRepository } from '@aries-framework/anoncreds' +import { AnonCredsLinkSecretRepository, generateLegacyProverDidLikeString } from '@aries-framework/anoncreds' import { AriesFrameworkError, injectable, inject, utils } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' import { getIndySeqNoFromUnqualifiedCredentialDefinitionId } from '../utils/identifiers' -import { generateLegacyProverDidLikeString } from '../utils/proverDid' import { indySdkCredentialDefinitionFromAnonCreds, indySdkRevocationRegistryDefinitionFromAnonCreds, diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index ba6c2a1780..d0d0a796d1 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -12,12 +12,12 @@ import type { } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' +import { generateLegacyProverDidLikeString } from '@aries-framework/anoncreds' import { injectable, AriesFrameworkError, inject } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' -import { generateLegacyProverDidLikeString } from '../utils/proverDid' import { createTailsReader } from '../utils/tails' import { indySdkSchemaFromAnonCreds } from '../utils/transform' From e14d8539134b1d99cd9e81983cb69ead92108bbb Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 24 Feb 2023 17:59:30 +0100 Subject: [PATCH 064/139] test: increase indy-sdk timeout (#1345) Signed-off-by: Timo Glastra --- packages/indy-sdk/tests/setup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/indy-sdk/tests/setup.ts b/packages/indy-sdk/tests/setup.ts index b60b932be5..7f0aeddaa3 100644 --- a/packages/indy-sdk/tests/setup.ts +++ b/packages/indy-sdk/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(25000) +jest.setTimeout(60000) From dc60acba28752a8317b4a88937aca7aa6308a873 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 24 Feb 2023 20:50:28 +0100 Subject: [PATCH 065/139] build(anoncreds): remove node package from deps (#1339) Signed-off-by: Timo Glastra --- packages/anoncreds/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 7f0dfd3bc2..ed2353d6db 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -25,12 +25,12 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@aries-framework/node": "0.3.3", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.13.1" }, "devDependencies": { + "@aries-framework/node": "0.3.3", "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.0.7", "rxjs": "^7.8.0", From 21d4bf7652e30062e82f440f0c70470cc6f31f53 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 24 Feb 2023 22:20:54 +0100 Subject: [PATCH 066/139] feat!: allow to import created dids (and remove legacy `publicDidSeed`) (#1325) Signed-off-by: Timo Glastra --- demo/src/BaseAgent.ts | 22 -- demo/src/Faber.ts | 32 ++- .../v1-connectionless-proofs.e2e.test.ts | 1 - packages/anoncreds/tests/anoncreds.test.ts | 15 +- .../anoncreds/tests/legacyAnonCredsSetup.ts | 21 +- .../askar/src/storage/AskarStorageService.ts | 12 +- packages/askar/src/utils/askarError.ts | 5 +- packages/askar/src/wallet/AskarWallet.ts | 56 ++--- .../src/wallet/__tests__/AskarWallet.test.ts | 10 + packages/askar/tests/helpers.ts | 1 - packages/core/src/agent/AgentConfig.ts | 10 - packages/core/src/agent/BaseAgent.ts | 17 +- .../src/agent/__tests__/AgentConfig.test.ts | 4 - .../v2-connectionless-credentials.e2e.test.ts | 1 - ...f.credentials.propose-offerED25519.test.ts | 1 - packages/core/src/modules/dids/DidsApi.ts | 73 ++++++ .../core/src/modules/dids/DidsApiOptions.ts | 33 +++ .../modules/dids/__tests__/DidsApi.test.ts | 230 ++++++++++++++++++ packages/core/src/modules/dids/index.ts | 1 + .../src/modules/dids/repository/DidRecord.ts | 2 +- .../modules/dids/repository/DidRepository.ts | 34 +++ .../v2-indy-connectionless-proofs.e2e.test.ts | 1 - .../routing/__tests__/mediation.test.ts | 1 - .../storage/migration/__tests__/0.1.test.ts | 4 +- .../storage/migration/__tests__/0.2.test.ts | 4 +- .../storage/migration/__tests__/0.3.test.ts | 7 +- packages/core/src/types.ts | 1 - packages/core/src/wallet/Wallet.ts | 37 +-- packages/core/src/wallet/WalletApi.ts | 19 +- .../src/wallet/error/WalletDuplicateError.ts | 4 +- .../src/wallet/error/WalletInvalidKeyError.ts | 4 +- .../src/wallet/error/WalletKeyExistsError.ts | 7 + .../src/wallet/error/WalletNotFoundError.ts | 4 +- packages/core/src/wallet/error/index.ts | 1 + packages/core/tests/helpers.ts | 19 +- packages/core/tests/migration.test.ts | 2 +- packages/core/tests/mocks/MockWallet.ts | 6 - packages/core/tests/oob.test.ts | 1 - .../services/IndySdkAnonCredsRegistry.ts | 53 +++- .../src/dids/IndySdkSovDidRegistrar.ts | 87 +++++-- .../__tests__/IndySdkSovDidRegistrar.test.ts | 91 +++---- .../indy-sdk/src/ledger/IndySdkPoolService.ts | 28 ++- .../__tests__/IndySdkPoolService.test.ts | 8 +- .../serializeRequestForSignature.test.ts | 87 +++++++ .../ledger/serializeRequestForSignature.ts | 61 +++++ packages/indy-sdk/src/wallet/IndySdkWallet.ts | 118 ++++----- .../wallet/__tests__/IndySdkWallet.test.ts | 56 +++-- .../indy-sdk-anoncreds-registry.e2e.test.ts | 22 +- .../tests/sov-did-registrar.e2e.test.ts | 10 +- .../tests/sov-did-resolver.e2e.test.ts | 12 +- .../indy-vdr-anoncreds-registry.e2e.test.ts | 11 +- tests/e2e-test.ts | 2 - 52 files changed, 985 insertions(+), 364 deletions(-) create mode 100644 packages/core/src/modules/dids/DidsApiOptions.ts create mode 100644 packages/core/src/modules/dids/__tests__/DidsApi.test.ts create mode 100644 packages/core/src/wallet/error/WalletKeyExistsError.ts create mode 100644 packages/indy-sdk/src/ledger/__tests__/serializeRequestForSignature.test.ts create mode 100644 packages/indy-sdk/src/ledger/serializeRequestForSignature.ts diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index 26429ca358..2f1258ab90 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -12,8 +12,6 @@ import { import { AnonCredsRsModule } from '@aries-framework/anoncreds-rs' import { AskarModule } from '@aries-framework/askar' import { - TypedArrayEncoder, - KeyType, DidsModule, V2ProofProtocol, V2CredentialProtocol, @@ -53,7 +51,6 @@ export class BaseAgent { public name: string public config: InitConfig public agent: DemoAgent - public anonCredsIssuerId: string public useLegacyIndySdk: boolean public constructor({ @@ -74,15 +71,12 @@ export class BaseAgent { id: name, key: name, }, - publicDidSeed: 'afjdemoverysercure00000000000000', endpoints: [`http://localhost:${this.port}`], autoAcceptConnections: true, } satisfies InitConfig this.config = config - // TODO: do not hardcode this - this.anonCredsIssuerId = '2jEvRuKmfBJTRa7QowDpNN' this.useLegacyIndySdk = useLegacyIndySdk this.agent = new Agent({ @@ -97,22 +91,6 @@ export class BaseAgent { public async initializeAgent() { await this.agent.initialize() - // FIXME: - // We need to make sure the key to submit transactions is created. We should update this to use the dids module, and allow - // to add an existing did based on a seed/secretKey, and not register it on the the ledger. However for Indy SDK we currently - // use the deprecated publicDidSeed property (which will register the did in the wallet), and for Askar we manually create the key - // in the wallet. - if (!this.useLegacyIndySdk) { - try { - await this.agent.context.wallet.createKey({ - keyType: KeyType.Ed25519, - privateKey: TypedArrayEncoder.fromString('afjdemoverysercure00000000000000'), - }) - } catch (error) { - // We assume the key already exists, and that's why askar failed - } - } - console.log(greenText(`\nAgent ${this.name} created!\n`)) } } diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 4585f82c7d..f32d50bed8 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -2,7 +2,7 @@ import type { RegisterCredentialDefinitionReturnStateFinished } from '../../pack import type { ConnectionRecord, ConnectionStateChangedEvent } from '@aries-framework/core' import type BottomBar from 'inquirer/lib/ui/bottom-bar' -import { utils, ConnectionEventTypes } from '@aries-framework/core' +import { KeyType, TypedArrayEncoder, utils, ConnectionEventTypes } from '@aries-framework/core' import { ui } from 'inquirer' import { BaseAgent } from './BaseAgent' @@ -11,16 +11,35 @@ import { Color, greenText, Output, purpleText, redText } from './OutputClass' export class Faber extends BaseAgent { public outOfBandId?: string public credentialDefinition?: RegisterCredentialDefinitionReturnStateFinished + public anonCredsIssuerId?: string public ui: BottomBar public constructor(port: number, name: string) { - super({ port, name }) + super({ port, name, useLegacyIndySdk: true }) this.ui = new ui.BottomBar() } public static async build(): Promise { const faber = new Faber(9001, 'faber') await faber.initializeAgent() + + // NOTE: we assume the did is already registered on the ledger, we just store the private key in the wallet + // and store the existing did in the wallet + const privateKey = TypedArrayEncoder.fromString('afjdemoverysercure00000000000000') + + const key = await faber.agent.wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey, + }) + + // did is first 16 bytes of public key encoded as base58 + const unqualifiedIndyDid = TypedArrayEncoder.toBase58(key.publicKey.slice(0, 16)) + await faber.agent.dids.import({ + did: `did:sov:${unqualifiedIndyDid}`, + }) + + faber.anonCredsIssuerId = unqualifiedIndyDid + return faber } @@ -102,6 +121,9 @@ export class Faber extends BaseAgent { } private async registerSchema() { + if (!this.anonCredsIssuerId) { + throw new Error(redText('Missing anoncreds issuerId')) + } const schemaTemplate = { name: 'Faber College' + utils.uuid(), version: '1.0.0', @@ -120,7 +142,7 @@ export class Faber extends BaseAgent { if (schemaState.state !== 'finished') { throw new Error( - `Error registering schema: ${schemaState.state === 'failed' ? schemaState.reason : 'Not Finished'}}` + `Error registering schema: ${schemaState.state === 'failed' ? schemaState.reason : 'Not Finished'}` ) } this.ui.updateBottomBar('\nSchema registered!\n') @@ -128,6 +150,10 @@ export class Faber extends BaseAgent { } private async registerCredentialDefinition(schemaId: string) { + if (!this.anonCredsIssuerId) { + throw new Error(redText('Missing anoncreds issuerId')) + } + this.ui.updateBottomBar('\nRegistering credential definition...\n') const { credentialDefinitionState } = await this.agent.modules.anoncreds.registerCredentialDefinition({ credentialDefinition: { diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index be1b7c1deb..046f454c7c 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -325,7 +325,6 @@ describe('V1 Proofs - Connectionless - Indy', () => { const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'image_0', 'image_1'], - issuerId: faberAgent.publicDid?.did as string, }) const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index 5d0561d8f0..127bbee586 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -1,6 +1,6 @@ -import { Agent, KeyDerivationMethod } from '@aries-framework/core' +import { Agent, KeyDerivationMethod, KeyType, TypedArrayEncoder } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' -import indySdk from 'indy-sdk' +import * as indySdk from 'indy-sdk' import { IndySdkModule } from '../../indy-sdk/src/IndySdkModule' import { AnonCredsCredentialDefinitionRepository, AnonCredsModule, AnonCredsSchemaRepository } from '../src' @@ -186,10 +186,13 @@ describe('AnonCreds API', () => { }) test('register a credential definition', async () => { - // NOTE: the indy-sdk MUST have a did created, we can't just create a key - await agent.context.wallet.initPublicDid({ seed: '00000000000000000000000000000My1' }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const issuerId = agent.context.wallet.publicDid!.did + // Create key + await agent.wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('00000000000000000000000000000My1'), + keyType: KeyType.Ed25519, + }) + + const issuerId = 'VsKV7grR1BUE29mG2Fm2kX' const credentialDefinitionResult = await agent.modules.anoncreds.registerCredentialDefinition({ credentialDefinition: { diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 32e248f586..32a876fd1a 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -11,6 +11,7 @@ import type { import type { AutoAcceptProof, ConnectionRecord } from '@aries-framework/core' import { + TypedArrayEncoder, CacheModule, InMemoryLruCache, Agent, @@ -30,11 +31,14 @@ import { randomUUID } from 'crypto' import { AnonCredsRsModule } from '../../anoncreds-rs/src' import { AskarModule } from '../../askar/src' +import { sleep } from '../../core/src/utils/sleep' import { uuid } from '../../core/src/utils/uuid' import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests' import { getAgentOptions, + importExistingIndyDidFromPrivateKey, makeConnection, + publicDidSeed, genesisTransactions, taaVersion, taaAcceptanceMechanism, @@ -403,9 +407,6 @@ export async function setupAnonCredsTests< const { credentialDefinition, schema } = await prepareForAnonCredsIssuance(issuerAgent, { attributeNames, - // TODO: replace with more dynamic / generic value We should create a did using the dids module - // and use that probably - issuerId: issuerAgent.publicDid?.did as string, }) let issuerHolderConnection: ConnectionRecord | undefined @@ -441,10 +442,10 @@ export async function setupAnonCredsTests< } as unknown as SetupAnonCredsTestsReturn } -export async function prepareForAnonCredsIssuance( - agent: Agent, - { attributeNames, issuerId }: { attributeNames: string[]; issuerId: string } -) { +export async function prepareForAnonCredsIssuance(agent: Agent, { attributeNames }: { attributeNames: string[] }) { + // Add existing endorser did to the wallet + const issuerId = await importExistingIndyDidFromPrivateKey(agent, TypedArrayEncoder.fromString(publicDidSeed)) + const schema = await registerSchema(agent, { // TODO: update attrNames to attributeNames attrNames: attributeNames, @@ -453,12 +454,18 @@ export async function prepareForAnonCredsIssuance( issuerId, }) + // Wait some time pass to let ledger settle the object + await sleep(1000) + const credentialDefinition = await registerCredentialDefinition(agent, { schemaId: schema.schemaId, issuerId, tag: 'default', }) + // Wait some time pass to let ledger settle the object + await sleep(1000) + return { schema, credentialDefinition, diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index cdf537745d..d901f6e767 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -9,7 +9,7 @@ import { } from '@aries-framework/core' import { Scan } from '@hyperledger/aries-askar-shared' -import { askarErrors, isAskarError } from '../utils/askarError' +import { AskarErrorCode, isAskarError } from '../utils/askarError' import { assertAskarWallet } from '../utils/assertAskarWallet' import { askarQueryFromSearchQuery, recordToInstance, transformFromRecordTagValues } from './utils' @@ -29,7 +29,7 @@ export class AskarStorageService implements StorageService try { await session.insert({ category: record.type, name: record.id, value, tags }) } catch (error) { - if (isAskarError(error) && error.code === askarErrors.Duplicate) { + if (isAskarError(error, AskarErrorCode.Duplicate)) { throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type }) } @@ -50,7 +50,7 @@ export class AskarStorageService implements StorageService try { await session.replace({ category: record.type, name: record.id, value, tags }) } catch (error) { - if (isAskarError(error) && error.code === askarErrors.NotFound) { + if (isAskarError(error, AskarErrorCode.NotFound)) { throw new RecordNotFoundError(`record with id ${record.id} not found.`, { recordType: record.type, cause: error, @@ -69,7 +69,7 @@ export class AskarStorageService implements StorageService try { await session.remove({ category: record.type, name: record.id }) } catch (error) { - if (isAskarError(error) && error.code === askarErrors.NotFound) { + if (isAskarError(error, AskarErrorCode.NotFound)) { throw new RecordNotFoundError(`record with id ${record.id} not found.`, { recordType: record.type, cause: error, @@ -91,7 +91,7 @@ export class AskarStorageService implements StorageService try { await session.remove({ category: recordClass.type, name: id }) } catch (error) { - if (isAskarError(error) && error.code === askarErrors.NotFound) { + if (isAskarError(error, AskarErrorCode.NotFound)) { throw new RecordNotFoundError(`record with id ${id} not found.`, { recordType: recordClass.type, cause: error, @@ -117,7 +117,7 @@ export class AskarStorageService implements StorageService } catch (error) { if ( isAskarError(error) && - (error.code === askarErrors.NotFound || + (error.code === AskarErrorCode.NotFound || // FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario error.message === 'Received null pointer. The native library could not find the value.') ) { diff --git a/packages/askar/src/utils/askarError.ts b/packages/askar/src/utils/askarError.ts index 2cfcbd90cf..632733413a 100644 --- a/packages/askar/src/utils/askarError.ts +++ b/packages/askar/src/utils/askarError.ts @@ -1,6 +1,6 @@ import { AriesAskarError } from '@hyperledger/aries-askar-shared' -export enum askarErrors { +export enum AskarErrorCode { Success = 0, Backend = 1, Busy = 2, @@ -13,4 +13,5 @@ export enum askarErrors { Custom = 100, } -export const isAskarError = (error: Error) => error instanceof AriesAskarError +export const isAskarError = (error: Error, askarErrorCode?: AskarErrorCode): error is AriesAskarError => + error instanceof AriesAskarError && (askarErrorCode === undefined || error.code === askarErrorCode) diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 46b004006e..35831c43a6 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -2,7 +2,6 @@ import type { EncryptedMessage, WalletConfig, WalletCreateKeyOptions, - DidInfo, WalletSignOptions, UnpackedMessageContext, WalletVerifyOptions, @@ -14,11 +13,11 @@ import type { import type { Session } from '@hyperledger/aries-askar-shared' import { + WalletKeyExistsError, isValidSeed, isValidPrivateKey, JsonTransformer, RecordNotFoundError, - RecordDuplicateError, WalletInvalidKeyError, WalletDuplicateError, JsonEncoder, @@ -50,7 +49,7 @@ const isError = (error: unknown): error is Error => error instanceof Error import { inject, injectable } from 'tsyringe' import { - askarErrors, + AskarErrorCode, isAskarError, keyDerivationMethodToStoreKeyMethod, keyTypeSupportedByAskar, @@ -70,7 +69,6 @@ export class AskarWallet implements Wallet { private fileSystem: FileSystem private signingKeyProviderRegistry: SigningProviderRegistry - private publicDidInfo: DidInfo | undefined public constructor( @inject(InjectionSymbols.Logger) logger: Logger, @@ -90,10 +88,6 @@ export class AskarWallet implements Wallet { return this._store !== undefined } - public get publicDid() { - return this.publicDidInfo - } - public get store() { if (!this._store) { throw new AriesFrameworkError( @@ -151,7 +145,10 @@ export class AskarWallet implements Wallet { } catch (error) { // FIXME: Askar should throw a Duplicate error code, but is currently returning Encryption // And if we provide the very same wallet key, it will open it without any error - if (isAskarError(error) && (error.code === askarErrors.Encryption || error.code === askarErrors.Duplicate)) { + if ( + isAskarError(error) && + (error.code === AskarErrorCode.Encryption || error.code === AskarErrorCode.Duplicate) + ) { const errorMessage = `Wallet '${walletConfig.id}' already exists` this.logger.debug(errorMessage) @@ -234,7 +231,7 @@ export class AskarWallet implements Wallet { this.walletConfig = walletConfig } catch (error) { - if (isAskarError(error) && error.code === askarErrors.NotFound) { + if (isAskarError(error) && error.code === AskarErrorCode.NotFound) { const errorMessage = `Wallet '${walletConfig.id}' not found` this.logger.debug(errorMessage) @@ -242,7 +239,7 @@ export class AskarWallet implements Wallet { walletType: 'AskarWallet', cause: error, }) - } else if (isAskarError(error) && error.code === askarErrors.Encryption) { + } else if (isAskarError(error) && error.code === AskarErrorCode.Encryption) { const errorMessage = `Incorrect key for wallet '${walletConfig.id}'` this.logger.debug(errorMessage) throw new WalletInvalidKeyError(errorMessage, { @@ -314,7 +311,6 @@ export class AskarWallet implements Wallet { await this.store.close() this._session = undefined this._store = undefined - this.publicDidInfo = undefined } catch (error) { const errorMessage = `Error closing wallet': ${error.message}` this.logger.error(errorMessage, { @@ -326,22 +322,9 @@ export class AskarWallet implements Wallet { } } - public async initPublicDid() { - // Not implemented, as it does not work with legacy Ledger module - } - /** * Create a key with an optional seed and keyType. * The keypair is also automatically stored in the wallet afterwards - * - * @param privateKey Buffer Optional privateKey for creating a key - * @param seed string Optional seed for creating a key - * @param keyType KeyType the type of key that should be created - * - * @returns a Key instance with a publicKeyBase58 - * - * @throws {WalletError} When an unsupported keytype is requested - * @throws {WalletError} When the key could not be created */ public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { @@ -368,8 +351,18 @@ export class AskarWallet implements Wallet { : AskarKey.generate(algorithm) // Store key - await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(key.publicBytes) }) - return Key.fromPublicKey(key.publicBytes, keyType) + try { + await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(key.publicBytes) }) + return Key.fromPublicKey(key.publicBytes, keyType) + } catch (error) { + // Handle case where key already exists + if (isAskarError(error, AskarErrorCode.Duplicate)) { + throw new WalletKeyExistsError('Key already exists') + } + + // Otherwise re-throw error + throw error + } } else { // Check if there is a signing key provider for the specified key type. if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { @@ -382,6 +375,9 @@ export class AskarWallet implements Wallet { throw new WalletError(`Unsupported key type: '${keyType}'`) } } catch (error) { + // If already instance of `WalletError`, re-throw + if (error instanceof WalletError) throw error + if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) } @@ -721,7 +717,7 @@ export class AskarWallet implements Wallet { } catch (error) { if ( isAskarError(error) && - (error.code === askarErrors.NotFound || + (error.code === AskarErrorCode.NotFound || // FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario error.message === 'Received null pointer. The native library could not find the value.') ) { @@ -745,8 +741,8 @@ export class AskarWallet implements Wallet { }, }) } catch (error) { - if (isAskarError(error) && error.code === askarErrors.Duplicate) { - throw new RecordDuplicateError(`Record already exists`, { recordType: 'KeyPairRecord' }) + if (isAskarError(error, AskarErrorCode.Duplicate)) { + throw new WalletKeyExistsError('Key already exists') } throw new WalletError('Error saving KeyPair record', { cause: error }) } diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 37d28b14b6..2d47e201cd 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -8,6 +8,8 @@ import type { } from '@aries-framework/core' import { + WalletKeyExistsError, + Key, WalletError, WalletDuplicateError, WalletNotFoundError, @@ -97,6 +99,14 @@ describe('AskarWallet basic operations', () => { }) }) + test('throws WalletKeyExistsError when a key already exists', async () => { + const privateKey = TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b68') + await expect(askarWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).resolves.toEqual(expect.any(Key)) + await expect(askarWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError( + WalletKeyExistsError + ) + }) + describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { test('Fail to create a Bls12381g1g2 keypair', async () => { await expect(askarWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index d6e9ba0727..8be4b2a833 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -26,7 +26,6 @@ export function getPostgresAgentOptions( key: `Key${name}`, storage: storageConfig, }, - publicDidSeed, autoAcceptConnections: true, autoUpdateStorageOnStartup: false, logger: new TestLogger(LogLevel.off, name), diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index b234ed4efd..c654ef3eb0 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -33,16 +33,6 @@ export class AgentConfig { } } - /** - * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be - * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but - * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather - * use the `DidsModule`. - */ - public get publicDidSeed() { - return this.initConfig.publicDidSeed - } - /** * @todo move to context configuration */ diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 6707bef9fc..27a97be56b 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -120,7 +120,7 @@ export abstract class BaseAgent { const agentConfig = new AgentConfig( { label: 'hello', - publicDidSeed: 'hello', }, agentDependencies ) @@ -61,7 +60,6 @@ describe('AgentConfig', () => { expect(newAgentConfig).toMatchObject({ label: 'hello', - publicDidSeed: 'hello', }) }) @@ -69,7 +67,6 @@ describe('AgentConfig', () => { const agentConfig = new AgentConfig( { label: 'hello', - publicDidSeed: 'hello', }, agentDependencies ) @@ -82,7 +79,6 @@ describe('AgentConfig', () => { expect(newAgentConfig).toMatchObject({ label: 'anotherLabel', autoAcceptConnections: true, - publicDidSeed: 'hello', }) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index cb2fd2b0c1..b19448a771 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -73,7 +73,6 @@ describe('V2 Connectionless Credentials', () => { }) const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { - issuerId: faberAgent.publicDid?.did as string, attributeNames: ['name', 'age'], }) credentialDefinitionId = credentialDefinition.credentialDefinitionId diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 114cf1cf2d..8c0956a145 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -176,7 +176,6 @@ describe('V2 Credentials - JSON-LD - Ed25519', () => { const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'profile_picture', 'x-ray'], - issuerId: faberAgent.publicDid?.did as string, }) credentialDefinitionId = credentialDefinition.credentialDefinitionId diff --git a/packages/core/src/modules/dids/DidsApi.ts b/packages/core/src/modules/dids/DidsApi.ts index 49b997d8e5..e20ef573e2 100644 --- a/packages/core/src/modules/dids/DidsApi.ts +++ b/packages/core/src/modules/dids/DidsApi.ts @@ -1,3 +1,4 @@ +import type { ImportDidOptions } from './DidsApiOptions' import type { DidCreateOptions, DidCreateResult, @@ -9,7 +10,9 @@ import type { } from './types' import { AgentContext } from '../../agent' +import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' +import { WalletKeyExistsError } from '../../wallet/error' import { DidsModuleConfig } from './DidsModuleConfig' import { DidRepository } from './repository' @@ -99,4 +102,74 @@ export class DidsApi { public getCreatedDids({ method, did }: { method?: string; did?: string } = {}) { return this.didRepository.getCreatedDids(this.agentContext, { method, did }) } + + /** + * Import an existing did that was created outside of the DidsApi. This will create a `DidRecord` for the did + * and will allow the did to be used in other parts of the agent. If you need to create a new did document, + * you can use the {@link DidsApi.create} method to create and register the did. + * + * If no `didDocument` is provided, the did document will be resolved using the did resolver. You can optionally provide a list + * of private key buffer with the respective private key bytes. These keys will be stored in the wallet, and allows you to use the + * did for other operations. Providing keys that already exist in the wallet is allowed, and those keys will be skipped from being + * added to the wallet. + * + * By default, this method will throw an error if the did already exists in the wallet. You can override this behavior by setting + * the `overwrite` option to `true`. This will update the did document in the record, and allows you to update the did over time. + */ + public async import({ did, didDocument, privateKeys = [], overwrite }: ImportDidOptions) { + if (didDocument && didDocument.id !== did) { + throw new AriesFrameworkError(`Did document id ${didDocument.id} does not match did ${did}`) + } + + const existingDidRecord = await this.didRepository.findCreatedDid(this.agentContext, did) + if (existingDidRecord && !overwrite) { + throw new AriesFrameworkError( + `A created did ${did} already exists. If you want to override the existing did, set the 'overwrite' option to update the did.` + ) + } + + if (!didDocument) { + didDocument = await this.resolveDidDocument(did) + } + + // Loop over all private keys and store them in the wallet. We don't check whether the keys are actually associated + // with the did document, this is up to the user. + for (const key of privateKeys) { + try { + // We can't check whether the key already exists in the wallet, but we can try to create it and catch the error + // if the key already exists. + await this.agentContext.wallet.createKey({ + keyType: key.keyType, + privateKey: key.privateKey, + }) + } catch (error) { + if (error instanceof WalletKeyExistsError) { + // If the error is a WalletKeyExistsError, we can ignore it. This means the key + // already exists in the wallet. We don't want to throw an error in this case. + } else { + throw error + } + } + } + + // Update existing did record + if (existingDidRecord) { + existingDidRecord.didDocument = didDocument + existingDidRecord.setTags({ + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + }) + + await this.didRepository.update(this.agentContext, existingDidRecord) + return + } + + // Create new did record + await this.didRepository.storeCreatedDid(this.agentContext, { + did, + didDocument, + tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint), + }, + }) + } } diff --git a/packages/core/src/modules/dids/DidsApiOptions.ts b/packages/core/src/modules/dids/DidsApiOptions.ts new file mode 100644 index 0000000000..8561296a66 --- /dev/null +++ b/packages/core/src/modules/dids/DidsApiOptions.ts @@ -0,0 +1,33 @@ +import type { DidDocument } from './domain' +import type { KeyType } from '../../crypto' +import type { Buffer } from '../../utils' + +interface PrivateKey { + keyType: KeyType + privateKey: Buffer +} + +export interface ImportDidOptions { + /** + * The did to import. + */ + did: string + + /** + * Optional did document to import. If not provided, the did document will be resolved using the did resolver. + */ + didDocument?: DidDocument + + /** + * List of private keys associated with the did document that should be stored in the wallet. + */ + privateKeys?: PrivateKey[] + + /** + * Whether to overwrite an existing did record if it exists. If set to false, + * an error will be thrown if the did record already exists. + * + * @default false + */ + overwrite?: boolean +} diff --git a/packages/core/src/modules/dids/__tests__/DidsApi.test.ts b/packages/core/src/modules/dids/__tests__/DidsApi.test.ts new file mode 100644 index 0000000000..41993e9d43 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/DidsApi.test.ts @@ -0,0 +1,230 @@ +import { IndySdkModule } from '../../../../../indy-sdk/src' +import { indySdk } from '../../../../tests' +import { getAgentOptions } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' + +import { DidDocument, DidDocumentService, KeyType, TypedArrayEncoder } from '@aries-framework/core' + +const agentOptions = getAgentOptions( + 'DidsApi', + {}, + { + indySdk: new IndySdkModule({ + indySdk, + }), + } +) + +const agent = new Agent(agentOptions) + +describe('DidsApi', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + test('import an existing did without providing a did document', async () => { + const createKeySpy = jest.spyOn(agent.context.wallet, 'createKey') + + // Private key is for public key associated with did:key did + const privateKey = TypedArrayEncoder.fromString('a-sample-seed-of-32-bytes-in-tot') + const did = 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty' + + expect(await agent.dids.getCreatedDids({ did })).toHaveLength(0) + + await agent.dids.import({ + did, + privateKeys: [ + { + privateKey, + keyType: KeyType.Ed25519, + }, + ], + }) + + expect(createKeySpy).toHaveBeenCalledWith({ + privateKey, + keyType: KeyType.Ed25519, + }) + + const createdDids = await agent.dids.getCreatedDids({ + did, + }) + expect(createdDids).toHaveLength(1) + + expect(createdDids[0].getTags()).toEqual({ + did, + legacyUnqualifiedDid: undefined, + method: 'key', + methodSpecificIdentifier: 'z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + role: 'created', + }) + + expect(createdDids[0].toJSON()).toMatchObject({ + did, + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + + verificationMethod: [ + { + id: 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty#z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + type: 'Ed25519VerificationKey2018', + controller: 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + publicKeyBase58: '5nKwL9aJ9kpnEE1pSsqvLMqDnE1ubeBr4TjzC56roC7b', + }, + ], + + authentication: [ + 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty#z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + ], + assertionMethod: [ + 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty#z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + ], + keyAgreement: [ + { + id: 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty#z6LSd6ed6s6HGsVsDL9vyx3s1Vi2jQYsX9TqjqVFam2oz776', + type: 'X25519KeyAgreementKey2019', + controller: 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + publicKeyBase58: '2RUTaZHRBQn87wnATJXuguVYtG1kpYHgrrma6JPHGjLL', + }, + ], + capabilityInvocation: [ + 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty#z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + ], + capabilityDelegation: [ + 'did:key:z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty#z6MkjEayvPpjVJKFLirX8SomBTPDboHm1XSCkUev2M4siQty', + ], + }, + }) + }) + + test('import an existing did with providing a did document', async () => { + const createKeySpy = jest.spyOn(agent.context.wallet, 'createKey') + + // Private key is for public key associated with did:key did + const privateKey = TypedArrayEncoder.fromString('a-new-sample-seed-of-32-bytes-in') + const did = 'did:peer:0z6Mkhu3G8viiebsWmCiSgWiQoCZrTeuX76oLDow81YNYvJQM' + + expect(await agent.dids.getCreatedDids({ did })).toHaveLength(0) + + await agent.dids.import({ + did, + didDocument: new DidDocument({ + id: did, + }), + privateKeys: [ + { + privateKey, + keyType: KeyType.Ed25519, + }, + ], + }) + + expect(createKeySpy).toHaveBeenCalledWith({ + privateKey, + keyType: KeyType.Ed25519, + }) + + const createdDids = await agent.dids.getCreatedDids({ + did, + }) + expect(createdDids).toHaveLength(1) + + expect(createdDids[0].getTags()).toEqual({ + did, + legacyUnqualifiedDid: undefined, + method: 'peer', + methodSpecificIdentifier: '0z6Mkhu3G8viiebsWmCiSgWiQoCZrTeuX76oLDow81YNYvJQM', + role: 'created', + }) + + expect(createdDids[0].toJSON()).toMatchObject({ + did, + didDocument: { + id: did, + }, + }) + }) + + test('can only overwrite if overwrite option is set', async () => { + const did = 'did:example:123' + const didDocument = new DidDocument({ id: did }) + const didDocument2 = new DidDocument({ + id: did, + service: [new DidDocumentService({ id: 'did:example:123#service', type: 'test', serviceEndpoint: 'test' })], + }) + + expect(await agent.dids.getCreatedDids({ did })).toHaveLength(0) + + // First import, should work + await agent.dids.import({ + did, + didDocument, + }) + + expect(await agent.dids.getCreatedDids({ did })).toHaveLength(1) + expect( + agent.dids.import({ + did, + didDocument: didDocument2, + }) + ).rejects.toThrowError( + "A created did did:example:123 already exists. If you want to override the existing did, set the 'overwrite' option to update the did." + ) + + // Should not have stored the updated record + const createdDids = await agent.dids.getCreatedDids({ did }) + expect(createdDids[0].didDocument?.service).toBeUndefined() + + // Should work, overwrite is set + await agent.dids.import({ + did, + didDocument: didDocument2, + overwrite: true, + }) + + // Should not have stored the updated record + const createdDidsOverwrite = await agent.dids.getCreatedDids({ did }) + expect(createdDidsOverwrite[0].didDocument?.service).toHaveLength(1) + }) + + test('providing privateKeys that already exist is allowd', async () => { + const privateKey = TypedArrayEncoder.fromString('another-samples-seed-of-32-bytes') + + const did = 'did:example:456' + const didDocument = new DidDocument({ id: did }) + + await agent.dids.import({ + did, + didDocument, + privateKeys: [ + { + keyType: KeyType.Ed25519, + privateKey, + }, + ], + }) + + // Provide the same key again, should work + await agent.dids.import({ + did, + didDocument, + overwrite: true, + privateKeys: [ + { + keyType: KeyType.Ed25519, + privateKey, + }, + ], + }) + }) +}) diff --git a/packages/core/src/modules/dids/index.ts b/packages/core/src/modules/dids/index.ts index 9ad363c0a2..5f1677eb88 100644 --- a/packages/core/src/modules/dids/index.ts +++ b/packages/core/src/modules/dids/index.ts @@ -1,6 +1,7 @@ export * from './types' export * from './domain' export * from './DidsApi' +export * from './DidsApiOptions' export * from './repository' export * from './services' export * from './DidsModule' diff --git a/packages/core/src/modules/dids/repository/DidRecord.ts b/packages/core/src/modules/dids/repository/DidRecord.ts index b36f3f03d0..5f8b9cc375 100644 --- a/packages/core/src/modules/dids/repository/DidRecord.ts +++ b/packages/core/src/modules/dids/repository/DidRecord.ts @@ -21,7 +21,7 @@ export interface DidRecordProps { tags?: CustomDidTags } -interface CustomDidTags extends TagsBase { +export interface CustomDidTags extends TagsBase { recipientKeyFingerprints?: string[] } diff --git a/packages/core/src/modules/dids/repository/DidRepository.ts b/packages/core/src/modules/dids/repository/DidRepository.ts index 538270eac5..11a6c60b9a 100644 --- a/packages/core/src/modules/dids/repository/DidRepository.ts +++ b/packages/core/src/modules/dids/repository/DidRepository.ts @@ -1,5 +1,7 @@ +import type { CustomDidTags } from './DidRecord' import type { AgentContext } from '../../../agent' import type { Key } from '../../../crypto' +import type { DidDocument } from '../domain' import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' @@ -64,4 +66,36 @@ export class DidRepository extends Repository { did, }) } + + public async storeCreatedDid(agentContext: AgentContext, { did, didDocument, tags }: StoreDidOptions) { + const didRecord = new DidRecord({ + did, + didDocument, + role: DidDocumentRole.Created, + tags, + }) + + await this.save(agentContext, didRecord) + + return didRecord + } + + public async storeReceivedDid(agentContext: AgentContext, { did, didDocument, tags }: StoreDidOptions) { + const didRecord = new DidRecord({ + did, + didDocument, + role: DidDocumentRole.Received, + tags, + }) + + await this.save(agentContext, didRecord) + + return didRecord + } +} + +interface StoreDidOptions { + did: string + didDocument?: DidDocument + tags?: CustomDidTags } diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index 5b343f3471..d272ee4fc5 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -329,7 +329,6 @@ describe('V2 Connectionless Proofs - Indy', () => { const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'image_0', 'image_1'], - issuerId: faberAgent.publicDid?.did as string, }) const [faberConnection] = await makeConnection(faberAgent, aliceAgent) diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index e792d1c32c..8554984d2f 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -10,7 +10,6 @@ import { SubjectOutboundTransport } from '../../../../../../tests/transport/Subj import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' -import { sleep } from '../../../utils/sleep' import { ConnectionRecord, HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index cd5fddaccf..ff2033f3ba 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -1,7 +1,6 @@ -import type { FileSystem } from '../../../../src' import type { V0_1ToV0_2UpdateConfig } from '../updates/0.1-0.2' -import { unlinkSync, readFileSync } from 'fs' +import { readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' @@ -17,7 +16,6 @@ import { UpdateAssistant } from '../UpdateAssistant' const backupDate = new Date('2022-01-21T22:50:20.522Z') jest.useFakeTimers().setSystemTime(backupDate) -const backupIdentifier = backupDate.getTime() const walletConfig = { id: `Wallet: 0.1 Update`, diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index 1f619b1b57..e1d0521dd5 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -1,6 +1,4 @@ -import type { FileSystem } from '../../../storage/FileSystem' - -import { unlinkSync, readFileSync } from 'fs' +import { readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index 29ed62d1b9..47cf43bff6 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -1,6 +1,4 @@ -import type { FileSystem } from '../../FileSystem' - -import { unlinkSync, readFileSync } from 'fs' +import { readFileSync } from 'fs' import path from 'path' import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' @@ -16,7 +14,6 @@ import { UpdateAssistant } from '../UpdateAssistant' const backupDate = new Date('2022-01-21T22:50:20.522Z') jest.useFakeTimers().setSystemTime(backupDate) -const backupIdentifier = backupDate.getTime() const walletConfig = { id: `Wallet: 0.3 Update`, @@ -49,8 +46,6 @@ describe('UpdateAssistant | v0.3 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index c80de6c997..7a62a494fa 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -52,7 +52,6 @@ export enum DidCommMimeType { export interface InitConfig { endpoints?: string[] label: string - publicDidSeed?: string walletConfig?: WalletConfig logger?: Logger didCommMimeType?: DidCommMimeType diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 4fcf70f5c2..667448efc3 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -10,14 +10,6 @@ import type { import type { Buffer } from '../utils/buffer' export interface Wallet extends Disposable { - /** - * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be - * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but - * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather - * use the `DidsModule`. - */ - publicDid: DidInfo | undefined - isInitialized: boolean isProvisioned: boolean @@ -30,29 +22,28 @@ export interface Wallet extends Disposable { export(exportConfig: WalletExportImportConfig): Promise import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise + /** + * Create a key with an optional private key and keyType. + * + * @param options.privateKey Buffer Private key (formerly called 'seed') + * @param options.keyType KeyType the type of key that should be created + * + * @returns a `Key` instance + * + * @throws {WalletError} When an unsupported keytype is requested + * @throws {WalletError} When the key could not be created + * @throws {WalletKeyExistsError} When the key already exists in the wallet + */ createKey(options: WalletCreateKeyOptions): Promise sign(options: WalletSignOptions): Promise verify(options: WalletVerifyOptions): Promise - /** - * @deprecated The public did functionality of the wallet has been deprecated in favour of the DidsModule, which can be - * used to create and resolve dids. Currently the global agent public did functionality is still used by the `LedgerModule`, but - * will be removed once the `LedgerModule` has been deprecated. Do not use this property for new functionality, but rather - * use the `DidsModule`. - */ - initPublicDid(didConfig: DidConfig): Promise - pack(payload: Record, recipientKeys: string[], senderVerkey?: string): Promise unpack(encryptedMessage: EncryptedMessage): Promise generateNonce(): Promise generateWalletKey(): Promise } -export interface DidInfo { - did: string - verkey: string -} - export interface WalletCreateKeyOptions { keyType: KeyType seed?: Buffer @@ -70,10 +61,6 @@ export interface WalletVerifyOptions { signature: Buffer } -export interface DidConfig { - seed?: string -} - export interface UnpackedMessageContext { plaintextMessage: PlaintextMessage senderKey?: string diff --git a/packages/core/src/wallet/WalletApi.ts b/packages/core/src/wallet/WalletApi.ts index 144e9722c3..ac987b1c75 100644 --- a/packages/core/src/wallet/WalletApi.ts +++ b/packages/core/src/wallet/WalletApi.ts @@ -1,4 +1,4 @@ -import type { Wallet } from './Wallet' +import type { Wallet, WalletCreateKeyOptions } from './Wallet' import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '../types' import { AgentContext } from '../agent' @@ -116,4 +116,21 @@ export class WalletApi { public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise { await this.wallet.import(walletConfig, importConfig) } + + /** + * Create a key for and store it in the wallet. You can optionally provide a `privateKey` + * or `seed` for deterministic key generation. + * + * @param privateKey Buffer Private key (formerly called 'seed') + * @param seed Buffer (formerly called 'seed') + * @param keyType KeyType the type of key that should be created + * + * @returns a `Key` instance + * + * @throws {WalletError} When an unsupported `KeyType` is provided + * @throws {WalletError} When the key could not be created + */ + public async createKey(options: WalletCreateKeyOptions) { + return this.wallet.createKey(options) + } } diff --git a/packages/core/src/wallet/error/WalletDuplicateError.ts b/packages/core/src/wallet/error/WalletDuplicateError.ts index d7aa80c1c0..615b2563bb 100644 --- a/packages/core/src/wallet/error/WalletDuplicateError.ts +++ b/packages/core/src/wallet/error/WalletDuplicateError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from '../../error/AriesFrameworkError' +import { WalletError } from './WalletError' -export class WalletDuplicateError extends AriesFrameworkError { +export class WalletDuplicateError extends WalletError { public constructor(message: string, { walletType, cause }: { walletType: string; cause?: Error }) { super(`${walletType}: ${message}`, { cause }) } diff --git a/packages/core/src/wallet/error/WalletInvalidKeyError.ts b/packages/core/src/wallet/error/WalletInvalidKeyError.ts index d3562d9bce..b7a29de2d9 100644 --- a/packages/core/src/wallet/error/WalletInvalidKeyError.ts +++ b/packages/core/src/wallet/error/WalletInvalidKeyError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from '../../error/AriesFrameworkError' +import { WalletError } from './WalletError' -export class WalletInvalidKeyError extends AriesFrameworkError { +export class WalletInvalidKeyError extends WalletError { public constructor(message: string, { walletType, cause }: { walletType: string; cause?: Error }) { super(`${walletType}: ${message}`, { cause }) } diff --git a/packages/core/src/wallet/error/WalletKeyExistsError.ts b/packages/core/src/wallet/error/WalletKeyExistsError.ts new file mode 100644 index 0000000000..3e0a19e7b4 --- /dev/null +++ b/packages/core/src/wallet/error/WalletKeyExistsError.ts @@ -0,0 +1,7 @@ +import { WalletError } from './WalletError' + +export class WalletKeyExistsError extends WalletError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/core/src/wallet/error/WalletNotFoundError.ts b/packages/core/src/wallet/error/WalletNotFoundError.ts index b5cc194fd8..a2e8d32d45 100644 --- a/packages/core/src/wallet/error/WalletNotFoundError.ts +++ b/packages/core/src/wallet/error/WalletNotFoundError.ts @@ -1,6 +1,6 @@ -import { AriesFrameworkError } from '../../error/AriesFrameworkError' +import { WalletError } from './WalletError' -export class WalletNotFoundError extends AriesFrameworkError { +export class WalletNotFoundError extends WalletError { public constructor(message: string, { walletType, cause }: { walletType: string; cause?: Error }) { super(`${walletType}: ${message}`, { cause }) } diff --git a/packages/core/src/wallet/error/index.ts b/packages/core/src/wallet/error/index.ts index 222cb8a532..1337a5dd46 100644 --- a/packages/core/src/wallet/error/index.ts +++ b/packages/core/src/wallet/error/index.ts @@ -2,3 +2,4 @@ export { WalletDuplicateError } from './WalletDuplicateError' export { WalletNotFoundError } from './WalletNotFoundError' export { WalletInvalidKeyError } from './WalletInvalidKeyError' export { WalletError } from './WalletError' +export { WalletKeyExistsError } from './WalletKeyExistsError' diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 1ae3f39709..3309c1afdf 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -12,6 +12,7 @@ import type { Wallet, Agent, CredentialState, + Buffer, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' @@ -26,6 +27,7 @@ import { catchError, filter, map, take, timeout } from 'rxjs/operators' import { agentDependencies, IndySdkPostgresWalletScheme } from '../../node/src' import { + TypedArrayEncoder, AgentConfig, AgentContext, BasicMessageEventTypes, @@ -75,7 +77,6 @@ export function getAgentOptions = {} diff --git a/packages/core/tests/migration.test.ts b/packages/core/tests/migration.test.ts index fbf05abf3f..ac898caa73 100644 --- a/packages/core/tests/migration.test.ts +++ b/packages/core/tests/migration.test.ts @@ -6,7 +6,7 @@ import { UpdateAssistant } from '../src/storage/migration/UpdateAssistant' import { getAgentOptions } from './helpers' -const agentOptions = getAgentOptions('Migration', { publicDidSeed: undefined }, getIndySdkModules()) +const agentOptions = getAgentOptions('Migration', {}, getIndySdkModules()) describe('migration', () => { test('manually initiating the update assistant to perform an update', async () => { diff --git a/packages/core/tests/mocks/MockWallet.ts b/packages/core/tests/mocks/MockWallet.ts index 7f941325b7..c2a1f176f0 100644 --- a/packages/core/tests/mocks/MockWallet.ts +++ b/packages/core/tests/mocks/MockWallet.ts @@ -4,16 +4,13 @@ import type { Key } from '../../src/crypto' import type { EncryptedMessage, WalletConfig, WalletExportImportConfig, WalletConfigRekey } from '../../src/types' import type { Buffer } from '../../src/utils/buffer' import type { - DidInfo, UnpackedMessageContext, - DidConfig, WalletCreateKeyOptions, WalletSignOptions, WalletVerifyOptions, } from '../../src/wallet' export class MockWallet implements Wallet { - public publicDid = undefined public isInitialized = true public isProvisioned = true @@ -41,9 +38,6 @@ export class MockWallet implements Wallet { public import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise { throw new Error('Method not implemented.') } - public initPublicDid(didConfig: DidConfig): Promise { - throw new Error('Method not implemented.') - } public pack( payload: Record, recipientKeys: string[], diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 836ca766bd..e5f2b34550 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -89,7 +89,6 @@ describe('out of band', () => { const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, { attributeNames: ['name', 'age', 'profile_picture', 'x-ray'], - issuerId: faberAgent.publicDid?.did as string, }) credentialTemplate = { diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 8b9157221a..8e97b63748 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -13,6 +13,8 @@ import type { import type { AgentContext } from '@aries-framework/core' import type { Schema as IndySdkSchema } from 'indy-sdk' +import { DidsApi, getKeyDidMappingByVerificationMethod } from '@aries-framework/core' + import { IndySdkError, isIndyError } from '../../error' import { IndySdkPoolService } from '../../ledger' import { IndySdkSymbol } from '../../types' @@ -139,7 +141,28 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const request = await indySdk.buildSchemaRequest(options.schema.issuerId, schema) - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, options.schema.issuerId) + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(`did:sov:${options.schema.issuerId}`) + + if (!didResult.didDocument) { + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + schema: options.schema, + state: 'failed', + reason: `didNotFound: unable to resolve did did:sov:${options.schema.issuerId}: ${didResult.didResolutionMetadata.message}`, + }, + } + } + + const verificationMethod = didResult.didDocument.dereferenceKey(`did:sov:${options.schema.issuerId}#key-1`) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const submitterKey = getKeyFromVerificationMethod(verificationMethod) + + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) agentContext.config.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.didIndyNamespace}'`, { response, schema, @@ -330,12 +353,30 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ver: '1.0', }) - const response = await indySdkPoolService.submitWriteRequest( - agentContext, - pool, - request, - options.credentialDefinition.issuerId + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(`did:sov:${options.credentialDefinition.issuerId}`) + + if (!didResult.didDocument) { + return { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + credentialDefinition: options.credentialDefinition, + state: 'failed', + reason: `didNotFound: unable to resolve did did:sov:${options.credentialDefinition.issuerId}: ${didResult.didResolutionMetadata.message}`, + }, + } + } + + const verificationMethod = didResult.didDocument.dereferenceKey( + `did:sov:${options.credentialDefinition.issuerId}#key-1` ) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const submitterKey = getKeyFromVerificationMethod(verificationMethod) + + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) agentContext.config.logger.debug( `Registered credential definition '${credentialDefinitionId}' on ledger '${pool.didIndyNamespace}'`, diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts index 4c88f84427..c85cf7c0e0 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -8,18 +8,26 @@ import type { DidCreateResult, DidDeactivateResult, DidUpdateResult, - Key, Buffer, + Key, } from '@aries-framework/core' import type { NymRole } from 'indy-sdk' -import { KeyType, isValidPrivateKey, DidDocumentRole, DidRecord, DidRepository } from '@aries-framework/core' +import { + DidsApi, + getKeyDidMappingByVerificationMethod, + KeyType, + isValidPrivateKey, + DidDocumentRole, + DidRecord, + DidRepository, +} from '@aries-framework/core' import { IndySdkError } from '../error' import { isIndyError } from '../error/indyError' import { IndySdkPoolService } from '../ledger' import { IndySdkSymbol } from '../types' -import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' +import { indyDidFromPublicKeyBase58 } from '../utils/did' import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' @@ -27,11 +35,10 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { public readonly supportedMethods = ['sov'] public async create(agentContext: AgentContext, options: IndySdkSovDidCreateOptions): Promise { - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const didRepository = agentContext.dependencyManager.resolve(DidRepository) - const { alias, role, submitterDid, indyNamespace } = options.options + const { alias, role, submitterVerificationMethod, indyNamespace } = options.options const privateKey = options.secret?.privateKey if (privateKey && !isValidPrivateKey(privateKey, KeyType.Ed25519)) { @@ -45,7 +52,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { } } - if (!submitterDid.startsWith('did:sov:')) { + if (!submitterVerificationMethod.startsWith('did:sov:')) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -57,27 +64,55 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { } try { - // NOTE: we need to use the createAndStoreMyDid method from indy to create the did - // If we just create a key and handle the creating of the did ourselves, indy will throw a - // WalletItemNotFound when it needs to sign ledger transactions using this did. This means we need - // to rely directly on the indy SDK, as we don't want to expose a createDid method just for. - assertIndySdkWallet(agentContext.wallet) - const [unqualifiedIndyDid, verkey] = await indySdk.createAndStoreMyDid(agentContext.wallet.handle, { - seed: privateKey?.toString(), + const signingKey = await agentContext.wallet.createKey({ + privateKey, + keyType: KeyType.Ed25519, }) + const verkey = signingKey.publicKeyBase58 + + const unqualifiedIndyDid = indyDidFromPublicKeyBase58(verkey) + + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(submitterVerificationMethod) + + if (!didResult.didDocument) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `didNotFound: unable to resolve did ${submitterVerificationMethod}: ${didResult.didResolutionMetadata.message}`, + }, + } + } + + const verificationMethod = didResult.didDocument.dereferenceKey(submitterVerificationMethod) + const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) + const submitterSigningKey = getKeyFromVerificationMethod(verificationMethod) const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` - const unqualifiedSubmitterDid = submitterDid.replace('did:sov:', '') + const [unqualifiedSubmitterDid] = submitterVerificationMethod.replace('did:sov:', '').split('#') const pool = indySdkPoolService.getPoolForNamespace(indyNamespace) - await this.registerPublicDid(agentContext, unqualifiedSubmitterDid, unqualifiedIndyDid, verkey, alias, pool, role) + await this.registerPublicDid( + agentContext, + unqualifiedSubmitterDid, + submitterSigningKey, + unqualifiedIndyDid, + signingKey, + alias, + pool, + role + ) // Create did document const didDocumentBuilder = sovDidDocumentFromDid(qualifiedSovDid, verkey) // Add services if endpoints object was passed. if (options.options.endpoints) { - await this.setEndpointsForDid(agentContext, unqualifiedIndyDid, options.options.endpoints, pool) + await this.setEndpointsForDid(agentContext, unqualifiedIndyDid, signingKey, options.options.endpoints, pool) addServicesFromEndpointsAttrib( didDocumentBuilder, qualifiedSovDid, @@ -161,8 +196,9 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { public async registerPublicDid( agentContext: AgentContext, submitterDid: string, + submitterSigningKey: Key, targetDid: string, - verkey: string, + signingKey: Key, alias: string, pool: IndySdkPool, role?: NymRole @@ -173,9 +209,15 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { try { agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`) - const request = await indySdk.buildNymRequest(submitterDid, targetDid, verkey, alias, role || null) + const request = await indySdk.buildNymRequest( + submitterDid, + targetDid, + signingKey.publicKeyBase58, + alias, + role || null + ) - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterDid) + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterSigningKey) agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`, { response, @@ -189,7 +231,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { error, submitterDid, targetDid, - verkey, + verkey: signingKey.publicKeyBase58, alias, role, pool: pool.didIndyNamespace, @@ -203,6 +245,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { public async setEndpointsForDid( agentContext: AgentContext, did: string, + signingKey: Key, endpoints: IndyEndpointAttrib, pool: IndySdkPool ): Promise { @@ -214,7 +257,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { const request = await indySdk.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, did) + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, signingKey) agentContext.config.logger.debug( `Successfully set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, { @@ -248,7 +291,7 @@ export interface IndySdkSovDidCreateOptions extends DidCreateOptions { role?: NymRole endpoints?: IndyEndpointAttrib indyNamespace?: string - submitterDid: string + submitterVerificationMethod: string } secret?: { privateKey?: Buffer diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts index 29ea34618c..2f63d2a91a 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts @@ -2,22 +2,23 @@ import type { IndySdkPool } from '../../ledger/IndySdkPool' import type { Wallet, DidRecord, RecordSavedEvent } from '@aries-framework/core' import { + DidsApi, + DidDocument, + VerificationMethod, + KeyType, + Key, TypedArrayEncoder, DidRepository, - SigningProviderRegistry, JsonTransformer, DidDocumentRole, EventEmitter, RepositoryEventTypes, } from '@aries-framework/core' -import indySdk from 'indy-sdk' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' -import { mockFunction, getAgentConfig, getAgentContext, agentDependencies, mockProperty } from '../../../../core/tests' +import { mockFunction, getAgentConfig, getAgentContext, agentDependencies } from '../../../../core/tests' import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' -import { IndySdkSymbol } from '../../types' -import { IndySdkWallet } from '../../wallet' import { IndySdkSovDidRegistrar } from '../IndySdkSovDidRegistrar' jest.mock('../../ledger/IndySdkPoolService') @@ -30,20 +31,38 @@ mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ const agentConfig = getAgentConfig('IndySdkSovDidRegistrar') -const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const wallet = { + createKey: jest + .fn() + .mockResolvedValue(Key.fromPublicKeyBase58('E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', KeyType.Ed25519)), +} as unknown as Wallet const storageService = new InMemoryStorageService() const eventEmitter = new EventEmitter(agentDependencies, new Subject()) const didRepository = new DidRepository(storageService, eventEmitter) -const createDidMock = jest.fn(async () => ['R1xKJw17sUoXhejEpugMYJ', 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu']) -mockProperty(wallet, 'handle', 10) - const agentContext = getAgentContext({ wallet, registerInstances: [ [DidRepository, didRepository], [IndySdkPoolService, indySdkPoolServiceMock], - [IndySdkSymbol, { createAndStoreMyDid: createDidMock }], + [ + DidsApi, + { + resolve: jest.fn().mockResolvedValue({ + didDocument: new DidDocument({ + id: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + authentication: [ + new VerificationMethod({ + id: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', + type: 'Ed25519VerificationKey2018', + controller: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }), + ], + }), + }), + }, + ], ], agentConfig, }) @@ -60,7 +79,7 @@ describe('IndySdkSovDidRegistrar', () => { method: 'sov', options: { - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', alias: 'Hello', }, secret: { @@ -78,44 +97,11 @@ describe('IndySdkSovDidRegistrar', () => { }) }) - it('should return an error state if the wallet is not an indy wallet', async () => { - const agentContext = getAgentContext({ - wallet: {} as unknown as Wallet, - agentConfig, - registerInstances: [ - [DidRepository, didRepository], - [IndySdkPoolService, indySdkPoolServiceMock], - [IndySdkSymbol, indySdk], - ], - }) - - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - - options: { - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - alias: 'Hello', - }, - secret: { - privateKey: TypedArrayEncoder.fromString('12345678901234567890123456789012'), - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'unknownError: Expected wallet to be instance of IndySdkWallet, found Object', - }, - }) - }) - it('should return an error state if the submitter did is not qualified with did:sov', async () => { const result = await indySdkSovDidRegistrar.create(agentContext, { method: 'sov', options: { - submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', + submitterVerificationMethod: 'BzCbsNYhMrjHiqZDTUASHg', alias: 'Hello', }, }) @@ -140,22 +126,23 @@ describe('IndySdkSovDidRegistrar', () => { method: 'sov', options: { alias: 'Hello', - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', role: 'STEWARD', }, secret: { privateKey, }, }) - expect(registerPublicDidSpy).toHaveBeenCalledWith( agentContext, // Unqualified submitter did 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), // Unqualified created indy did 'R1xKJw17sUoXhejEpugMYJ', // Verkey - 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + expect.any(Key), // Alias 'Hello', // Pool @@ -218,7 +205,7 @@ describe('IndySdkSovDidRegistrar', () => { method: 'sov', options: { alias: 'Hello', - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', role: 'STEWARD', endpoints: { endpoint: 'https://example.com/endpoint', @@ -235,10 +222,12 @@ describe('IndySdkSovDidRegistrar', () => { agentContext, // Unqualified submitter did 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), // Unqualified created indy did 'R1xKJw17sUoXhejEpugMYJ', // Verkey - 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + expect.any(Key), // Alias 'Hello', // Pool @@ -328,7 +317,7 @@ describe('IndySdkSovDidRegistrar', () => { method: 'sov', options: { alias: 'Hello', - submitterDid: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', + submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', role: 'STEWARD', endpoints: { endpoint: 'https://example.com/endpoint', diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index 16bc0ac6a2..be9217b0ed 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -1,9 +1,17 @@ import type { AcceptanceMechanisms, AuthorAgreement } from './IndySdkPool' import type { IndySdk } from '../types' -import type { AgentContext } from '@aries-framework/core' +import type { AgentContext, Key } from '@aries-framework/core' import type { GetNymResponse, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' -import { CacheModuleConfig, InjectionSymbols, Logger, injectable, inject, FileSystem } from '@aries-framework/core' +import { + TypedArrayEncoder, + CacheModuleConfig, + InjectionSymbols, + Logger, + injectable, + inject, + FileSystem, +} from '@aries-framework/core' import { Subject } from 'rxjs' import { IndySdkModuleConfig } from '../IndySdkModuleConfig' @@ -14,6 +22,7 @@ import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' import { IndySdkPool } from './IndySdkPool' import { IndySdkPoolError, IndySdkPoolNotConfiguredError, IndySdkPoolNotFoundError } from './error' +import { serializeRequestForSignature } from './serializeRequestForSignature' export interface CachedDidResponse { nymResponse: GetNymResponse @@ -160,11 +169,11 @@ export class IndySdkPoolService { agentContext: AgentContext, pool: IndySdkPool, request: LedgerRequest, - signDid: string + signingKey: Key ): Promise { try { const requestWithTaa = await this.appendTaa(pool, request) - const signedRequestWithTaa = await this.signRequest(agentContext, signDid, requestWithTaa) + const signedRequestWithTaa = await this.signRequest(agentContext, signingKey, requestWithTaa) const response = await pool.submitWriteRequest(signedRequestWithTaa) @@ -184,11 +193,18 @@ export class IndySdkPoolService { } } - private async signRequest(agentContext: AgentContext, did: string, request: LedgerRequest): Promise { + private async signRequest(agentContext: AgentContext, key: Key, request: LedgerRequest): Promise { assertIndySdkWallet(agentContext.wallet) try { - return this.indySdk.signRequest(agentContext.wallet.handle, did, request) + const signedPayload = await this.indySdk.cryptoSign( + agentContext.wallet.handle, + key.publicKeyBase58, + TypedArrayEncoder.fromString(serializeRequestForSignature(request)) + ) + + const signedRequest = { ...request, signature: TypedArrayEncoder.toBase58(signedPayload) } + return signedRequest } catch (error) { throw isIndyError(error) ? new IndySdkError(error) : error } diff --git a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts index ba96c32b48..ee595c31ec 100644 --- a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts +++ b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts @@ -6,6 +6,8 @@ import { InMemoryLruCache, SigningProviderRegistry, AriesFrameworkError, + Key, + KeyType, } from '@aries-framework/core' import indySdk from 'indy-sdk' import { Subject } from 'rxjs' @@ -328,7 +330,7 @@ describe('IndySdkPoolService', () => { }, protocolVersion: 2, }, - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + Key.fromPublicKeyBase58('GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', KeyType.Ed25519) ) ).rejects.toThrowError( 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["accept"] and version 2.0 in pool.' @@ -366,7 +368,7 @@ describe('IndySdkPoolService', () => { }, protocolVersion: 2, }, - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + Key.fromPublicKeyBase58('GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', KeyType.Ed25519) ) ).rejects.toThrowError( 'Unable to satisfy matching TAA with mechanism "accept" and version "1" in pool.\n Found ["decline"] and version 1.0 in pool.' @@ -406,7 +408,7 @@ describe('IndySdkPoolService', () => { }, protocolVersion: 2, }, - 'GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf' + Key.fromPublicKeyBase58('GAb4NUvpBcHVCvtP45vTVa5Bp74vFg3iXzdp1Gbd68Wf', KeyType.Ed25519) ) ).rejects.toThrowError(/Please, specify a transaction author agreement with version and acceptance mechanism/) }) diff --git a/packages/indy-sdk/src/ledger/__tests__/serializeRequestForSignature.test.ts b/packages/indy-sdk/src/ledger/__tests__/serializeRequestForSignature.test.ts new file mode 100644 index 0000000000..7bf0c64a67 --- /dev/null +++ b/packages/indy-sdk/src/ledger/__tests__/serializeRequestForSignature.test.ts @@ -0,0 +1,87 @@ +import { serializeRequestForSignature } from '../serializeRequestForSignature' + +describe('serializeRequestForSignature', () => { + it('Should correctly serialize the json for signature input', () => { + const request = { + name: 'John Doe', + age: 43, + operation: { + dest: 54, + }, + phones: ['1234567', '2345678', { rust: 5, age: 1 }, 3], + } + + const expectedResult = 'age:43|name:John Doe|operation:dest:54|phones:1234567,2345678,age:1|rust:5,3' + + expect(serializeRequestForSignature(request)).toEqual(expectedResult) + }) + + it('Should correctly serialize the json for signature with skipped fields', () => { + const request = { + name: 'John Doe', + age: 43, + operation: { + type: '100', + hash: 'cool hash', + dest: 54, + }, + fees: 'fees1', + signature: 'sign1', + signatures: 'sign-m', + phones: ['1234567', '2345678', { rust: 5, age: 1 }, 3], + } + + const expectedResult = + 'age:43|name:John Doe|operation:dest:54|hash:46aa0c92129b33ee72ee1478d2ae62fa6e756869dedc6c858af3214a6fcf1904|type:100|phones:1234567,2345678,age:1|rust:5,3' + + expect(serializeRequestForSignature(request)).toEqual(expectedResult) + }) + + it('Should correctly serialize the json for signature with raw hash for attrib related types', () => { + const request = { + name: 'John Doe', + age: 43, + operation: { + type: '100', + hash: 'cool hash', + dest: 54, + raw: 'string for hash', + }, + phones: ['1234567', '2345678', { rust: 5, age: 1 }, 3], + } + + const expectedResult = + 'age:43|name:John Doe|operation:dest:54|hash:46aa0c92129b33ee72ee1478d2ae62fa6e756869dedc6c858af3214a6fcf1904|raw:1dcd0759ce38f57049344a6b3c5fc18144fca1724713090c2ceeffa788c02711|type:100|phones:1234567,2345678,age:1|rust:5,3' + + expect(serializeRequestForSignature(request)).toEqual(expectedResult) + }) + + it('Should correctly serialize the json for signature with raw hash for non-attrib related types', () => { + const request = { + name: 'John Doe', + age: 43, + operation: { + type: '101', + hash: 'cool hash', + dest: 54, + raw: 'string for hash', + }, + phones: ['1234567', '2345678', { rust: 5, age: 1 }, 3], + } + + const expectedResult = + 'age:43|name:John Doe|operation:dest:54|hash:cool hash|raw:string for hash|type:101|phones:1234567,2345678,age:1|rust:5,3' + + expect(serializeRequestForSignature(request)).toEqual(expectedResult) + }) + + it('Should correctly serialize the json for signature with null signature', () => { + const request = { + signature: null, + } + + const expectedResult = '' + + expect(serializeRequestForSignature(request)).toEqual(expectedResult) + }) +}) diff --git a/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts b/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts new file mode 100644 index 0000000000..de361806ba --- /dev/null +++ b/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts @@ -0,0 +1,61 @@ +import { Hasher, TypedArrayEncoder } from '../../../core/src' + +const ATTRIB_TYPE = '100' +const GET_ATTR_TYPE = '104' + +/// Generate the normalized form of a ledger transaction request for signing +export function serializeRequestForSignature(v: any): string { + const type = v?.operation?.type + + return _serializeRequestForSignature(v, true, type != undefined ? `${type}` : undefined) +} + +/** + * Serialize an indy ledger request object for signing input. Based on the rust code. Indy SDK requires ledger requests to be signed using + * a did, however in AFJ's the wallet only creates keys, and we create custom did records. This allows us to remove the legacy createDid and + * publicDidSeed properties from the wallet, as we create the request payload ourselves. + * + * @see https://github.com/hyperledger/indy-shared-rs/blob/6af1e939586d1f16341dc03b62970cf28b32d118/indy-utils/src/txn_signature.rs#L10 + */ +function _serializeRequestForSignature(v: any, isTopLevel: boolean, _type?: string): string { + const vType = typeof v + + if (vType === 'boolean') return v ? 'True' : 'False' + if (vType === 'number') return v.toString() + if (vType === 'string') return v + + if (vType === 'object') { + if (Array.isArray(v)) { + return v.map((element) => _serializeRequestForSignature(element, false, _type)).join(',') + } + + let result = '' + let inMiddle = false + + for (const vKey of Object.keys(v).sort()) { + // Skip signature field at top level as in python code + if (isTopLevel && (vKey == 'signature' || vKey == 'fees' || vKey == 'signatures')) { + continue + } + + if (inMiddle) { + result += '|' + } + + let value = v[vKey] + if ((_type == ATTRIB_TYPE || _type == GET_ATTR_TYPE) && (vKey == 'raw' || vKey == 'hash' || vKey == 'enc')) { + // do it only for attribute related request + if (typeof value !== 'string') throw new Error('Value must be a string for hash') + const hash = Hasher.hash(TypedArrayEncoder.fromString(value), 'sha2-256') + value = Buffer.from(hash).toString('hex') + } + + result = `${result}${vKey}:${_serializeRequestForSignature(value, false, _type)}` + inMiddle = true + } + + return result + } + + return '' +} diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index d65b95ada2..72463c7b66 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -1,44 +1,44 @@ import type { + Buffer, EncryptedMessage, KeyDerivationMethod, + KeyPair, + UnpackedMessageContext, + Wallet, WalletConfig, - Buffer, + WalletConfigRekey, WalletCreateKeyOptions, - DidConfig, - DidInfo, + WalletExportImportConfig, WalletSignOptions, - UnpackedMessageContext, WalletVerifyOptions, - Wallet, - KeyPair, - WalletExportImportConfig, - WalletConfigRekey, } from '@aries-framework/core' -import type { WalletStorageConfig, WalletConfig as IndySdkWalletConfig, OpenWalletCredentials } from 'indy-sdk' - -const isError = (error: unknown): error is Error => error instanceof Error +import type { OpenWalletCredentials, WalletConfig as IndySdkWalletConfig, WalletStorageConfig } from 'indy-sdk' +// eslint-disable-next-line import/order import { AriesFrameworkError, - RecordDuplicateError, - RecordNotFoundError, - Logger, - JsonEncoder, - WalletDuplicateError, - WalletError, - WalletNotFoundError, - WalletInvalidKeyError, InjectionSymbols, - KeyType, + isValidPrivateKey, + isValidSeed, + JsonEncoder, Key, + KeyType, + Logger, + RecordNotFoundError, SigningProviderRegistry, TypedArrayEncoder, - isValidSeed, - isValidPrivateKey, + WalletDuplicateError, + WalletError, + WalletInvalidKeyError, + WalletKeyExistsError, + WalletNotFoundError, } from '@aries-framework/core' + +const isError = (error: unknown): error is Error => error instanceof Error + import { inject, injectable } from 'tsyringe' -import { isIndyError, IndySdkError } from '../error' +import { IndySdkError, isIndyError } from '../error' import { IndySdk, IndySdkSymbol } from '../types' @injectable() @@ -48,7 +48,6 @@ export class IndySdkWallet implements Wallet { private logger: Logger private signingKeyProviderRegistry: SigningProviderRegistry - private publicDidInfo: DidInfo | undefined private indySdk: IndySdk public constructor( @@ -69,10 +68,6 @@ export class IndySdkWallet implements Wallet { return this.walletHandle !== undefined } - public get publicDid() { - return this.publicDidInfo - } - public get handle() { if (!this.walletHandle) { throw new AriesFrameworkError( @@ -355,7 +350,6 @@ export class IndySdkWallet implements Wallet { try { await this.indySdk.closeWallet(this.walletHandle) this.walletHandle = undefined - this.publicDidInfo = undefined } catch (error) { if (isIndyError(error, 'WalletInvalidHandle')) { const errorMessage = `Error closing wallet: wallet already closed` @@ -379,40 +373,11 @@ export class IndySdkWallet implements Wallet { } } - public async initPublicDid(didConfig: DidConfig) { - const { did, verkey } = await this.createDid(didConfig) - this.publicDidInfo = { - did, - verkey, - } - } - - private async createDid(didConfig?: DidConfig): Promise { - try { - const [did, verkey] = await this.indySdk.createAndStoreMyDid(this.handle, didConfig || {}) - - return { did, verkey } - } catch (error) { - if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) - } - throw new WalletError('Error creating Did', { cause: error }) - } - } - /** * Create a key with an optional private key and keyType. * The keypair is also automatically stored in the wallet afterwards * * Bls12381g1g2 and X25519 are not supported. - * - * @param privateKey Buffer Private key (formerly called 'seed') - * @param keyType KeyType the type of key that should be created - * - * @returns a Key instance with a publicKeyBase58 - * - * @throws {WalletError} When an unsupported keytype is requested - * @throws {WalletError} When the key could not be created */ public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { @@ -431,17 +396,28 @@ export class IndySdkWallet implements Wallet { // Ed25519 is supported natively in Indy wallet if (keyType === KeyType.Ed25519) { if (seed) { - throw new AriesFrameworkError( + throw new WalletError( 'IndySdkWallet does not support seed. You may rather want to specify a private key for deterministic ed25519 key generation' ) } - const verkey = await this.indySdk.createKey(this.handle, { - seed: privateKey?.toString(), - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - crypto_type: 'ed25519', - }) - return Key.fromPublicKeyBase58(verkey, keyType) + try { + const verkey = await this.indySdk.createKey(this.handle, { + seed: privateKey?.toString(), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + crypto_type: 'ed25519', + }) + + return Key.fromPublicKeyBase58(verkey, keyType) + } catch (error) { + // Handle case where key already exists + if (isIndyError(error, 'WalletItemAlreadyExists')) { + throw new WalletKeyExistsError('Key already exists') + } + + // Otherwise re-throw error + throw error + } } // Check if there is a signing key provider for the specified key type. @@ -453,9 +429,15 @@ export class IndySdkWallet implements Wallet { return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) } } catch (error) { + // If already instance of `WalletError`, re-throw + if (error instanceof WalletError) throw error + if (!isError(error)) { - throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) + throw new AriesFrameworkError(`Attempted to throw error, but it was not of type Error: ${error}`, { + cause: error, + }) } + throw new WalletError(`Error creating key with key type '${keyType}': ${error.message}`, { cause: error }) } @@ -630,7 +612,7 @@ export class IndySdkWallet implements Wallet { ) } catch (error) { if (isIndyError(error, 'WalletItemAlreadyExists')) { - throw new RecordDuplicateError(`Record already exists`, { recordType: 'KeyPairRecord' }) + throw new WalletKeyExistsError('Key already exists') } throw isIndyError(error) ? new IndySdkError(error) : error } diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts index dff7241b85..1aa3120681 100644 --- a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -1,8 +1,9 @@ -import type { WalletConfig } from '@aries-framework/core' +import type { SigningProvider, WalletConfig } from '@aries-framework/core' import { + Key, + WalletKeyExistsError, KeyType, - WalletError, SigningProviderRegistry, TypedArrayEncoder, KeyDerivationMethod, @@ -20,6 +21,11 @@ const walletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } +const signingProvider = { + keyType: KeyType.X25519, + createKeyPair: () => Promise.resolve({ keyType: KeyType.X25519, privateKeyBase58: 'b', publicKeyBase58: 'a' }), +} satisfies Partial + describe('IndySdkWallet', () => { let indySdkWallet: IndySdkWallet @@ -27,7 +33,11 @@ describe('IndySdkWallet', () => { const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { - indySdkWallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([])) + indySdkWallet = new IndySdkWallet( + indySdk, + testLogger, + new SigningProviderRegistry([signingProvider as unknown as SigningProvider]) + ) await indySdkWallet.createAndOpen(walletConfig) }) @@ -35,27 +45,10 @@ describe('IndySdkWallet', () => { await indySdkWallet.delete() }) - test('Get the public DID', async () => { - await indySdkWallet.initPublicDid({ seed: '000000000000000000000000Trustee9' }) - expect(indySdkWallet.publicDid).toMatchObject({ - did: expect.any(String), - verkey: expect.any(String), - }) - }) - test('Get the wallet handle', () => { expect(indySdkWallet.handle).toEqual(expect.any(Number)) }) - test('Initializes a public did', async () => { - await indySdkWallet.initPublicDid({ seed: '00000000000000000000000Forward01' }) - - expect(indySdkWallet.publicDid).toEqual({ - did: 'DtWRdd6C5dN5vpcN6XRAvu', - verkey: '82RBSn3heLgXzZd74UsMC8Q8YRfEEhQoAM7LUqE6bevJ', - }) - }) - test('Generate Nonce', async () => { await expect(indySdkWallet.generateNonce()).resolves.toEqual(expect.any(String)) }) @@ -71,12 +64,29 @@ describe('IndySdkWallet', () => { }) }) - test('Fail to create ed25519 keypair from seed', async () => { - await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError(WalletError) + test('throws WalletKeyExistsError when a key already exists', async () => { + const privateKey = TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b68') + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).resolves.toEqual(expect.any(Key)) + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError( + WalletKeyExistsError + ) + + // This should result in the signign provider being called twice, resulting in the record + // being stored twice + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.X25519 })).resolves.toEqual(expect.any(Key)) + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.X25519 })).rejects.toThrowError( + WalletKeyExistsError + ) + }) + + test('Fail to create ed25519 keypair from invalid private key', async () => { + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError( + /Invalid private key provided/ + ) }) test('Fail to create x25519 keypair', async () => { - await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) + await expect(indySdkWallet.createKey({ keyType: KeyType.Bls12381g1 })).rejects.toThrowError(/Unsupported key type/) }) test('Create a signature with a ed25519 keypair', async () => { diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index 186d1d9a15..046767b8cc 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -1,19 +1,22 @@ -import { Agent } from '@aries-framework/core' - -import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' -import { IndySdkModule } from '../src' +import { Agent, TypedArrayEncoder } from '@aries-framework/core' + +import { + agentDependencies, + getAgentConfig, + importExistingIndyDidFromPrivateKey, + publicDidSeed, +} from '../../core/tests/helpers' import { IndySdkAnonCredsRegistry } from '../src/anoncreds/services/IndySdkAnonCredsRegistry' -import { getIndySdkModuleConfig } from './setupIndySdkModule' +import { getIndySdkModules } from './setupIndySdkModule' const agentConfig = getAgentConfig('IndySdkAnonCredsRegistry') +const indySdkModules = getIndySdkModules() const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, - modules: { - indySdk: new IndySdkModule(getIndySdkModuleConfig()), - }, + modules: indySdkModules, }) const indySdkAnonCredsRegistry = new IndySdkAnonCredsRegistry() @@ -21,6 +24,9 @@ const indySdkAnonCredsRegistry = new IndySdkAnonCredsRegistry() describe('IndySdkAnonCredsRegistry', () => { beforeAll(async () => { await agent.initialize() + + // We need to import the endorser did/key into the wallet + await importExistingIndyDidFromPrivateKey(agent, TypedArrayEncoder.fromString(publicDidSeed)) }) afterAll(async () => { diff --git a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts b/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts index eef3b5c8dd..4518010326 100644 --- a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts +++ b/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts @@ -3,7 +3,7 @@ import type { IndySdkSovDidCreateOptions } from '../src/dids/IndySdkSovDidRegist import { Agent, TypedArrayEncoder, convertPublicKeyToX25519, JsonTransformer } from '@aries-framework/core' import { generateKeyPairFromSeed } from '@stablelib/ed25519' -import { getAgentOptions } from '../../core/tests/helpers' +import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests' import { indyDidFromPublicKeyBase58 } from '../src/utils/did' import { getIndySdkModules } from './setupIndySdkModule' @@ -24,6 +24,12 @@ describe('dids', () => { }) it('should create a did:sov did', async () => { + // Add existing endorser did to the wallet + const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + agent, + TypedArrayEncoder.fromString(publicDidSeed) + ) + // Generate a seed and the indy did. This allows us to create a new did every time // but still check if the created output document is as expected. const privateKey = TypedArrayEncoder.fromString( @@ -40,7 +46,7 @@ describe('dids', () => { const did = await agent.dids.create({ method: 'sov', options: { - submitterDid: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', + submitterVerificationMethod: `did:sov:${unqualifiedSubmitterDid}#key-1`, alias: 'Alias', endpoints: { endpoint: 'https://example.com/endpoint', diff --git a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts index dfa3281214..b1847a3f6d 100644 --- a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts +++ b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts @@ -1,8 +1,8 @@ import type { IndySdkSovDidCreateOptions } from '../src/dids/IndySdkSovDidRegistrar' -import { Agent, AriesFrameworkError, JsonTransformer } from '@aries-framework/core' +import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' -import { getAgentOptions } from '../../core/tests/helpers' +import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests/helpers' import { getIndySdkModules } from './setupIndySdkModule' @@ -19,10 +19,16 @@ describe('Indy SDK Sov DID resolver', () => { }) it('should resolve a did:sov did', async () => { + // Add existing endorser did to the wallet + const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + agent, + TypedArrayEncoder.fromString(publicDidSeed) + ) + const createResult = await agent.dids.create({ method: 'sov', options: { - submitterDid: 'did:sov:TL1EaPFCZ8Si5aUrqScBDt', + submitterVerificationMethod: `did:sov:${unqualifiedSubmitterDid}#key-1`, alias: 'Alias', role: 'TRUSTEE', }, diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index dee2fbc4d0..83f3323c54 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -1,7 +1,12 @@ -import { Agent, DidsModule, Key, KeyType } from '@aries-framework/core' +import { Agent, DidsModule, Key, KeyType, TypedArrayEncoder } from '@aries-framework/core' import { RevocationRegistryDefinitionRequest, RevocationRegistryEntryRequest } from '@hyperledger/indy-vdr-shared' -import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' +import { + agentDependencies, + getAgentConfig, + importExistingIndyDidFromPrivateKey, + publicDidSeed, +} from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrSovDidResolver } from '../src' @@ -38,6 +43,8 @@ agent.dependencyManager.registerInstance(IndyVdrPoolService, indyVdrPoolService) describe('IndyVdrAnonCredsRegistry', () => { beforeAll(async () => { await agent.initialize() + + await importExistingIndyDidFromPrivateKey(agent, TypedArrayEncoder.fromString(publicDidSeed)) }) afterAll(async () => { diff --git a/tests/e2e-test.ts b/tests/e2e-test.ts index 34960d8d8f..ffacd8be0a 100644 --- a/tests/e2e-test.ts +++ b/tests/e2e-test.ts @@ -56,8 +56,6 @@ export async function e2eTest({ // Issue credential from sender to recipient const { credentialDefinition } = await prepareForAnonCredsIssuance(senderAgent, { attributeNames: ['name', 'age', 'dateOfBirth'], - // TODO: update to dynamic created did - issuerId: senderAgent.publicDid?.did as string, }) const { holderCredentialExchangeRecord, issuerCredentialExchangeRecord } = await issueLegacyAnonCredsCredential({ issuerAgent: senderAgent, From 254f661c2e925b62dd07c3565099f9e226bd2b41 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 25 Feb 2023 11:49:36 -0300 Subject: [PATCH 067/139] fix(indy-sdk): import from core (#1346) Signed-off-by: Ariel Gentile --- .eslintrc.js | 10 ++++ demo/src/BaseAgent.ts | 4 +- demo/src/Faber.ts | 2 +- package.json | 1 + .../ledger/serializeRequestForSignature.ts | 2 +- yarn.lock | 56 +++++++++++++++++++ 6 files changed, 71 insertions(+), 4 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index e45d4d2cad..8db1c9bd25 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,8 +5,10 @@ module.exports = { 'plugin:import/recommended', 'plugin:import/typescript', 'plugin:@typescript-eslint/recommended', + 'plugin:workspaces/recommended', 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. ], + plugins: ['workspaces'], parserOptions: { tsconfigRootDir: __dirname, project: ['./tsconfig.eslint.json'], @@ -122,5 +124,13 @@ module.exports = { ], }, }, + { + files: ['*.test.ts', '**/__tests__/**', '**/tests/**', '**/tests/**'], + rules: { + 'workspaces/no-relative-imports': 'off', + 'workspaces/require-dependency': 'off', + 'workspaces/no-absolute-imports': 'off', + }, + }, ], } diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index 2f1258ab90..f06d0016fe 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -1,6 +1,6 @@ -import type { IndySdkPoolConfig } from '../../packages/indy-sdk/src/ledger' -import type { IndyVdrPoolConfig } from '../../packages/indy-vdr/src/pool' import type { InitConfig } from '@aries-framework/core' +import type { IndySdkPoolConfig } from '@aries-framework/indy-sdk' +import type { IndyVdrPoolConfig } from '@aries-framework/indy-vdr' import { AnonCredsModule, diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index f32d50bed8..fdaca44b66 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -1,4 +1,4 @@ -import type { RegisterCredentialDefinitionReturnStateFinished } from '../../packages/anoncreds/src' +import type { RegisterCredentialDefinitionReturnStateFinished } from '@aries-framework/anoncreds' import type { ConnectionRecord, ConnectionStateChangedEvent } from '@aries-framework/core' import type BottomBar from 'inquirer/lib/ui/bottom-bar' diff --git a/package.json b/package.json index 8f5fee182f..0269aa22bf 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-import": "^2.23.4", "eslint-plugin-prettier": "^3.4.0", + "eslint-plugin-workspaces": "^0.7.0", "express": "^4.17.1", "indy-sdk": "^1.16.0-dev-1655", "jest": "^27.0.4", diff --git a/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts b/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts index de361806ba..630dcbab2b 100644 --- a/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts +++ b/packages/indy-sdk/src/ledger/serializeRequestForSignature.ts @@ -1,4 +1,4 @@ -import { Hasher, TypedArrayEncoder } from '../../../core/src' +import { Hasher, TypedArrayEncoder } from '@aries-framework/core' const ATTRIB_TYPE = '100' const GET_ATTR_TYPE = '104' diff --git a/yarn.lock b/yarn.lock index 1c48eeb5a7..a96c0c26f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1116,6 +1116,14 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@joshuajaco/get-monorepo-packages@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@joshuajaco/get-monorepo-packages/-/get-monorepo-packages-1.2.1.tgz#070bdc4268f5e14d2dd593b02f32bc4c5601d06d" + integrity sha512-3I32bp/UB4UmLqEj/yrBEWTYuCzojn8nR34PZ9Jwb2OeHV95l4Kw0ax1xnnA4yYWU1E1krM2RIWghP3U725dGQ== + dependencies: + globby "^7.1.1" + load-json-file "^4.0.0" + "@jridgewell/gen-mapping@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" @@ -3175,11 +3183,23 @@ array-reduce@~0.0.0: resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" integrity sha512-8jR+StqaC636u7h3ye1co3lQRefgVVUQUhuAmRbDqIMeR2yuXzRvkCNQiQ5J/wbREmoBLNtp13dhaaVpZQDRUw== +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== + dependencies: + array-uniq "^1.0.1" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== + array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -4658,6 +4678,13 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +dir-glob@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -4993,6 +5020,13 @@ eslint-plugin-prettier@^3.4.0: dependencies: prettier-linter-helpers "^1.0.0" +eslint-plugin-workspaces@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-workspaces/-/eslint-plugin-workspaces-0.7.0.tgz#be97fad9d25ab7430074c526f046b0e5c037633f" + integrity sha512-1+qzAM/iFFJ4MR3IOSY7n6Kw9XW/Fc+eVzWfN9nCcneDHr21rccZWUtGyMesk35bFnOWyp9dmJbYL0v5KYZ14w== + dependencies: + "@joshuajaco/get-monorepo-packages" "^1.2.1" + eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -5889,6 +5923,18 @@ globby@^11.0.2, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +globby@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + integrity sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g== + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -6129,6 +6175,11 @@ ignore-walk@^3.0.1, ignore-walk@^3.0.3: dependencies: minimatch "^3.0.4" +ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -10256,6 +10307,11 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" From 4ab3b54e9db630a6ba022af6becdd7276692afc5 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 27 Feb 2023 13:23:47 +0100 Subject: [PATCH 068/139] refactor!: set default outbound content type to didcomm v1 (#1314) Signed-off-by: Timo Glastra BREAKING CHANGE: Agent default outbound content type has been changed to DIDComm V1. If you want to use former behaviour, you can do it so by manually setting `didcommMimeType` in `Agent`'s init config: ``` const agent = new Agent({ config: { ... didCommMimeType: DidCommMimeType.V0 }, ... }) ``` --- packages/core/src/agent/AgentConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index c654ef3eb0..971bff8d6d 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -62,7 +62,7 @@ export class AgentConfig { } public get didCommMimeType() { - return this.initConfig.didCommMimeType ?? DidCommMimeType.V0 + return this.initConfig.didCommMimeType ?? DidCommMimeType.V1 } /** From 1bda3f0733a472b536059cee8d34e25fb04c9f2d Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 27 Feb 2023 15:35:16 -0300 Subject: [PATCH 069/139] fix(anoncreds-rs): save revocation registry index (#1351) Signed-off-by: Ariel Gentile --- packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index b4c1e02f53..f0516665ee 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -251,6 +251,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schemaName: schema.name, schemaIssuerId: schema.issuerId, schemaVersion: schema.version, + credentialRevocationId: processedCredential.revocationRegistryIndex?.toString(), }) ) From 78ecf1ed959c9daba1c119d03f4596f1db16c57c Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 1 Mar 2023 18:44:26 -0300 Subject: [PATCH 070/139] refactor!: remove Dispatcher.registerMessageHandler (#1354) BREAKING CHANGE: `Dispatcher.registerMessageHandler` has been removed in favour of `MessageHandlerRegistry.registerMessageHandler`. If you want to register message handlers in an extension module, you can use directly `agentContext.dependencyManager.registerMessageHandlers`. Signed-off-by: Ariel Gentile --- packages/action-menu/src/ActionMenuApi.ts | 17 +++++------ packages/core/src/agent/Dispatcher.ts | 8 ------ .../src/agent/__tests__/Dispatcher.test.ts | 8 ++++-- .../basic-messages/BasicMessagesApi.ts | 10 +++---- .../src/modules/connections/ConnectionsApi.ts | 28 +++++++++++-------- .../services/RevocationNotificationService.ts | 14 ++++------ .../RevocationNotificationService.test.ts | 10 +++---- .../protocol/v1/V1DiscoverFeaturesService.ts | 14 +++++----- .../V1DiscoverFeaturesService.test.ts | 10 +++---- .../protocol/v2/V2DiscoverFeaturesService.ts | 14 +++++----- .../V2DiscoverFeaturesService.test.ts | 10 +++---- .../services/DiscoverFeaturesService.ts | 4 --- .../core/src/modules/routing/MediatorApi.ts | 18 ++++++------ .../core/src/modules/routing/RecipientApi.ts | 20 ++++++------- .../pickup/v1/MessagePickupService.ts | 14 ++++------ .../pickup/v2/V2MessagePickupService.ts | 20 ++++++------- .../__tests__/V2MessagePickupService.test.ts | 10 +++---- .../question-answer/src/QuestionAnswerApi.ts | 13 ++++----- samples/extension-module/dummy/DummyApi.ts | 12 +++----- 19 files changed, 116 insertions(+), 138 deletions(-) diff --git a/packages/action-menu/src/ActionMenuApi.ts b/packages/action-menu/src/ActionMenuApi.ts index bb6f3cd4f3..6abe1b3fac 100644 --- a/packages/action-menu/src/ActionMenuApi.ts +++ b/packages/action-menu/src/ActionMenuApi.ts @@ -10,7 +10,6 @@ import { AgentContext, AriesFrameworkError, ConnectionService, - Dispatcher, MessageSender, OutboundMessageContext, injectable, @@ -36,7 +35,6 @@ export class ActionMenuApi { private agentContext: AgentContext public constructor( - dispatcher: Dispatcher, connectionService: ConnectionService, messageSender: MessageSender, actionMenuService: ActionMenuService, @@ -46,7 +44,13 @@ export class ActionMenuApi { this.messageSender = messageSender this.actionMenuService = actionMenuService this.agentContext = agentContext - this.registerMessageHandlers(dispatcher) + + this.agentContext.dependencyManager.registerMessageHandlers([ + new ActionMenuProblemReportHandler(this.actionMenuService), + new MenuMessageHandler(this.actionMenuService), + new MenuRequestMessageHandler(this.actionMenuService), + new PerformMessageHandler(this.actionMenuService), + ]) } /** @@ -160,11 +164,4 @@ export class ActionMenuApi { return actionMenuRecord ? await this.actionMenuService.clearMenu(this.agentContext, { actionMenuRecord }) : null } - - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new ActionMenuProblemReportHandler(this.actionMenuService)) - dispatcher.registerMessageHandler(new MenuMessageHandler(this.actionMenuService)) - dispatcher.registerMessageHandler(new MenuRequestMessageHandler(this.actionMenuService)) - dispatcher.registerMessageHandler(new PerformMessageHandler(this.actionMenuService)) - } } diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 50ff6da42e..709ba04ea0 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -1,6 +1,5 @@ import type { AgentMessage } from './AgentMessage' import type { AgentMessageProcessedEvent } from './Events' -import type { MessageHandler } from './MessageHandler' import type { InboundMessageContext } from './models/InboundMessageContext' import { InjectionSymbols } from '../constants' @@ -35,13 +34,6 @@ class Dispatcher { this.logger = logger } - /** - * @deprecated Use {@link MessageHandlerRegistry.registerMessageHandler} directly - */ - public registerMessageHandler(messageHandler: MessageHandler) { - this.messageHandlerRegistry.registerMessageHandler(messageHandler) - } - public async dispatch(messageContext: InboundMessageContext): Promise { const { agentContext, connection, senderKey, recipientKey, message } = messageContext const messageHandler = this.messageHandlerRegistry.getHandlerForMessageType(message.type) diff --git a/packages/core/src/agent/__tests__/Dispatcher.test.ts b/packages/core/src/agent/__tests__/Dispatcher.test.ts index 3c91825d2d..7bbcb89f95 100644 --- a/packages/core/src/agent/__tests__/Dispatcher.test.ts +++ b/packages/core/src/agent/__tests__/Dispatcher.test.ts @@ -22,17 +22,18 @@ describe('Dispatcher', () => { describe('dispatch()', () => { it('calls the handle method of the handler', async () => { + const messageHandlerRegistry = new MessageHandlerRegistry() const dispatcher = new Dispatcher( new MessageSenderMock(), eventEmitter, - new MessageHandlerRegistry(), + messageHandlerRegistry, agentConfig.logger ) const customProtocolMessage = new CustomProtocolMessage() const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) const mockHandle = jest.fn() - dispatcher.registerMessageHandler({ supportedMessages: [CustomProtocolMessage], handle: mockHandle }) + messageHandlerRegistry.registerMessageHandler({ supportedMessages: [CustomProtocolMessage], handle: mockHandle }) await dispatcher.dispatch(inboundMessageContext) @@ -40,6 +41,7 @@ describe('Dispatcher', () => { }) it('throws an error if no handler for the message could be found', async () => { + const messageHandlerRegistry = new MessageHandlerRegistry() const dispatcher = new Dispatcher( new MessageSenderMock(), eventEmitter, @@ -50,7 +52,7 @@ describe('Dispatcher', () => { const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) const mockHandle = jest.fn() - dispatcher.registerMessageHandler({ supportedMessages: [], handle: mockHandle }) + messageHandlerRegistry.registerMessageHandler({ supportedMessages: [], handle: mockHandle }) await expect(dispatcher.dispatch(inboundMessageContext)).rejects.toThrow( 'No handler for message type "https://didcomm.org/fake-protocol/1.5/message" found' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts index 938f6b8407..ff788e00fe 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts @@ -2,7 +2,7 @@ import type { BasicMessageRecord } from './repository/BasicMessageRecord' import type { Query } from '../../storage/StorageService' import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' +import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { injectable } from '../../plugins' @@ -19,7 +19,7 @@ export class BasicMessagesApi { private agentContext: AgentContext public constructor( - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, basicMessageService: BasicMessageService, messageSender: MessageSender, connectionService: ConnectionService, @@ -29,7 +29,7 @@ export class BasicMessagesApi { this.messageSender = messageSender this.connectionService = connectionService this.agentContext = agentContext - this.registerMessageHandlers(dispatcher) + this.registerMessageHandlers(messageHandlerRegistry) } /** @@ -91,7 +91,7 @@ export class BasicMessagesApi { await this.basicMessageService.deleteById(this.agentContext, basicMessageRecordId) } - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new BasicMessageHandler(this.basicMessageService)) + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new BasicMessageHandler(this.basicMessageService)) } } diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index 9777bc903a..468c02c507 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -5,7 +5,7 @@ import type { Query } from '../../storage/StorageService' import type { OutOfBandRecord } from '../oob/repository' import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' +import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' @@ -55,7 +55,7 @@ export class ConnectionsApi { private agentContext: AgentContext public constructor( - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, didExchangeProtocol: DidExchangeProtocol, connectionService: ConnectionService, outOfBandService: OutOfBandService, @@ -78,7 +78,7 @@ export class ConnectionsApi { this.agentContext = agentContext this.config = connectionsModuleConfig - this.registerMessageHandlers(dispatcher) + this.registerMessageHandlers(messageHandlerRegistry) } public async acceptOutOfBandInvitation( @@ -407,8 +407,8 @@ export class ConnectionsApi { return this.connectionService.findByInvitationDid(this.agentContext, invitationDid) } - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler( + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler( new ConnectionRequestHandler( this.connectionService, this.outOfBandService, @@ -417,14 +417,16 @@ export class ConnectionsApi { this.config ) ) - dispatcher.registerMessageHandler( + messageHandlerRegistry.registerMessageHandler( new ConnectionResponseHandler(this.connectionService, this.outOfBandService, this.didResolverService, this.config) ) - dispatcher.registerMessageHandler(new AckMessageHandler(this.connectionService)) - dispatcher.registerMessageHandler(new TrustPingMessageHandler(this.trustPingService, this.connectionService)) - dispatcher.registerMessageHandler(new TrustPingResponseMessageHandler(this.trustPingService)) + messageHandlerRegistry.registerMessageHandler(new AckMessageHandler(this.connectionService)) + messageHandlerRegistry.registerMessageHandler( + new TrustPingMessageHandler(this.trustPingService, this.connectionService) + ) + messageHandlerRegistry.registerMessageHandler(new TrustPingResponseMessageHandler(this.trustPingService)) - dispatcher.registerMessageHandler( + messageHandlerRegistry.registerMessageHandler( new DidExchangeRequestHandler( this.didExchangeProtocol, this.outOfBandService, @@ -434,7 +436,7 @@ export class ConnectionsApi { ) ) - dispatcher.registerMessageHandler( + messageHandlerRegistry.registerMessageHandler( new DidExchangeResponseHandler( this.didExchangeProtocol, this.outOfBandService, @@ -443,6 +445,8 @@ export class ConnectionsApi { this.config ) ) - dispatcher.registerMessageHandler(new DidExchangeCompleteHandler(this.didExchangeProtocol, this.outOfBandService)) + messageHandlerRegistry.registerMessageHandler( + new DidExchangeCompleteHandler(this.didExchangeProtocol, this.outOfBandService) + ) } } diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts index e2b9d6e1f9..6a641d9457 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/RevocationNotificationService.ts @@ -5,8 +5,8 @@ import type { RevocationNotificationReceivedEvent } from '../../../CredentialEve import type { V1RevocationNotificationMessage } from '../messages/V1RevocationNotificationMessage' import type { V2RevocationNotificationMessage } from '../messages/V2RevocationNotificationMessage' -import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' +import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' import { InjectionSymbols } from '../../../../../constants' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { Logger } from '../../../../../logger' @@ -22,21 +22,19 @@ import { v1ThreadRegex, v2IndyRevocationFormat, v2IndyRevocationIdentifierRegex export class RevocationNotificationService { private credentialRepository: CredentialRepository private eventEmitter: EventEmitter - private dispatcher: Dispatcher private logger: Logger public constructor( credentialRepository: CredentialRepository, eventEmitter: EventEmitter, - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, @inject(InjectionSymbols.Logger) logger: Logger ) { this.credentialRepository = credentialRepository this.eventEmitter = eventEmitter - this.dispatcher = dispatcher this.logger = logger - this.registerMessageHandlers() + this.registerMessageHandlers(messageHandlerRegistry) } private async processRevocationNotification( @@ -147,8 +145,8 @@ export class RevocationNotificationService { } } - private registerMessageHandlers() { - this.dispatcher.registerMessageHandler(new V1RevocationNotificationHandler(this)) - this.dispatcher.registerMessageHandler(new V2RevocationNotificationHandler(this)) + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new V1RevocationNotificationHandler(this)) + messageHandlerRegistry.registerMessageHandler(new V2RevocationNotificationHandler(this)) } } diff --git a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts index e834ca5585..90d07d81ae 100644 --- a/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts +++ b/packages/core/src/modules/credentials/protocol/revocation-notification/services/__tests__/RevocationNotificationService.test.ts @@ -6,8 +6,8 @@ import { Subject } from 'rxjs' import { CredentialExchangeRecord, CredentialState, InboundMessageContext } from '../../../../../..' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../../tests/helpers' -import { Dispatcher } from '../../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../../agent/EventEmitter' +import { MessageHandlerRegistry } from '../../../../../../agent/MessageHandlerRegistry' import { DidExchangeState } from '../../../../../connections' import { CredentialEventTypes } from '../../../../CredentialEvents' import { CredentialRepository } from '../../../../repository/CredentialRepository' @@ -18,9 +18,9 @@ jest.mock('../../../../repository/CredentialRepository') const CredentialRepositoryMock = CredentialRepository as jest.Mock const credentialRepository = new CredentialRepositoryMock() -jest.mock('../../../../../../agent/Dispatcher') -const DispatcherMock = Dispatcher as jest.Mock -const dispatcher = new DispatcherMock() +jest.mock('../../../../../../agent/MessageHandlerRegistry') +const MessageHandlerRegistryMock = MessageHandlerRegistry as jest.Mock +const messageHandlerRegistry = new MessageHandlerRegistryMock() const connection = getMockConnection({ state: DidExchangeState.Completed, @@ -40,7 +40,7 @@ describe('RevocationNotificationService', () => { revocationNotificationService = new RevocationNotificationService( credentialRepository, eventEmitter, - dispatcher, + messageHandlerRegistry, agentConfig.logger ) }) diff --git a/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts index 39a694ccd1..172381d316 100644 --- a/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/protocol/v1/V1DiscoverFeaturesService.ts @@ -10,9 +10,9 @@ import type { DiscoverFeaturesProtocolMsgReturnType, } from '../../DiscoverFeaturesServiceOptions' -import { Dispatcher } from '../../../../agent/Dispatcher' import { EventEmitter } from '../../../../agent/EventEmitter' import { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import { MessageHandlerRegistry } from '../../../../agent/MessageHandlerRegistry' import { Protocol } from '../../../../agent/models' import { InjectionSymbols } from '../../../../constants' import { AriesFrameworkError } from '../../../../error' @@ -30,13 +30,13 @@ export class V1DiscoverFeaturesService extends DiscoverFeaturesService { public constructor( featureRegistry: FeatureRegistry, eventEmitter: EventEmitter, - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, @inject(InjectionSymbols.Logger) logger: Logger, discoverFeaturesConfig: DiscoverFeaturesModuleConfig ) { - super(featureRegistry, eventEmitter, dispatcher, logger, discoverFeaturesConfig) + super(featureRegistry, eventEmitter, logger, discoverFeaturesConfig) - this.registerMessageHandlers(dispatcher) + this.registerMessageHandlers(messageHandlerRegistry) } /** @@ -44,9 +44,9 @@ export class V1DiscoverFeaturesService extends DiscoverFeaturesService { */ public readonly version = 'v1' - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new V1DiscloseMessageHandler(this)) - dispatcher.registerMessageHandler(new V1QueryMessageHandler(this)) + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new V1DiscloseMessageHandler(this)) + messageHandlerRegistry.registerMessageHandler(new V1QueryMessageHandler(this)) } public async createQuery( diff --git a/packages/core/src/modules/discover-features/protocol/v1/__tests__/V1DiscoverFeaturesService.test.ts b/packages/core/src/modules/discover-features/protocol/v1/__tests__/V1DiscoverFeaturesService.test.ts index 133a2b3442..03db2cf74a 100644 --- a/packages/core/src/modules/discover-features/protocol/v1/__tests__/V1DiscoverFeaturesService.test.ts +++ b/packages/core/src/modules/discover-features/protocol/v1/__tests__/V1DiscoverFeaturesService.test.ts @@ -7,9 +7,9 @@ import type { DiscoverFeaturesProtocolMsgReturnType } from '../../../DiscoverFea import { Subject } from 'rxjs' import { agentDependencies, getAgentContext, getMockConnection } from '../../../../../../tests/helpers' -import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' import { FeatureRegistry } from '../../../../../agent/FeatureRegistry' +import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' import { Protocol } from '../../../../../agent/models' import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import { ConsoleLogger } from '../../../../../logger/ConsoleLogger' @@ -19,8 +19,8 @@ import { DiscoverFeaturesModuleConfig } from '../../../DiscoverFeaturesModuleCon import { V1DiscoverFeaturesService } from '../V1DiscoverFeaturesService' import { V1DiscloseMessage, V1QueryMessage } from '../messages' -jest.mock('../../../../../agent/Dispatcher') -const DispatcherMock = Dispatcher as jest.Mock +jest.mock('../../../../../agent/MessageHandlerRegistry') +const MessageHandlerRegistryMock = MessageHandlerRegistry as jest.Mock const eventEmitter = new EventEmitter(agentDependencies, new Subject()) const featureRegistry = new FeatureRegistry() featureRegistry.register(new Protocol({ id: 'https://didcomm.org/connections/1.0' })) @@ -36,7 +36,7 @@ describe('V1DiscoverFeaturesService - auto accept queries', () => { const discoverFeaturesService = new V1DiscoverFeaturesService( featureRegistry, eventEmitter, - new DispatcherMock(), + new MessageHandlerRegistryMock(), new LoggerMock(), discoverFeaturesModuleConfig ) @@ -239,7 +239,7 @@ describe('V1DiscoverFeaturesService - auto accept disabled', () => { const discoverFeaturesService = new V1DiscoverFeaturesService( featureRegistry, eventEmitter, - new DispatcherMock(), + new MessageHandlerRegistry(), new LoggerMock(), discoverFeaturesModuleConfig ) diff --git a/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts index 2e007ae142..0196a351c4 100644 --- a/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts @@ -9,9 +9,9 @@ import type { CreateDisclosureOptions, } from '../../DiscoverFeaturesServiceOptions' -import { Dispatcher } from '../../../../agent/Dispatcher' import { EventEmitter } from '../../../../agent/EventEmitter' import { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import { MessageHandlerRegistry } from '../../../../agent/MessageHandlerRegistry' import { InjectionSymbols } from '../../../../constants' import { Logger } from '../../../../logger' import { inject, injectable } from '../../../../plugins' @@ -27,12 +27,12 @@ export class V2DiscoverFeaturesService extends DiscoverFeaturesService { public constructor( featureRegistry: FeatureRegistry, eventEmitter: EventEmitter, - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, @inject(InjectionSymbols.Logger) logger: Logger, discoverFeaturesModuleConfig: DiscoverFeaturesModuleConfig ) { - super(featureRegistry, eventEmitter, dispatcher, logger, discoverFeaturesModuleConfig) - this.registerMessageHandlers(dispatcher) + super(featureRegistry, eventEmitter, logger, discoverFeaturesModuleConfig) + this.registerMessageHandlers(messageHandlerRegistry) } /** @@ -40,9 +40,9 @@ export class V2DiscoverFeaturesService extends DiscoverFeaturesService { */ public readonly version = 'v2' - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new V2DisclosuresMessageHandler(this)) - dispatcher.registerMessageHandler(new V2QueriesMessageHandler(this)) + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new V2DisclosuresMessageHandler(this)) + messageHandlerRegistry.registerMessageHandler(new V2QueriesMessageHandler(this)) } public async createQuery( diff --git a/packages/core/src/modules/discover-features/protocol/v2/__tests__/V2DiscoverFeaturesService.test.ts b/packages/core/src/modules/discover-features/protocol/v2/__tests__/V2DiscoverFeaturesService.test.ts index 9669c9a63f..897fe5d1b4 100644 --- a/packages/core/src/modules/discover-features/protocol/v2/__tests__/V2DiscoverFeaturesService.test.ts +++ b/packages/core/src/modules/discover-features/protocol/v2/__tests__/V2DiscoverFeaturesService.test.ts @@ -7,9 +7,9 @@ import type { DiscoverFeaturesProtocolMsgReturnType } from '../../../DiscoverFea import { Subject } from 'rxjs' import { agentDependencies, getAgentContext, getMockConnection } from '../../../../../../tests/helpers' -import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' import { FeatureRegistry } from '../../../../../agent/FeatureRegistry' +import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' import { InboundMessageContext, Protocol, GoalCode } from '../../../../../agent/models' import { ConsoleLogger } from '../../../../../logger/ConsoleLogger' import { DidExchangeState } from '../../../../connections' @@ -18,8 +18,8 @@ import { DiscoverFeaturesModuleConfig } from '../../../DiscoverFeaturesModuleCon import { V2DiscoverFeaturesService } from '../V2DiscoverFeaturesService' import { V2DisclosuresMessage, V2QueriesMessage } from '../messages' -jest.mock('../../../../../agent/Dispatcher') -const DispatcherMock = Dispatcher as jest.Mock +jest.mock('../../../../../agent/MessageHandlerRegistry') +const MessageHandlerRegistryMock = MessageHandlerRegistry as jest.Mock const eventEmitter = new EventEmitter(agentDependencies, new Subject()) const featureRegistry = new FeatureRegistry() featureRegistry.register(new Protocol({ id: 'https://didcomm.org/connections/1.0' })) @@ -38,7 +38,7 @@ describe('V2DiscoverFeaturesService - auto accept queries', () => { const discoverFeaturesService = new V2DiscoverFeaturesService( featureRegistry, eventEmitter, - new DispatcherMock(), + new MessageHandlerRegistryMock(), new LoggerMock(), discoverFeaturesModuleConfig ) @@ -250,7 +250,7 @@ describe('V2DiscoverFeaturesService - auto accept disabled', () => { const discoverFeaturesService = new V2DiscoverFeaturesService( featureRegistry, eventEmitter, - new DispatcherMock(), + new MessageHandlerRegistryMock(), new LoggerMock(), discoverFeaturesModuleConfig ) diff --git a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts index fb5cd56a1e..c9e532b4c7 100644 --- a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts @@ -1,5 +1,4 @@ import type { AgentMessage } from '../../../agent/AgentMessage' -import type { Dispatcher } from '../../../agent/Dispatcher' import type { EventEmitter } from '../../../agent/EventEmitter' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' @@ -14,20 +13,17 @@ import type { export abstract class DiscoverFeaturesService { protected featureRegistry: FeatureRegistry protected eventEmitter: EventEmitter - protected dispatcher: Dispatcher protected logger: Logger protected discoverFeaturesModuleConfig: DiscoverFeaturesModuleConfig public constructor( featureRegistry: FeatureRegistry, eventEmitter: EventEmitter, - dispatcher: Dispatcher, logger: Logger, discoverFeaturesModuleConfig: DiscoverFeaturesModuleConfig ) { this.featureRegistry = featureRegistry this.eventEmitter = eventEmitter - this.dispatcher = dispatcher this.logger = logger this.discoverFeaturesModuleConfig = discoverFeaturesModuleConfig } diff --git a/packages/core/src/modules/routing/MediatorApi.ts b/packages/core/src/modules/routing/MediatorApi.ts index 78a1cbd849..af477e6052 100644 --- a/packages/core/src/modules/routing/MediatorApi.ts +++ b/packages/core/src/modules/routing/MediatorApi.ts @@ -2,8 +2,8 @@ import type { MediationRecord } from './repository' import type { EncryptedMessage } from '../../types' import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' +import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { injectable } from '../../plugins' @@ -28,7 +28,7 @@ export class MediatorApi { private connectionService: ConnectionService public constructor( - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, mediationService: MediatorService, messagePickupService: MessagePickupService, // Only imported so it is injected and handlers are registered @@ -46,7 +46,7 @@ export class MediatorApi { this.connectionService = connectionService this.agentContext = agentContext this.config = config - this.registerMessageHandlers(dispatcher) + this.registerMessageHandlers(messageHandlerRegistry) } public async initialize() { @@ -85,13 +85,13 @@ export class MediatorApi { return this.messagePickupService.queueMessage(connectionId, message) } - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new KeylistUpdateHandler(this.mediatorService)) - dispatcher.registerMessageHandler( + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new KeylistUpdateHandler(this.mediatorService)) + messageHandlerRegistry.registerMessageHandler( new ForwardHandler(this.mediatorService, this.connectionService, this.messageSender) ) - dispatcher.registerMessageHandler(new BatchPickupHandler(this.messagePickupService)) - dispatcher.registerMessageHandler(new BatchHandler(this.eventEmitter)) - dispatcher.registerMessageHandler(new MediationRequestHandler(this.mediatorService, this.config)) + messageHandlerRegistry.registerMessageHandler(new BatchPickupHandler(this.messagePickupService)) + messageHandlerRegistry.registerMessageHandler(new BatchHandler(this.eventEmitter)) + messageHandlerRegistry.registerMessageHandler(new MediationRequestHandler(this.mediatorService, this.config)) } } diff --git a/packages/core/src/modules/routing/RecipientApi.ts b/packages/core/src/modules/routing/RecipientApi.ts index e74ee664ca..65216881b6 100644 --- a/packages/core/src/modules/routing/RecipientApi.ts +++ b/packages/core/src/modules/routing/RecipientApi.ts @@ -8,9 +8,9 @@ import { firstValueFrom, interval, merge, ReplaySubject, Subject, timer } from ' import { delayWhen, filter, first, takeUntil, tap, throttleTime, timeout } from 'rxjs/operators' import { AgentContext } from '../../agent' -import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' import { filterContextCorrelationId } from '../../agent/Events' +import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' @@ -58,7 +58,7 @@ export class RecipientApi { private readonly stopMessagePickup$ = new Subject() public constructor( - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, mediationRecipientService: MediationRecipientService, connectionService: ConnectionService, dids: DidsApi, @@ -84,7 +84,7 @@ export class RecipientApi { this.agentContext = agentContext this.stop$ = stop$ this.config = recipientModuleConfig - this.registerMessageHandlers(dispatcher) + this.registerMessageHandlers(messageHandlerRegistry) } public async initialize() { @@ -484,12 +484,12 @@ export class RecipientApi { } // Register handlers for the several messages for the mediator. - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) - dispatcher.registerMessageHandler(new MediationGrantHandler(this.mediationRecipientService)) - dispatcher.registerMessageHandler(new MediationDenyHandler(this.mediationRecipientService)) - dispatcher.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) - dispatcher.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) - //dispatcher.registerMessageHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this + private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) + messageHandlerRegistry.registerMessageHandler(new MediationGrantHandler(this.mediationRecipientService)) + messageHandlerRegistry.registerMessageHandler(new MediationDenyHandler(this.mediationRecipientService)) + messageHandlerRegistry.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) + messageHandlerRegistry.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) + //messageHandlerRegistry.registerMessageHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this } } diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts index 9211359eb0..aa97fc51a4 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts @@ -2,8 +2,8 @@ import type { BatchPickupMessage } from './messages' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { EncryptedMessage } from '../../../../../types' -import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' +import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' import { OutboundMessageContext } from '../../../../../agent/models' import { InjectionSymbols } from '../../../../../constants' import { inject, injectable } from '../../../../../plugins' @@ -15,19 +15,17 @@ import { BatchMessage, BatchMessageMessage } from './messages' @injectable() export class MessagePickupService { private messageRepository: MessageRepository - private dispatcher: Dispatcher private eventEmitter: EventEmitter public constructor( @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, eventEmitter: EventEmitter ) { this.messageRepository = messageRepository - this.dispatcher = dispatcher this.eventEmitter = eventEmitter - this.registerMessageHandlers() + this.registerMessageHandlers(messageHandlerRegistry) } public async batch(messageContext: InboundMessageContext) { @@ -57,8 +55,8 @@ export class MessagePickupService { await this.messageRepository.add(connectionId, message) } - protected registerMessageHandlers() { - this.dispatcher.registerMessageHandler(new BatchPickupHandler(this)) - this.dispatcher.registerMessageHandler(new BatchHandler(this.eventEmitter)) + protected registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new BatchPickupHandler(this)) + messageHandlerRegistry.registerMessageHandler(new BatchHandler(this.eventEmitter)) } } diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts index 147467f10d..dc99c47856 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts @@ -2,7 +2,7 @@ import type { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMess import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { EncryptedMessage } from '../../../../../types' -import { Dispatcher } from '../../../../../agent/Dispatcher' +import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' import { OutboundMessageContext } from '../../../../../agent/models' import { InjectionSymbols } from '../../../../../constants' import { Attachment } from '../../../../../decorators/attachment/Attachment' @@ -23,19 +23,17 @@ import { MessageDeliveryMessage, StatusMessage } from './messages' @injectable() export class V2MessagePickupService { private messageRepository: MessageRepository - private dispatcher: Dispatcher private mediationRecipientService: MediationRecipientService public constructor( @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, - dispatcher: Dispatcher, + messageHandlerRegistry: MessageHandlerRegistry, mediationRecipientService: MediationRecipientService ) { this.messageRepository = messageRepository - this.dispatcher = dispatcher this.mediationRecipientService = mediationRecipientService - this.registerMessageHandlers() + this.registerMessageHandlers(messageHandlerRegistry) } public async processStatusRequest(messageContext: InboundMessageContext) { @@ -116,11 +114,11 @@ export class V2MessagePickupService { return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) } - protected registerMessageHandlers() { - this.dispatcher.registerMessageHandler(new StatusRequestHandler(this)) - this.dispatcher.registerMessageHandler(new DeliveryRequestHandler(this)) - this.dispatcher.registerMessageHandler(new MessagesReceivedHandler(this)) - this.dispatcher.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) - this.dispatcher.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) + protected registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { + messageHandlerRegistry.registerMessageHandler(new StatusRequestHandler(this)) + messageHandlerRegistry.registerMessageHandler(new DeliveryRequestHandler(this)) + messageHandlerRegistry.registerMessageHandler(new MessagesReceivedHandler(this)) + messageHandlerRegistry.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) + messageHandlerRegistry.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) } } diff --git a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts index 53012f73b0..95055f4945 100644 --- a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts @@ -2,7 +2,7 @@ import type { MessageRepository } from '../../../../storage/MessageRepository' import type { EncryptedMessage } from '../../../../types' import { getAgentContext, getMockConnection, mockFunction } from '../../../../../tests/helpers' -import { Dispatcher } from '../../../../agent/Dispatcher' +import { MessageHandlerRegistry } from '../../../../agent/MessageHandlerRegistry' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import { InMemoryMessageRepository } from '../../../../storage/InMemoryMessageRepository' import { DidExchangeState } from '../../../connections' @@ -23,11 +23,11 @@ const mockConnection = getMockConnection({ // Mock classes jest.mock('../MediationRecipientService') jest.mock('../../../../storage/InMemoryMessageRepository') -jest.mock('../../../../agent/Dispatcher') +jest.mock('../../../../agent/MessageHandlerRegistry') // Mock typed object const MediationRecipientServiceMock = MediationRecipientService as jest.Mock -const DispatcherMock = Dispatcher as jest.Mock +const MessageHandlerRegistryMock = MessageHandlerRegistry as jest.Mock const InMessageRepositoryMock = InMemoryMessageRepository as jest.Mock const agentContext = getAgentContext() @@ -45,11 +45,11 @@ describe('V2MessagePickupService', () => { let messageRepository: MessageRepository beforeEach(async () => { - const dispatcher = new DispatcherMock() + const messageHandlerRegistry = new MessageHandlerRegistryMock() const mediationRecipientService = new MediationRecipientServiceMock() messageRepository = new InMessageRepositoryMock() - pickupService = new V2MessagePickupService(messageRepository, dispatcher, mediationRecipientService) + pickupService = new V2MessagePickupService(messageRepository, messageHandlerRegistry, mediationRecipientService) }) describe('processStatusRequest', () => { diff --git a/packages/question-answer/src/QuestionAnswerApi.ts b/packages/question-answer/src/QuestionAnswerApi.ts index 6fed567575..97ea98c143 100644 --- a/packages/question-answer/src/QuestionAnswerApi.ts +++ b/packages/question-answer/src/QuestionAnswerApi.ts @@ -5,7 +5,6 @@ import { AgentContext, ConnectionService, OutboundMessageContext, - Dispatcher, injectable, MessageSender, } from '@aries-framework/core' @@ -22,7 +21,6 @@ export class QuestionAnswerApi { private agentContext: AgentContext public constructor( - dispatcher: Dispatcher, questionAnswerService: QuestionAnswerService, messageSender: MessageSender, connectionService: ConnectionService, @@ -32,7 +30,11 @@ export class QuestionAnswerApi { this.messageSender = messageSender this.connectionService = connectionService this.agentContext = agentContext - this.registerMessageHandlers(dispatcher) + + this.agentContext.dependencyManager.registerMessageHandlers([ + new QuestionMessageHandler(this.questionAnswerService), + new AnswerMessageHandler(this.questionAnswerService), + ]) } /** @@ -131,9 +133,4 @@ export class QuestionAnswerApi { public findById(questionAnswerId: string) { return this.questionAnswerService.findById(this.agentContext, questionAnswerId) } - - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new QuestionMessageHandler(this.questionAnswerService)) - dispatcher.registerMessageHandler(new AnswerMessageHandler(this.questionAnswerService)) - } } diff --git a/samples/extension-module/dummy/DummyApi.ts b/samples/extension-module/dummy/DummyApi.ts index 1bb998336c..9d4aa765d3 100644 --- a/samples/extension-module/dummy/DummyApi.ts +++ b/samples/extension-module/dummy/DummyApi.ts @@ -5,7 +5,6 @@ import { OutboundMessageContext, AgentContext, ConnectionService, - Dispatcher, injectable, MessageSender, } from '@aries-framework/core' @@ -22,7 +21,6 @@ export class DummyApi { private agentContext: AgentContext public constructor( - dispatcher: Dispatcher, messageSender: MessageSender, dummyService: DummyService, connectionService: ConnectionService, @@ -33,7 +31,10 @@ export class DummyApi { this.connectionService = connectionService this.agentContext = agentContext - this.registerMessageHandlers(dispatcher) + this.agentContext.dependencyManager.registerMessageHandlers([ + new DummyRequestHandler(this.dummyService), + new DummyResponseHandler(this.dummyService), + ]) } /** @@ -93,9 +94,4 @@ export class DummyApi { public findAllByQuery(query: Query): Promise { return this.dummyService.findAllByQuery(this.agentContext, query) } - - private registerMessageHandlers(dispatcher: Dispatcher) { - dispatcher.registerMessageHandler(new DummyRequestHandler(this.dummyService)) - dispatcher.registerMessageHandler(new DummyResponseHandler(this.dummyService)) - } } From 2c792fe2013d48bcb27c2550d3ffa1377638a479 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Thu, 2 Mar 2023 14:29:05 +0100 Subject: [PATCH 071/139] refactor!: remove getKeyDidMappingByVerificationMethod (#1350) Signed-off-by: martin auer --- packages/core/src/agent/MessageSender.ts | 3 +-- packages/core/src/crypto/WalletKeyPair.ts | 3 +-- .../core/src/modules/connections/DidExchangeProtocol.ts | 3 +-- packages/core/src/modules/dids/domain/DidDocument.ts | 3 +-- packages/core/src/modules/dids/domain/key-type/index.ts | 2 +- .../core/src/modules/dids/domain/key-type/keyDidMapping.ts | 4 ++-- .../core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts | 3 +-- packages/core/src/modules/vc/W3cCredentialService.ts | 7 +++---- .../src/anoncreds/services/IndySdkAnonCredsRegistry.ts | 4 +--- packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts | 3 +-- .../indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts | 4 +--- packages/openid4vc-client/src/OpenId4VcClientService.ts | 3 +-- 12 files changed, 15 insertions(+), 27 deletions(-) diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 796da85243..a481521e8c 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -15,7 +15,7 @@ import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import { AriesFrameworkError, MessageSendingError } from '../error' import { Logger } from '../logger' import { DidCommDocumentService } from '../modules/didcomm' -import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type' +import { getKeyFromVerificationMethod } from '../modules/dids/domain/key-type' import { didKeyToInstanceOfKey } from '../modules/dids/helpers' import { DidResolverService } from '../modules/dids/services/DidResolverService' import { inject, injectable } from '../plugins' @@ -508,7 +508,6 @@ function getAuthenticationKeys(didDocument: DidDocument) { didDocument.authentication?.map((authentication) => { const verificationMethod = typeof authentication === 'string' ? didDocument.dereferenceVerificationMethod(authentication) : authentication - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const key = getKeyFromVerificationMethod(verificationMethod) return key }) ?? [] diff --git a/packages/core/src/crypto/WalletKeyPair.ts b/packages/core/src/crypto/WalletKeyPair.ts index 7df8cbb6ad..97c8db0a56 100644 --- a/packages/core/src/crypto/WalletKeyPair.ts +++ b/packages/core/src/crypto/WalletKeyPair.ts @@ -3,7 +3,7 @@ import type { LdKeyPairOptions } from '../modules/vc/models/LdKeyPair' import type { Wallet } from '../wallet' import { VerificationMethod } from '../modules/dids' -import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type/keyDidMapping' +import { getKeyFromVerificationMethod } from '../modules/dids/domain/key-type/keyDidMapping' import { LdKeyPair } from '../modules/vc/models/LdKeyPair' import { JsonTransformer } from '../utils' import { MessageValidator } from '../utils/MessageValidator' @@ -43,7 +43,6 @@ export function createWalletKeyPairClass(wallet: Wallet) { public static async from(verificationMethod: VerificationMethod): Promise { const vMethod = JsonTransformer.fromJSON(verificationMethod, VerificationMethod) MessageValidator.validateSync(vMethod) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(vMethod) const key = getKeyFromVerificationMethod(vMethod) return new WalletKeyPair({ diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index d337a818de..5f83ab1a73 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -25,7 +25,7 @@ import { getNumAlgoFromPeerDid, PeerDidNumAlgo, } from '../dids' -import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' +import { getKeyFromVerificationMethod } from '../dids/domain/key-type' import { didKeyToInstanceOfKey } from '../dids/helpers' import { DidRecord, DidRepository } from '../dids/repository' import { OutOfBandRole } from '../oob/domain/OutOfBandRole' @@ -523,7 +523,6 @@ export class DidExchangeProtocol { typeof authentication === 'string' ? didDocument.dereferenceVerificationMethod(authentication) : authentication - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const key = getKeyFromVerificationMethod(verificationMethod) return key.publicKeyBase58 }) diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index 5316933952..b4d4525e36 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -7,7 +7,7 @@ import { KeyType, Key } from '../../../crypto' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IsStringOrStringArray } from '../../../utils/transformers' -import { getKeyDidMappingByVerificationMethod } from './key-type' +import { getKeyFromVerificationMethod } from './key-type' import { IndyAgentService, ServiceTransformer, DidCommV1Service } from './service' import { VerificationMethodTransformer, VerificationMethod, IsStringOrVerificationMethod } from './verificationMethod' @@ -210,7 +210,6 @@ export function keyReferenceToKey(didDocument: DidDocument, keyId: string) { // for didcomm. In the future we should update this to only be allowed for IndyAgent and DidCommV1 services // as didcomm v2 doesn't have this issue anymore const verificationMethod = didDocument.dereferenceKey(keyId, ['authentication', 'keyAgreement']) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const key = getKeyFromVerificationMethod(verificationMethod) return key diff --git a/packages/core/src/modules/dids/domain/key-type/index.ts b/packages/core/src/modules/dids/domain/key-type/index.ts index edb319be90..29a61e8d0d 100644 --- a/packages/core/src/modules/dids/domain/key-type/index.ts +++ b/packages/core/src/modules/dids/domain/key-type/index.ts @@ -1,4 +1,4 @@ -export { getKeyDidMappingByKeyType, getKeyDidMappingByVerificationMethod } from './keyDidMapping' +export { getKeyDidMappingByKeyType, getKeyFromVerificationMethod } from './keyDidMapping' export * from './bls12381g2' export * from './bls12381g1' diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index 713817d1bb..bb788c8532 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -60,12 +60,12 @@ export function getKeyDidMappingByKeyType(keyType: KeyType) { return keyDid } -export function getKeyDidMappingByVerificationMethod(verificationMethod: VerificationMethod) { +export function getKeyFromVerificationMethod(verificationMethod: VerificationMethod) { const keyDid = verificationMethodKeyDidMapping[verificationMethod.type] if (!keyDid) { throw new Error(`Unsupported key did from verification method type '${verificationMethod.type}'`) } - return keyDid + return keyDid.getKeyFromVerificationMethod(verificationMethod) } diff --git a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts index 4b26cd5efa..eee20933ed 100644 --- a/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts +++ b/packages/core/src/modules/dids/methods/peer/peerDidNumAlgo2.ts @@ -6,7 +6,7 @@ import { Key } from '../../../../crypto' import { JsonEncoder, JsonTransformer } from '../../../../utils' import { DidCommV1Service, DidDocumentService } from '../../domain' import { DidDocumentBuilder } from '../../domain/DidDocumentBuilder' -import { getKeyDidMappingByKeyType, getKeyDidMappingByVerificationMethod } from '../../domain/key-type' +import { getKeyFromVerificationMethod, getKeyDidMappingByKeyType } from '../../domain/key-type' import { parseDid } from '../../domain/parse' import { DidKey } from '../key' @@ -116,7 +116,6 @@ export function didDocumentToNumAlgo2Did(didDocument: DidDocument) { // Transform als verification methods into a fingerprint (multibase, multicodec) const encoded = dereferenced.map((entry) => { - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(entry) const key = getKeyFromVerificationMethod(entry) // Encode as '.PurposeFingerprint' diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index c2f2092610..53a57a8717 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -18,7 +18,7 @@ import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' import { JsonTransformer } from '../../utils' import { VerificationMethod } from '../dids' -import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type' +import { getKeyFromVerificationMethod } from '../dids/domain/key-type' import { SignatureSuiteRegistry } from './SignatureSuiteRegistry' import { W3cVcModuleConfig } from './W3cVcModuleConfig' @@ -297,9 +297,8 @@ export class W3cCredentialService { const verificationMethodObject = await documentLoader(verificationMethod) const verificationMethodClass = JsonTransformer.fromJSON(verificationMethodObject.document, VerificationMethod) - const key = getKeyDidMappingByVerificationMethod(verificationMethodClass) - - return key.getKeyFromVerificationMethod(verificationMethodClass) + const key = getKeyFromVerificationMethod(verificationMethodClass) + return key } /** diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 8e97b63748..2dbf79cd53 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -13,7 +13,7 @@ import type { import type { AgentContext } from '@aries-framework/core' import type { Schema as IndySdkSchema } from 'indy-sdk' -import { DidsApi, getKeyDidMappingByVerificationMethod } from '@aries-framework/core' +import { DidsApi, getKeyFromVerificationMethod } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdkPoolService } from '../../ledger' @@ -159,7 +159,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } const verificationMethod = didResult.didDocument.dereferenceKey(`did:sov:${options.schema.issuerId}#key-1`) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const submitterKey = getKeyFromVerificationMethod(verificationMethod) const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) @@ -373,7 +372,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const verificationMethod = didResult.didDocument.dereferenceKey( `did:sov:${options.credentialDefinition.issuerId}#key-1` ) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const submitterKey = getKeyFromVerificationMethod(verificationMethod) const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts index c85cf7c0e0..86fb440e67 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -15,12 +15,12 @@ import type { NymRole } from 'indy-sdk' import { DidsApi, - getKeyDidMappingByVerificationMethod, KeyType, isValidPrivateKey, DidDocumentRole, DidRecord, DidRepository, + getKeyFromVerificationMethod, } from '@aries-framework/core' import { IndySdkError } from '../error' @@ -89,7 +89,6 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { } const verificationMethod = didResult.didDocument.dereferenceKey(submitterVerificationMethod) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const submitterSigningKey = getKeyFromVerificationMethod(verificationMethod) const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 0f492edcb2..4b787414b6 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -11,7 +11,7 @@ import type { } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' -import { DidsApi, getKeyDidMappingByVerificationMethod } from '@aries-framework/core' +import { getKeyFromVerificationMethod, DidsApi } from '@aries-framework/core' import { GetSchemaRequest, SchemaRequest, @@ -154,7 +154,6 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } const verificationMethod = didResult.didDocument.dereferenceKey(`did:sov:${options.schema.issuerId}#key-1`) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const key = getKeyFromVerificationMethod(verificationMethod) const response = await pool.submitWriteRequest(agentContext, schemaRequest, key) @@ -348,7 +347,6 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const verificationMethod = didResult.didDocument.dereferenceKey( `did:sov:${options.credentialDefinition.issuerId}#key-1` ) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const key = getKeyFromVerificationMethod(verificationMethod) const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, key) diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts index 9193b9d219..b2cfdfbafa 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -2,12 +2,12 @@ import type { AgentContext, W3cCredentialRecord } from '@aries-framework/core' import type { EndpointMetadata, Jwt } from '@sphereon/openid4vci-client' import { + getKeyFromVerificationMethod, inject, InjectionSymbols, isJwtAlgorithm, Logger, DidsApi, - getKeyDidMappingByVerificationMethod, AriesFrameworkError, injectable, JsonEncoder, @@ -85,7 +85,6 @@ export class OpenId4VcClientService { // TODO: which purposes are allowed? const verificationMethod = didResult.didDocument.dereferenceKey(kid, ['authentication']) - const { getKeyFromVerificationMethod } = getKeyDidMappingByVerificationMethod(verificationMethod) const key = getKeyFromVerificationMethod(verificationMethod) const payload = JsonEncoder.toBuffer(jwt.payload) From 18abb18316f155d0375af477dedef9cdfdada70e Mon Sep 17 00:00:00 2001 From: Pritam Singh <43764373+Zzocker@users.noreply.github.com> Date: Thu, 2 Mar 2023 22:11:20 +0530 Subject: [PATCH 072/139] fix: isNewSocket logic (#1355) Signed-off-by: Pritam Singh --- packages/core/src/transport/WsOutboundTransport.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index 68c882fa2b..ff6dabda8e 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -47,7 +47,7 @@ export class WsOutboundTransport implements OutboundTransport { throw new AriesFrameworkError("Missing connection or endpoint. I don't know how and where to send the message.") } - const isNewSocket = this.hasOpenSocket(endpoint) + const isNewSocket = !this.hasOpenSocket(endpoint) const socket = await this.resolveSocket({ socketId: endpoint, endpoint, connectionId }) socket.send(Buffer.from(JSON.stringify(payload))) From fd13bb87a9ce9efb73bd780bd076b1da867688c5 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Thu, 2 Mar 2023 17:00:57 -0300 Subject: [PATCH 073/139] feat(oob): implicit invitations (#1348) Signed-off-by: Ariel Gentile --- .../connections/DidExchangeProtocol.ts | 15 +- .../handlers/ConnectionRequestHandler.ts | 19 +- .../handlers/DidExchangeCompleteHandler.ts | 7 +- .../handlers/DidExchangeRequestHandler.ts | 22 +- .../connections/services/ConnectionService.ts | 2 +- packages/core/src/modules/oob/OutOfBandApi.ts | 76 +++++- .../core/src/modules/oob/OutOfBandService.ts | 64 +++++- .../oob/__tests__/implicit.e2e.test.ts | 216 ++++++++++++++++++ .../modules/oob/repository/OutOfBandRecord.ts | 3 + .../__tests__/OutOfBandRecord.test.ts | 1 + .../__tests__/__snapshots__/0.1.test.ts.snap | 7 + packages/core/tests/helpers.ts | 52 +++++ 12 files changed, 448 insertions(+), 36 deletions(-) create mode 100644 packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 5f83ab1a73..531b133e56 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -26,6 +26,7 @@ import { PeerDidNumAlgo, } from '../dids' import { getKeyFromVerificationMethod } from '../dids/domain/key-type' +import { tryParseDid } from '../dids/domain/parse' import { didKeyToInstanceOfKey } from '../dids/helpers' import { DidRecord, DidRepository } from '../dids/repository' import { OutOfBandRole } from '../oob/domain/OutOfBandRole' @@ -104,7 +105,7 @@ export class DidExchangeProtocol { // Create message const label = params.label ?? agentContext.config.label const didDocument = await this.createPeerDidDoc(agentContext, this.routingToServices(routing)) - const parentThreadId = outOfBandInvitation.id + const parentThreadId = outOfBandRecord.outOfBandInvitation.id const message = new DidExchangeRequestMessage({ label, parentThreadId, did: didDocument.id, goal, goalCode }) @@ -146,9 +147,13 @@ export class DidExchangeProtocol { const { message } = messageContext - // Check corresponding invitation ID is the request's ~thread.pthid + // Check corresponding invitation ID is the request's ~thread.pthid or pthid is a public did // TODO Maybe we can do it in handler, but that actually does not make sense because we try to find oob by parent thread ID there. - if (!message.thread?.parentThreadId || message.thread?.parentThreadId !== outOfBandRecord.getTags().invitationId) { + const parentThreadId = message.thread?.parentThreadId + if ( + !parentThreadId || + (!tryParseDid(parentThreadId) && parentThreadId !== outOfBandRecord.getTags().invitationId) + ) { throw new DidExchangeProblemReportError('Missing reference to invitation.', { problemCode: DidExchangeProblemReportReason.RequestNotAccepted, }) @@ -401,8 +406,8 @@ export class DidExchangeProtocol { problemCode: DidExchangeProblemReportReason.CompleteRejected, }) } - - if (!message.thread?.parentThreadId || message.thread?.parentThreadId !== outOfBandRecord.getTags().invitationId) { + const pthid = message.thread?.parentThreadId + if (!pthid || pthid !== outOfBandRecord.outOfBandInvitation.id) { throw new DidExchangeProblemReportError('Invalid or missing parent thread ID referencing to the invitation.', { problemCode: DidExchangeProblemReportReason.CompleteRejected, }) diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index fdb6799028..0cbead3793 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -7,7 +7,9 @@ import type { ConnectionService } from '../services/ConnectionService' import { OutboundMessageContext } from '../../../agent/models' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { tryParseDid } from '../../dids/domain/parse' import { ConnectionRequestMessage } from '../messages' +import { HandshakeProtocol } from '../models' export class ConnectionRequestHandler implements MessageHandler { private connectionService: ConnectionService @@ -32,16 +34,23 @@ export class ConnectionRequestHandler implements MessageHandler { } public async handle(messageContext: MessageHandlerInboundMessage) { - const { connection, recipientKey, senderKey } = messageContext + const { agentContext, connection, recipientKey, senderKey, message } = messageContext if (!recipientKey || !senderKey) { throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientKey') } - const outOfBandRecord = await this.outOfBandService.findCreatedByRecipientKey( - messageContext.agentContext, - recipientKey - ) + const parentThreadId = message.thread?.parentThreadId + + const outOfBandRecord = + parentThreadId && tryParseDid(parentThreadId) + ? await this.outOfBandService.createFromImplicitInvitation(agentContext, { + did: parentThreadId, + threadId: message.threadId, + recipientKey, + handshakeProtocols: [HandshakeProtocol.Connections], + }) + : await this.outOfBandService.findCreatedByRecipientKey(agentContext, recipientKey) if (!outOfBandRecord) { throw new AriesFrameworkError(`Out-of-band record for recipient key ${recipientKey.fingerprint} was not found.`) diff --git a/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts index 76f885e82b..5d4ad8eb6a 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeCompleteHandler.ts @@ -3,6 +3,7 @@ import type { OutOfBandService } from '../../oob/OutOfBandService' import type { DidExchangeProtocol } from '../DidExchangeProtocol' import { AriesFrameworkError } from '../../../error' +import { tryParseDid } from '../../dids/domain/parse' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeCompleteMessage } from '../messages' import { HandshakeProtocol } from '../models' @@ -32,12 +33,14 @@ export class DidExchangeCompleteHandler implements MessageHandler { } const { message } = messageContext - if (!message.thread?.parentThreadId) { + const parentThreadId = message.thread?.parentThreadId + if (!parentThreadId) { throw new AriesFrameworkError(`Message does not contain pthid attribute`) } const outOfBandRecord = await this.outOfBandService.findByCreatedInvitationId( messageContext.agentContext, - message.thread?.parentThreadId + parentThreadId, + tryParseDid(parentThreadId) ? message.threadId : undefined ) if (!outOfBandRecord) { diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index 2e3bcb740d..3983fd0a89 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -7,8 +7,10 @@ import type { DidExchangeProtocol } from '../DidExchangeProtocol' import { OutboundMessageContext } from '../../../agent/models' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' +import { tryParseDid } from '../../dids/domain/parse' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeRequestMessage } from '../messages' +import { HandshakeProtocol } from '../models' export class DidExchangeRequestHandler implements MessageHandler { private didExchangeProtocol: DidExchangeProtocol @@ -33,22 +35,28 @@ export class DidExchangeRequestHandler implements MessageHandler { } public async handle(messageContext: MessageHandlerInboundMessage) { - const { recipientKey, senderKey, message, connection } = messageContext + const { agentContext, recipientKey, senderKey, message, connection } = messageContext if (!recipientKey || !senderKey) { throw new AriesFrameworkError('Unable to process connection request without senderKey or recipientKey') } - if (!message.thread?.parentThreadId) { + const parentThreadId = message.thread?.parentThreadId + + if (!parentThreadId) { throw new AriesFrameworkError(`Message does not contain 'pthid' attribute`) } - const outOfBandRecord = await this.outOfBandService.findByCreatedInvitationId( - messageContext.agentContext, - message.thread.parentThreadId - ) + const outOfBandRecord = tryParseDid(parentThreadId) + ? await this.outOfBandService.createFromImplicitInvitation(agentContext, { + did: parentThreadId, + threadId: message.threadId, + recipientKey, + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + : await this.outOfBandService.findByCreatedInvitationId(agentContext, parentThreadId) if (!outOfBandRecord) { - throw new AriesFrameworkError(`OutOfBand record for message ID ${message.thread?.parentThreadId} not found!`) + throw new AriesFrameworkError(`OutOfBand record for message ID ${parentThreadId} not found!`) } if (connection && !outOfBandRecord.reusable) { diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index 539fabac8c..ea7adab79e 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -121,7 +121,7 @@ export class ConnectionService { connectionRequest.setThread({ threadId: connectionRequest.threadId, - parentThreadId: outOfBandInvitation.id, + parentThreadId: outOfBandRecord.outOfBandInvitation.id, }) const connectionRecord = await this.createConnection(agentContext, { diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 5f1e0c00b7..d96bfe2f16 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -67,7 +67,7 @@ export interface CreateLegacyInvitationConfig { routing?: Routing } -export interface ReceiveOutOfBandInvitationConfig { +interface BaseReceiveOutOfBandInvitationConfig { label?: string alias?: string imageUrl?: string @@ -76,6 +76,15 @@ export interface ReceiveOutOfBandInvitationConfig { reuseConnection?: boolean routing?: Routing acceptInvitationTimeoutMs?: number + isImplicit?: boolean +} + +export type ReceiveOutOfBandInvitationConfig = Omit + +export interface ReceiveOutOfBandImplicitInvitationConfig + extends Omit { + did: string + handshakeProtocols?: HandshakeProtocol[] } @injectable() @@ -321,6 +330,44 @@ export class OutOfBandApi { public async receiveInvitation( invitation: OutOfBandInvitation | ConnectionInvitationMessage, config: ReceiveOutOfBandInvitationConfig = {} + ): Promise<{ outOfBandRecord: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { + return this._receiveInvitation(invitation, config) + } + + /** + * Creates inbound out-of-band record from an implicit invitation, given as a public DID the agent + * should be capable of resolving. It automatically passes out-of-band invitation for further + * processing to `acceptInvitation` method. If you don't want to do that you can set + * `autoAcceptInvitation` attribute in `config` parameter to `false` and accept the message later by + * calling `acceptInvitation`. + * + * It supports both OOB (Aries RFC 0434: Out-of-Band Protocol 1.1) and Connection Invitation + * (0160: Connection Protocol). Handshake protocol to be used depends on handshakeProtocols + * (DID Exchange by default) + * + * Agent role: receiver (invitee) + * + * @param config config for creating and handling invitation + * + * @returns out-of-band record and connection record if one has been created. + */ + public async receiveImplicitInvitation(config: ReceiveOutOfBandImplicitInvitationConfig) { + const invitation = new OutOfBandInvitation({ + id: config.did, + label: config.label ?? '', + services: [config.did], + handshakeProtocols: config.handshakeProtocols ?? [HandshakeProtocol.DidExchange], + }) + + return this._receiveInvitation(invitation, { ...config, isImplicit: true }) + } + + /** + * Internal receive invitation method, for both explicit and implicit OOB invitations + */ + private async _receiveInvitation( + invitation: OutOfBandInvitation | ConnectionInvitationMessage, + config: BaseReceiveOutOfBandInvitationConfig = {} ): Promise<{ outOfBandRecord: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { // Convert to out of band invitation if needed const outOfBandInvitation = @@ -344,15 +391,19 @@ export class OutOfBandApi { ) } - // Make sure we haven't received this invitation before. (it's fine if we created it, that means we're connecting with ourselves - let [outOfBandRecord] = await this.outOfBandService.findAllByQuery(this.agentContext, { - invitationId: outOfBandInvitation.id, - role: OutOfBandRole.Receiver, - }) - if (outOfBandRecord) { - throw new AriesFrameworkError( - `An out of band record with invitation ${outOfBandInvitation.id} has already been received. Invitations should have a unique id.` - ) + // Make sure we haven't received this invitation before + // It's fine if we created it (means that we are connnecting to ourselves) or if it's an implicit + // invitation (it allows to connect multiple times to the same public did) + if (!config.isImplicit) { + const existingOobRecordsFromThisId = await this.outOfBandService.findAllByQuery(this.agentContext, { + invitationId: outOfBandInvitation.id, + role: OutOfBandRole.Receiver, + }) + if (existingOobRecordsFromThisId.length > 0) { + throw new AriesFrameworkError( + `An out of band record with invitation ${outOfBandInvitation.id} has already been received. Invitations should have a unique id.` + ) + } } const recipientKeyFingerprints: string[] = [] @@ -374,7 +425,7 @@ export class OutOfBandApi { } } - outOfBandRecord = new OutOfBandRecord({ + const outOfBandRecord = new OutOfBandRecord({ role: OutOfBandRole.Receiver, state: OutOfBandState.Initial, outOfBandInvitation: outOfBandInvitation, @@ -430,11 +481,12 @@ export class OutOfBandApi { const { outOfBandInvitation } = outOfBandRecord const { label, alias, imageUrl, autoAcceptConnection, reuseConnection, routing } = config - const { handshakeProtocols } = outOfBandInvitation const services = outOfBandInvitation.getServices() const messages = outOfBandInvitation.getRequests() const timeoutMs = config.timeoutMs ?? 20000 + const { handshakeProtocols } = outOfBandInvitation + const existingConnection = await this.findExistingConnection(outOfBandInvitation) await this.outOfBandService.updateState(this.agentContext, outOfBandRecord, OutOfBandState.PrepareResponse) diff --git a/packages/core/src/modules/oob/OutOfBandService.ts b/packages/core/src/modules/oob/OutOfBandService.ts index 377e8867c2..1802b03064 100644 --- a/packages/core/src/modules/oob/OutOfBandService.ts +++ b/packages/core/src/modules/oob/OutOfBandService.ts @@ -1,22 +1,32 @@ import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' -import type { OutOfBandRecord } from './repository' import type { AgentContext } from '../../agent' import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' import type { Key } from '../../crypto' import type { Query } from '../../storage/StorageService' import type { ConnectionRecord } from '../connections' +import type { HandshakeProtocol } from '../connections/models' import { EventEmitter } from '../../agent/EventEmitter' import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' import { JsonTransformer } from '../../utils' +import { DidsApi } from '../dids' +import { parseDid } from '../dids/domain/parse' import { OutOfBandEventTypes } from './domain/OutOfBandEvents' import { OutOfBandRole } from './domain/OutOfBandRole' import { OutOfBandState } from './domain/OutOfBandState' -import { HandshakeReuseMessage } from './messages' +import { HandshakeReuseMessage, OutOfBandInvitation } from './messages' import { HandshakeReuseAcceptedMessage } from './messages/HandshakeReuseAcceptedMessage' -import { OutOfBandRepository } from './repository' +import { OutOfBandRecord, OutOfBandRepository } from './repository' + +export interface CreateFromImplicitInvitationConfig { + did: string + threadId: string + handshakeProtocols: HandshakeProtocol[] + autoAcceptConnection?: boolean + recipientKey: Key +} @injectable() export class OutOfBandService { @@ -28,6 +38,51 @@ export class OutOfBandService { this.eventEmitter = eventEmitter } + /** + * Creates an Out of Band record from a Connection/DIDExchange request started by using + * a publicly resolvable DID this agent can control + */ + public async createFromImplicitInvitation( + agentContext: AgentContext, + config: CreateFromImplicitInvitationConfig + ): Promise { + const { did, threadId, handshakeProtocols, autoAcceptConnection, recipientKey } = config + + // Verify it is a valid did and it is present in the wallet + const publicDid = parseDid(did) + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const [createdDid] = await didsApi.getCreatedDids({ did: publicDid.did }) + if (!createdDid) { + throw new AriesFrameworkError(`Referenced public did ${did} not found.`) + } + + // Recreate an 'implicit invitation' matching the parameters used by the invitee when + // initiating the flow + const outOfBandInvitation = new OutOfBandInvitation({ + id: did, + label: '', + services: [did], + handshakeProtocols, + }) + + outOfBandInvitation.setThread({ threadId }) + + const outOfBandRecord = new OutOfBandRecord({ + role: OutOfBandRole.Sender, + state: OutOfBandState.AwaitResponse, + reusable: true, + autoAcceptConnection: autoAcceptConnection ?? false, + outOfBandInvitation, + tags: { + recipientKeyFingerprints: [recipientKey.fingerprint], + }, + }) + + await this.save(agentContext, outOfBandRecord) + this.emitStateChangedEvent(agentContext, outOfBandRecord, null) + return outOfBandRecord + } + public async processHandshakeReuse(messageContext: InboundMessageContext) { const reuseMessage = messageContext.message const parentThreadId = reuseMessage.thread?.parentThreadId @@ -172,10 +227,11 @@ export class OutOfBandService { }) } - public async findByCreatedInvitationId(agentContext: AgentContext, createdInvitationId: string) { + public async findByCreatedInvitationId(agentContext: AgentContext, createdInvitationId: string, threadId?: string) { return this.outOfBandRepository.findSingleByQuery(agentContext, { invitationId: createdInvitationId, role: OutOfBandRole.Sender, + threadId, }) } diff --git a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts new file mode 100644 index 0000000000..ae0c98e6d7 --- /dev/null +++ b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts @@ -0,0 +1,216 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { IndySdkSovDidCreateOptions } from '@aries-framework/indy-sdk' + +import { getLegacyAnonCredsModules } from '../../../../../anoncreds/tests/legacyAnonCredsSetup' +import { setupSubjectTransports } from '../../../../tests' +import { + getAgentOptions, + importExistingIndyDidFromPrivateKey, + publicDidSeed, + waitForConnectionRecord, +} from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { TypedArrayEncoder } from '../../../utils' +import { sleep } from '../../../utils/sleep' +import { DidExchangeState, HandshakeProtocol } from '../../connections' + +const faberAgentOptions = getAgentOptions( + 'Faber Agent OOB Implicit', + { + endpoints: ['rxjs:faber'], + }, + getLegacyAnonCredsModules() +) +const aliceAgentOptions = getAgentOptions( + 'Alice Agent OOB Implicit', + { + endpoints: ['rxjs:alice'], + }, + getLegacyAnonCredsModules() +) + +describe('out of band implicit', () => { + let faberAgent: Agent + let aliceAgent: Agent + let unqualifiedSubmitterDid: string + + beforeAll(async () => { + faberAgent = new Agent(faberAgentOptions) + aliceAgent = new Agent(aliceAgentOptions) + + setupSubjectTransports([faberAgent, aliceAgent]) + await faberAgent.initialize() + await aliceAgent.initialize() + + unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + faberAgent, + TypedArrayEncoder.fromString(publicDidSeed) + ) + }) + + afterAll(async () => { + await faberAgent.shutdown() + await faberAgent.wallet.delete() + await aliceAgent.shutdown() + await aliceAgent.wallet.delete() + }) + + afterEach(async () => { + const connections = await faberAgent.connections.getAll() + for (const connection of connections) { + await faberAgent.connections.deleteById(connection.id) + } + + jest.resetAllMocks() + }) + + test(`make a connection with ${HandshakeProtocol.DidExchange} based on implicit OOB invitation`, async () => { + const publicDid = await createPublicDid(faberAgent, unqualifiedSubmitterDid, 'rxjs:faber') + expect(publicDid).toBeDefined() + + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ + did: publicDid!, + alias: 'Faber public', + label: 'Alice', + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + + // Wait for a connection event in faber agent and accept the request + let faberAliceConnection = await waitForConnectionRecord(faberAgent, { state: DidExchangeState.RequestReceived }) + await faberAgent.connections.acceptRequest(faberAliceConnection.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection.state).toBe(DidExchangeState.Completed) + + // Alice should now be connected + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(faberAliceConnection.theirLabel).toBe('Alice') + expect(aliceFaberConnection.alias).toBe('Faber public') + expect(aliceFaberConnection.invitationDid).toBe(publicDid) + + // It is possible for an agent to check if it has already a connection to a certain public entity + expect(await aliceAgent.connections.findByInvitationDid(publicDid!)).toEqual([aliceFaberConnection]) + }) + + test(`make a connection with ${HandshakeProtocol.Connections} based on implicit OOB invitation`, async () => { + const publicDid = await createPublicDid(faberAgent, unqualifiedSubmitterDid, 'rxjs:faber') + expect(publicDid).toBeDefined() + + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ + did: publicDid!, + alias: 'Faber public', + label: 'Alice', + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + // Wait for a connection event in faber agent and accept the request + let faberAliceConnection = await waitForConnectionRecord(faberAgent, { state: DidExchangeState.RequestReceived }) + await faberAgent.connections.acceptRequest(faberAliceConnection.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection.state).toBe(DidExchangeState.Completed) + + // Alice should now be connected + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(faberAliceConnection.theirLabel).toBe('Alice') + expect(aliceFaberConnection.alias).toBe('Faber public') + expect(aliceFaberConnection.invitationDid).toBe(publicDid) + + // It is possible for an agent to check if it has already a connection to a certain public entity + expect(await aliceAgent.connections.findByInvitationDid(publicDid!)).toEqual([aliceFaberConnection]) + }) + + test(`receive an implicit invitation using an unresolvable did`, async () => { + await expect( + aliceAgent.oob.receiveImplicitInvitation({ + did: 'did:sov:ZSEqSci581BDZCFPa29ScB', + alias: 'Faber public', + label: 'Alice', + handshakeProtocols: [HandshakeProtocol.DidExchange], + }) + ).rejects.toThrowError(/Unable to resolve did/) + }) + + test(`create two connections using the same implicit invitation`, async () => { + const publicDid = await createPublicDid(faberAgent, unqualifiedSubmitterDid, 'rxjs:faber') + expect(publicDid).toBeDefined() + + let { connectionRecord: aliceFaberConnection } = await aliceAgent.oob.receiveImplicitInvitation({ + did: publicDid!, + alias: 'Faber public', + label: 'Alice', + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + // Wait for a connection event in faber agent and accept the request + let faberAliceConnection = await waitForConnectionRecord(faberAgent, { state: DidExchangeState.RequestReceived }) + await faberAgent.connections.acceptRequest(faberAliceConnection.id) + faberAliceConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceConnection!.id) + expect(faberAliceConnection.state).toBe(DidExchangeState.Completed) + + // Alice should now be connected + aliceFaberConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberConnection!.id) + expect(aliceFaberConnection.state).toBe(DidExchangeState.Completed) + + expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection) + expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) + expect(faberAliceConnection.theirLabel).toBe('Alice') + expect(aliceFaberConnection.alias).toBe('Faber public') + expect(aliceFaberConnection.invitationDid).toBe(publicDid) + + // Repeat implicit invitation procedure + let { connectionRecord: aliceFaberNewConnection } = await aliceAgent.oob.receiveImplicitInvitation({ + did: publicDid!, + alias: 'Faber public New', + label: 'Alice New', + handshakeProtocols: [HandshakeProtocol.Connections], + }) + + // Wait for a connection event in faber agent + let faberAliceNewConnection = await waitForConnectionRecord(faberAgent, { state: DidExchangeState.RequestReceived }) + await faberAgent.connections.acceptRequest(faberAliceNewConnection.id) + faberAliceNewConnection = await faberAgent.connections.returnWhenIsConnected(faberAliceNewConnection!.id) + expect(faberAliceNewConnection.state).toBe(DidExchangeState.Completed) + + // Alice should now be connected + aliceFaberNewConnection = await aliceAgent.connections.returnWhenIsConnected(aliceFaberNewConnection!.id) + expect(aliceFaberNewConnection.state).toBe(DidExchangeState.Completed) + + expect(aliceFaberNewConnection).toBeConnectedWith(faberAliceNewConnection) + expect(faberAliceNewConnection).toBeConnectedWith(aliceFaberNewConnection) + expect(faberAliceNewConnection.theirLabel).toBe('Alice New') + expect(aliceFaberNewConnection.alias).toBe('Faber public New') + expect(aliceFaberNewConnection.invitationDid).toBe(publicDid) + + // Both connections will be associated to the same invitation did + const connectionsFromFaberPublicDid = await aliceAgent.connections.findByInvitationDid(publicDid!) + expect(connectionsFromFaberPublicDid).toHaveLength(2) + expect(connectionsFromFaberPublicDid).toEqual( + expect.arrayContaining([aliceFaberConnection, aliceFaberNewConnection]) + ) + }) +}) + +async function createPublicDid(agent: Agent, unqualifiedSubmitterDid: string, endpoint: string) { + const createResult = await agent.dids.create({ + method: 'sov', + options: { + submitterVerificationMethod: `did:sov:${unqualifiedSubmitterDid}#key-1`, + alias: 'Alias', + endpoints: { + endpoint, + types: ['DIDComm', 'did-communication', 'endpoint'], + }, + }, + }) + + await sleep(1000) + + return createResult.didState.did +} diff --git a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts index ec291225c2..202e6a3886 100644 --- a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts +++ b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts @@ -13,6 +13,7 @@ type DefaultOutOfBandRecordTags = { role: OutOfBandRole state: OutOfBandState invitationId: string + threadId?: string } interface CustomOutOfBandRecordTags extends TagsBase { @@ -32,6 +33,7 @@ export interface OutOfBandRecordProps { reusable?: boolean mediatorId?: string reuseConnectionId?: string + threadId?: string } export class OutOfBandRecord extends BaseRecord { @@ -72,6 +74,7 @@ export class OutOfBandRecord extends BaseRecord { state: OutOfBandState.Done, role: OutOfBandRole.Receiver, invitationId: 'a-message-id', + threadId: 'a-message-id', recipientKeyFingerprints: ['z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th'], }) }) diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index a64116a5f5..6df70279be 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -788,6 +788,7 @@ Object { ], "role": "receiver", "state": "done", + "threadId": "d56fd7af-852e-458e-b750-7a4f4e53d6e6", }, "type": "OutOfBandRecord", "value": Object { @@ -853,6 +854,7 @@ Object { ], "role": "sender", "state": "done", + "threadId": "d939d371-3155-4d9c-87d1-46447f624f44", }, "type": "OutOfBandRecord", "value": Object { @@ -918,6 +920,7 @@ Object { ], "role": "sender", "state": "done", + "threadId": "21ef606f-b25b-48c6-bafa-e79193732413", }, "type": "OutOfBandRecord", "value": Object { @@ -983,6 +986,7 @@ Object { ], "role": "receiver", "state": "done", + "threadId": "08eb8d8b-67cf-4ce2-9aca-c7d260a5c143", }, "type": "OutOfBandRecord", "value": Object { @@ -1048,6 +1052,7 @@ Object { ], "role": "receiver", "state": "done", + "threadId": "cc67fb5e-1414-4ba6-9030-7456ccd2aaea", }, "type": "OutOfBandRecord", "value": Object { @@ -1113,6 +1118,7 @@ Object { ], "role": "sender", "state": "await-response", + "threadId": "f0ca03d8-2e11-4ff2-a5fc-e0137a434b7e", }, "type": "OutOfBandRecord", "value": Object { @@ -1173,6 +1179,7 @@ Object { ], "role": "sender", "state": "await-response", + "threadId": "1f516e35-08d3-43d8-900c-99d5239f54da", }, "type": "OutOfBandRecord", "value": Object { diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 3309c1afdf..2d11ae4109 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -12,6 +12,7 @@ import type { Wallet, Agent, CredentialState, + ConnectionStateChangedEvent, Buffer, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' @@ -27,6 +28,7 @@ import { catchError, filter, map, take, timeout } from 'rxjs/operators' import { agentDependencies, IndySdkPostgresWalletScheme } from '../../node/src' import { + ConnectionEventTypes, TypedArrayEncoder, AgentConfig, AgentContext, @@ -189,6 +191,8 @@ const isProofStateChangedEvent = (e: BaseEvent): e is ProofStateChangedEvent => e.type === ProofEventTypes.ProofStateChanged const isCredentialStateChangedEvent = (e: BaseEvent): e is CredentialStateChangedEvent => e.type === CredentialEventTypes.CredentialStateChanged +const isConnectionStateChangedEvent = (e: BaseEvent): e is ConnectionStateChangedEvent => + e.type === ConnectionEventTypes.ConnectionStateChanged const isTrustPingReceivedEvent = (e: BaseEvent): e is TrustPingReceivedEvent => e.type === TrustPingEventTypes.TrustPingReceivedEvent const isTrustPingResponseReceivedEvent = (e: BaseEvent): e is TrustPingResponseReceivedEvent => @@ -367,6 +371,54 @@ export async function waitForCredentialRecord( return waitForCredentialRecordSubject(observable, options) } +export function waitForConnectionRecordSubject( + subject: ReplaySubject | Observable, + { + threadId, + state, + previousState, + timeoutMs = 15000, // sign and store credential in W3c credential protocols take several seconds + }: { + threadId?: string + state?: DidExchangeState + previousState?: DidExchangeState | null + timeoutMs?: number + } +) { + const observable = subject instanceof ReplaySubject ? subject.asObservable() : subject + + return firstValueFrom( + observable.pipe( + filter(isConnectionStateChangedEvent), + filter((e) => previousState === undefined || e.payload.previousState === previousState), + filter((e) => threadId === undefined || e.payload.connectionRecord.threadId === threadId), + filter((e) => state === undefined || e.payload.connectionRecord.state === state), + timeout(timeoutMs), + catchError(() => { + throw new Error(`ConnectionStateChanged event not emitted within specified timeout: { + previousState: ${previousState}, + threadId: ${threadId}, + state: ${state} +}`) + }), + map((e) => e.payload.connectionRecord) + ) + ) +} + +export async function waitForConnectionRecord( + agent: Agent, + options: { + threadId?: string + state?: DidExchangeState + previousState?: DidExchangeState | null + timeoutMs?: number + } +) { + const observable = agent.events.observable(ConnectionEventTypes.ConnectionStateChanged) + return waitForConnectionRecordSubject(observable, options) +} + export async function waitForBasicMessage(agent: Agent, { content }: { content?: string }): Promise { return new Promise((resolve) => { const listener = (event: BasicMessageStateChangedEvent) => { From cb4e469b35ebcf9bd1c2c2aa7b03757142b0de8e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 2 Mar 2023 22:38:14 +0100 Subject: [PATCH 074/139] test: various improvements (#1361) Signed-off-by: Timo Glastra --- .eslintrc.js | 6 +++++- .github/workflows/continuous-integration.yml | 5 +++++ packages/anoncreds/tests/setup.ts | 2 +- .../storage/__tests__/AskarStorageService.test.ts | 3 ++- .../src/wallet/__tests__/AskarWallet.test.ts | 3 ++- .../askar/src/wallet/__tests__/packing.test.ts | 3 ++- packages/bbs-signatures/tests/util.ts | 10 ---------- packages/tenants/tests/setup.ts | 2 +- scripts/add-ref-napi-resolution.js | 15 +++++++++++++++ tests/e2e-askar-indy-sdk-wallet-subject.test.ts | 4 +++- tests/runInVersion.ts | 12 ++++++++++++ 11 files changed, 48 insertions(+), 17 deletions(-) create mode 100644 scripts/add-ref-napi-resolution.js create mode 100644 tests/runInVersion.ts diff --git a/.eslintrc.js b/.eslintrc.js index 8db1c9bd25..c669beed73 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -89,10 +89,14 @@ module.exports = { }, }, { - files: ['jest.config.ts', '.eslintrc.js'], + files: ['jest.config.ts', '.eslintrc.js', './scripts/**'], env: { node: true, }, + rules: { + '@typescript-eslint/no-var-requires': 'off', + 'no-undef': 'off', + }, }, { files: ['demo/**'], diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 0ad780e636..d75c6b89b0 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -111,6 +111,11 @@ jobs: uses: ./.github/actions/setup-node with: node-version: ${{ matrix.node-version }} + + - name: Add ref-napi resolution in Node18 + run: node ./scripts/add-ref-napi-resolution.js + if: matrix.node-version == '18.x' + - name: Install dependencies run: yarn install diff --git a/packages/anoncreds/tests/setup.ts b/packages/anoncreds/tests/setup.ts index b60b932be5..869a89f5d8 100644 --- a/packages/anoncreds/tests/setup.ts +++ b/packages/anoncreds/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(25000) +jest.setTimeout(50000) diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index 2208cde944..70ba8bec1f 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -8,6 +8,7 @@ import { } from '@aries-framework/core' import { ariesAskar } from '@hyperledger/aries-askar-shared' +import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' import { AskarWallet } from '../../wallet/AskarWallet' @@ -16,7 +17,7 @@ import { askarQueryFromSearchQuery } from '../utils' const startDate = Date.now() -describe('AskarStorageService', () => { +describeRunInNodeVersion([18], 'AskarStorageService', () => { let wallet: AskarWallet let storageService: AskarStorageService let agentContext: AgentContext diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 2d47e201cd..18b9fec9d0 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -22,6 +22,7 @@ import { } from '@aries-framework/core' import { Store } from '@hyperledger/aries-askar-shared' +import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { encodeToBase58 } from '../../../../core/src/utils/base58' import { agentDependencies } from '../../../../core/tests/helpers' import testLogger from '../../../../core/tests/logger' @@ -35,7 +36,7 @@ const walletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } -describe('AskarWallet basic operations', () => { +describeRunInNodeVersion([18], 'AskarWallet basic operations', () => { let askarWallet: AskarWallet const seed = TypedArrayEncoder.fromString('sample-seed-min-of-32-bytes-long') diff --git a/packages/askar/src/wallet/__tests__/packing.test.ts b/packages/askar/src/wallet/__tests__/packing.test.ts index 2a27e18678..5dcb8c1b58 100644 --- a/packages/askar/src/wallet/__tests__/packing.test.ts +++ b/packages/askar/src/wallet/__tests__/packing.test.ts @@ -8,6 +8,7 @@ import { KeyDerivationMethod, } from '@aries-framework/core' +import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { agentDependencies } from '../../../../core/tests/helpers' import testLogger from '../../../../core/tests/logger' import { AskarWallet } from '../AskarWallet' @@ -20,7 +21,7 @@ const walletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } -describe('askarWallet packing', () => { +describeRunInNodeVersion([18], 'askarWallet packing', () => { let askarWallet: AskarWallet beforeEach(async () => { diff --git a/packages/bbs-signatures/tests/util.ts b/packages/bbs-signatures/tests/util.ts index 5d73cbe64b..208a6ce8ac 100644 --- a/packages/bbs-signatures/tests/util.ts +++ b/packages/bbs-signatures/tests/util.ts @@ -1,13 +1,3 @@ -export function testSkipNode17And18(...parameters: Parameters) { - const version = process.version - - if (version.startsWith('v17.') || version.startsWith('v18.')) { - test.skip(...parameters) - } else { - test(...parameters) - } -} - export function describeSkipNode17And18(...parameters: Parameters) { const version = process.version diff --git a/packages/tenants/tests/setup.ts b/packages/tenants/tests/setup.ts index 4955aeb601..1a4c59d4ff 100644 --- a/packages/tenants/tests/setup.ts +++ b/packages/tenants/tests/setup.ts @@ -1,3 +1,3 @@ import 'reflect-metadata' -jest.setTimeout(20000) +jest.setTimeout(50000) diff --git a/scripts/add-ref-napi-resolution.js b/scripts/add-ref-napi-resolution.js new file mode 100644 index 0000000000..d8f01d135f --- /dev/null +++ b/scripts/add-ref-napi-resolution.js @@ -0,0 +1,15 @@ +const fs = require('fs') +const path = require('path') + +// Read package.json +const packageJsonPath = path.join(__dirname, '..', 'package.json') +const packageJson = JSON.parse(fs.readFileSync(packageJsonPath)) + +// Add ref-napi resolution +packageJson.resolutions = { + ...packageJson.resolutions, + 'ref-napi': 'npm:@2060.io/ref-napi', +} + +// Write package.json +fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)) diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts index d59263dfe2..5f1197a294 100644 --- a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -12,6 +12,7 @@ import { getAgentOptions } from '../packages/core/tests/helpers' import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { e2eTest } from './e2e-test' +import { describeRunInNodeVersion } from './runInVersion' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' @@ -46,7 +47,8 @@ const senderAgentOptions = getAgentOptions( }) ) -describe.skip('E2E Askar-AnonCredsRS-IndyVDR Subject tests', () => { +// Performance issues outside of Node 18 +describeRunInNodeVersion([18], 'E2E Askar-AnonCredsRS-IndyVDR Subject tests', () => { let recipientAgent: AnonCredsTestsAgent let mediatorAgent: AnonCredsTestsAgent let senderAgent: AnonCredsTestsAgent diff --git a/tests/runInVersion.ts b/tests/runInVersion.ts new file mode 100644 index 0000000000..86afbe3889 --- /dev/null +++ b/tests/runInVersion.ts @@ -0,0 +1,12 @@ +type NodeVersions = 14 | 16 | 17 | 18 + +export function describeRunInNodeVersion(versions: NodeVersions[], ...parameters: Parameters) { + const runtimeVersion = process.version + const mappedVersions = versions.map((version) => `v${version}.`) + + if (mappedVersions.some((version) => runtimeVersion.startsWith(version))) { + describe(...parameters) + } else { + describe.skip(...parameters) + } +} From 779597563a4236fdab851df9e102dca18ce2d4e4 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Fri, 3 Mar 2023 11:07:43 +0100 Subject: [PATCH 075/139] fix(tenant): Correctly configure storage for multi tenant agents (#1359) Fixes hyperledger#1353 Signed-off-by: martin auer --- .../src/context/TenantSessionCoordinator.ts | 18 ++++++++++++++++-- .../__tests__/TenantSessionCoordinator.test.ts | 8 +++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/tenants/src/context/TenantSessionCoordinator.ts b/packages/tenants/src/context/TenantSessionCoordinator.ts index cdc7428a86..c085675d5a 100644 --- a/packages/tenants/src/context/TenantSessionCoordinator.ts +++ b/packages/tenants/src/context/TenantSessionCoordinator.ts @@ -10,6 +10,7 @@ import { InjectionSymbols, Logger, WalletApi, + WalletError, } from '@aries-framework/core' import { Mutex, withTimeout } from 'async-mutex' @@ -180,7 +181,16 @@ export class TenantSessionCoordinator { private async createAgentContext(tenantRecord: TenantRecord) { const tenantDependencyManager = this.rootAgentContext.dependencyManager.createChild() - const tenantConfig = this.rootAgentContext.config.extend(tenantRecord.config) + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id, key, keyDerivationMethod, ...strippedWalletConfig } = this.rootAgentContext.config?.walletConfig ?? {} + const tenantConfig = this.rootAgentContext.config.extend({ + ...tenantRecord.config, + walletConfig: { + ...strippedWalletConfig, + ...tenantRecord.config.walletConfig, + }, + }) const agentContext = new AgentContext({ contextCorrelationId: tenantRecord.id, @@ -194,7 +204,11 @@ export class TenantSessionCoordinator { // and will also write the storage version to the storage, which is needed by the update assistant. We either // need to move this out of the module, or just keep using the module here. const walletApi = agentContext.dependencyManager.resolve(WalletApi) - await walletApi.initialize(tenantRecord.config.walletConfig) + + if (!tenantConfig.walletConfig) { + throw new WalletError('Cannot initialize tenant without Wallet config.') + } + await walletApi.initialize(tenantConfig.walletConfig) return agentContext } diff --git a/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts b/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts index dd659db44c..6744ef0359 100644 --- a/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts +++ b/packages/tenants/src/context/__tests__/TenantSessionCoordinator.test.ts @@ -1,7 +1,7 @@ import type { TenantAgentContextMapping } from '../TenantSessionCoordinator' import type { DependencyManager } from '@aries-framework/core' -import { AgentContext, AgentConfig, WalletApi } from '@aries-framework/core' +import { AgentConfig, AgentContext, WalletApi } from '@aries-framework/core' import { Mutex, withTimeout } from 'async-mutex' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' @@ -91,10 +91,8 @@ describe('TenantSessionCoordinator', () => { registerInstance: jest.fn(), resolve: jest.fn(() => wallet), } as unknown as DependencyManager - const mockConfig = jest.fn() as unknown as AgentConfig createChildSpy.mockReturnValue(tenantDependencyManager) - extendSpy.mockReturnValue(mockConfig) const tenantAgentContext = await tenantSessionCoordinator.getContextForSession(tenantRecord) @@ -103,7 +101,7 @@ describe('TenantSessionCoordinator', () => { expect(extendSpy).toHaveBeenCalledWith(tenantRecord.config) expect(createChildSpy).toHaveBeenCalledWith() expect(tenantDependencyManager.registerInstance).toHaveBeenCalledWith(AgentContext, expect.any(AgentContext)) - expect(tenantDependencyManager.registerInstance).toHaveBeenCalledWith(AgentConfig, mockConfig) + expect(tenantDependencyManager.registerInstance).toHaveBeenCalledWith(AgentConfig, expect.any(AgentConfig)) expect(tenantSessionCoordinator.tenantAgentContextMapping.tenant1).toEqual({ agentContext: tenantAgentContext, @@ -194,8 +192,8 @@ describe('TenantSessionCoordinator', () => { }) // Initialize should only be called once - expect(wallet.initialize).toHaveBeenCalledTimes(1) expect(wallet.initialize).toHaveBeenCalledWith(tenantRecord.config.walletConfig) + expect(wallet.initialize).toHaveBeenCalledTimes(1) expect(tenantAgentContext1).toBe(tenantAgentContext2) }) From 8f6b34465493551877e68f0ce3b20f0bcd4213ca Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 3 Mar 2023 12:15:58 +0100 Subject: [PATCH 076/139] feat(indy-sdk)!: move to did:indy with limited support (#1347) Signed-off-by: Timo Glastra --- .../__tests__/AnonCredsRsServices.test.ts | 2 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 2 +- .../legacy-indy-format-services.test.ts | 35 +- .../tests/InMemoryAnonCredsRegistry.ts | 66 ++- packages/anoncreds/tests/anoncreds.test.ts | 32 +- .../anoncreds/tests/legacyAnonCredsSetup.ts | 47 +- ...f.credentials.propose-offerED25519.test.ts | 7 +- .../oob/__tests__/implicit.e2e.test.ts | 8 +- .../services/IndySdkAnonCredsRegistry.ts | 240 ++++----- .../services/IndySdkHolderService.ts | 6 +- .../services/IndySdkIssuerService.ts | 19 +- .../services/IndySdkVerifierService.ts | 6 +- .../utils/__tests__/identifiers.test.ts | 182 +++++-- .../utils/__tests__/transform.test.ts | 20 +- .../src/anoncreds/utils/identifiers.ts | 161 ++++-- .../indy-sdk/src/anoncreds/utils/transform.ts | 29 +- .../src/dids/IndySdkIndyDidRegistrar.ts | 315 +++++++++++ .../src/dids/IndySdkIndyDidResolver.ts | 122 +++++ .../src/dids/IndySdkSovDidRegistrar.ts | 302 ----------- .../src/dids/IndySdkSovDidResolver.ts | 20 +- .../__tests__/IndySdkIndyDidRegistrar.test.ts | 494 ++++++++++++++++++ .../__tests__/IndySdkIndyDidResolver.test.ts | 127 +++++ .../__tests__/IndySdkSovDidRegistrar.test.ts | 372 ------------- .../__tests__/IndySdkSovDidResolver.test.ts | 4 + .../didIndyPool1R1xKJw17sUoXhejEpugMYJ.json | 50 ++ .../didIndyPool1WJz9mHyW9BZksioQnRsrAo.json | 48 ++ packages/indy-sdk/src/dids/didIndyUtil.ts | 67 +++ packages/indy-sdk/src/dids/didSovUtil.ts | 6 + packages/indy-sdk/src/dids/index.ts | 8 +- packages/indy-sdk/src/index.ts | 8 +- .../indy-sdk/src/ledger/IndySdkPoolService.ts | 39 +- .../__tests__/IndySdkPoolService.test.ts | 6 + .../indy-sdk/src/utils/__tests__/did.test.ts | 12 +- packages/indy-sdk/src/utils/did.ts | 7 +- .../indy-sdk/tests/__fixtures__/anoncreds.ts | 30 ++ ...test.ts => indy-did-registrar.e2e.test.ts} | 50 +- .../tests/indy-did-resolver.e2e.test.ts | 99 ++++ .../indy-sdk-anoncreds-registry.e2e.test.ts | 311 ++++++++--- packages/indy-sdk/tests/setupIndySdkModule.ts | 12 +- .../tests/sov-did-resolver.e2e.test.ts | 50 +- 40 files changed, 2297 insertions(+), 1124 deletions(-) create mode 100644 packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts create mode 100644 packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts delete mode 100644 packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts create mode 100644 packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts create mode 100644 packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts delete mode 100644 packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts create mode 100644 packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json create mode 100644 packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json create mode 100644 packages/indy-sdk/src/dids/didIndyUtil.ts create mode 100644 packages/indy-sdk/tests/__fixtures__/anoncreds.ts rename packages/indy-sdk/tests/{sov-did-registrar.e2e.test.ts => indy-did-registrar.e2e.test.ts} (68%) create mode 100644 packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index 019063bcbb..e0ba7089a9 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -48,7 +48,7 @@ const agentContext = getAgentContext({ describe('AnonCredsRsServices', () => { test('issuance flow without revocation', async () => { - const issuerId = 'issuer:uri' + const issuerId = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' const schema = await anonCredsIssuerService.createSchema(agentContext, { attrNames: ['name', 'age'], diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index d3b53ed6fe..eadaffd740 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -69,7 +69,7 @@ const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService( const legacyIndyProofFormatService = new LegacyIndyProofFormatService() // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) -const indyDid = 'TL1EaPFCZ8Si5aUrqScBDt' +const indyDid = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' describe('Legacy indy format services using anoncreds-rs', () => { test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 2a8e628097..4606899650 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -22,7 +22,13 @@ import { IndySdkWallet, } from '../../../../indy-sdk/src' import { IndySdkRevocationService } from '../../../../indy-sdk/src/anoncreds/services/IndySdkRevocationService' -import { indyDidFromPublicKeyBase58 } from '../../../../indy-sdk/src/utils/did' +import { + getLegacyCredentialDefinitionId, + getLegacySchemaId, + parseCredentialDefinitionId, + parseSchemaId, +} from '../../../../indy-sdk/src/anoncreds/utils/identifiers' +import { legacyIndyDidFromPublicKeyBase58 } from '../../../../indy-sdk/src/utils/did' import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' import { AnonCredsModuleConfig } from '../../AnonCredsModuleConfig' import { AnonCredsLinkSecretRecord, AnonCredsLinkSecretRepository } from '../../repository' @@ -79,7 +85,8 @@ describe('Legacy indy format services', () => { test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { // This is just so we don't have to register an actual indy did (as we don't have the indy did registrar configured) const key = await wallet.createKey({ keyType: KeyType.Ed25519 }) - const indyDid = indyDidFromPublicKeyBase58(key.publicKeyBase58) + const unqualifiedIndyDid = legacyIndyDidFromPublicKeyBase58(key.publicKeyBase58) + const indyDid = `did:indy:pool1:${unqualifiedIndyDid}` // Create link secret await anonCredsHolderService.createLinkSecret(agentContext, { @@ -155,6 +162,12 @@ describe('Legacy indy format services', () => { }), ] + const cd = parseCredentialDefinitionId(credentialDefinitionState.credentialDefinitionId) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.didIdentifier, cd.schemaSeqNo, cd.tag) + + const s = parseSchemaId(schemaState.schemaId) + const legacySchemaId = getLegacySchemaId(s.didIdentifier, s.schemaName, s.schemaVersion) + // Holder creates proposal holderCredentialRecord.credentialAttributes = credentialAttributes const { attachment: proposalAttachment } = await indyCredentialFormatService.createProposal(agentContext, { @@ -162,7 +175,7 @@ describe('Legacy indy format services', () => { credentialFormats: { indy: { attributes: credentialAttributes, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialDefinitionId: legacyCredentialDefinitionId, }, }, }) @@ -225,16 +238,16 @@ describe('Legacy indy format services', () => { age: '25', name: 'John', }, - schemaId: schemaState.schemaId, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + schemaId: legacySchemaId, + credentialDefinitionId: legacyCredentialDefinitionId, revocationRegistryId: null, credentialRevocationId: null, }) expect(holderCredentialRecord.metadata.data).toEqual({ '_anonCreds/anonCredsCredential': { - schemaId: schemaState.schemaId, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + schemaId: legacySchemaId, + credentialDefinitionId: legacyCredentialDefinitionId, }, '_anonCreds/anonCredsCredentialRequest': { master_secret_blinding_data: expect.any(Object), @@ -245,8 +258,8 @@ describe('Legacy indy format services', () => { expect(issuerCredentialRecord.metadata.data).toEqual({ '_anonCreds/anonCredsCredential': { - schemaId: schemaState.schemaId, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + schemaId: legacySchemaId, + credentialDefinitionId: legacyCredentialDefinitionId, }, }) @@ -267,14 +280,14 @@ describe('Legacy indy format services', () => { attributes: [ { name: 'name', - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialDefinitionId: legacyCredentialDefinitionId, value: 'John', referent: '1', }, ], predicates: [ { - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialDefinitionId: legacyCredentialDefinitionId, name: 'age', predicate: '>=', threshold: 18, diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index 18bd9cfaab..abd3ddd6ed 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -19,6 +19,15 @@ import type { AgentContext } from '@aries-framework/core' import { Hasher, TypedArrayEncoder } from '@aries-framework/core' import BigNumber from 'bn.js' +import { + getDidIndyCredentialDefinitionId, + getDidIndySchemaId, + getLegacyCredentialDefinitionId, + getLegacySchemaId, + parseSchemaId, +} from '../../indy-sdk/src/anoncreds/utils/identifiers' +import { parseIndyDid } from '../../indy-sdk/src/dids/didIndyUtil' + /** * In memory implementation of the {@link AnonCredsRegistry} interface. Useful for testing. */ @@ -26,7 +35,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { // Roughly match that the identifier starts with an unqualified indy did. Once the // anoncreds tests are not based on the indy-sdk anymore, we can use any identifier // we want, but the indy-sdk is picky about the identifier format. - public readonly supportedIdentifier = /^[a-zA-Z0-9]{21,22}/ + public readonly supportedIdentifier = /.+/ private schemas: Record private credentialDefinitions: Record @@ -52,7 +61,11 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { public async getSchema(agentContext: AgentContext, schemaId: string): Promise { const schema = this.schemas[schemaId] - const indyLedgerSeqNo = getSeqNoFromSchemaId(schemaId) + + const parsed = parseSchemaId(schemaId) + + const legacySchemaId = getLegacySchemaId(parsed.didIdentifier, parsed.schemaName, parsed.schemaVersion) + const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) if (!schema) { return { @@ -81,10 +94,17 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { agentContext: AgentContext, options: RegisterSchemaOptions ): Promise { - const schemaId = `${options.schema.issuerId}:2:${options.schema.name}:${options.schema.version}` - const indyLedgerSeqNo = getSeqNoFromSchemaId(schemaId) + const { id: didIdentifier, namespace } = parseIndyDid(options.schema.issuerId) + const didIndySchemaId = getDidIndySchemaId(namespace, didIdentifier, options.schema.name, options.schema.version) + const legacySchemaId = getLegacySchemaId(didIdentifier, options.schema.name, options.schema.version) - this.schemas[schemaId] = options.schema + const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) + + this.schemas[didIndySchemaId] = options.schema + this.schemas[legacySchemaId] = { + ...options.schema, + issuerId: didIdentifier, + } return { registrationMetadata: {}, @@ -96,7 +116,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { schemaState: { state: 'finished', schema: options.schema, - schemaId, + schemaId: didIndySchemaId, }, } } @@ -130,10 +150,34 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { agentContext: AgentContext, options: RegisterCredentialDefinitionOptions ): Promise { - const indyLedgerSeqNo = getSeqNoFromSchemaId(options.credentialDefinition.schemaId) - const credentialDefinitionId = `${options.credentialDefinition.issuerId}:3:CL:${indyLedgerSeqNo}:${options.credentialDefinition.tag}` - - this.credentialDefinitions[credentialDefinitionId] = options.credentialDefinition + const parsedSchema = parseSchemaId(options.credentialDefinition.schemaId) + const legacySchemaId = getLegacySchemaId( + parsedSchema.didIdentifier, + parsedSchema.schemaName, + parsedSchema.schemaVersion + ) + const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) + + const { id: didIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) + + const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( + namespace, + didIdentifier, + indyLedgerSeqNo, + options.credentialDefinition.tag + ) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( + didIdentifier, + indyLedgerSeqNo, + options.credentialDefinition.tag + ) + + this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition + this.credentialDefinitions[legacyCredentialDefinitionId] = { + ...options.credentialDefinition, + issuerId: didIdentifier, + schemaId: legacySchemaId, + } return { registrationMetadata: {}, @@ -141,7 +185,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { credentialDefinitionState: { state: 'finished', credentialDefinition: options.credentialDefinition, - credentialDefinitionId, + credentialDefinitionId: didIndyCredentialDefinitionId, }, } } diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index 127bbee586..1f4c32f973 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -123,7 +123,7 @@ describe('AnonCreds API', () => { options: {}, schema: { attrNames: ['name', 'age'], - issuerId: '6xDN7v3AiGgusRp4bqZACZ', + issuerId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ', name: 'Employee Credential', version: '1.0.0', }, @@ -136,11 +136,11 @@ describe('AnonCreds API', () => { state: 'finished', schema: { attrNames: ['name', 'age'], - issuerId: '6xDN7v3AiGgusRp4bqZACZ', + issuerId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ', name: 'Employee Credential', version: '1.0.0', }, - schemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', + schemaId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0', }, }) @@ -148,22 +148,22 @@ describe('AnonCreds API', () => { const anonCredsSchemaRepository = agent.dependencyManager.resolve(AnonCredsSchemaRepository) const schemaRecord = await anonCredsSchemaRepository.getBySchemaId( agent.context, - '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0' + 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0' ) expect(schemaRecord).toMatchObject({ - schemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', + schemaId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0', schema: { attrNames: ['name', 'age'], - issuerId: '6xDN7v3AiGgusRp4bqZACZ', + issuerId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ', name: 'Employee Credential', version: '1.0.0', }, }) expect(schemaRecord.getTags()).toEqual({ - schemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', - issuerId: '6xDN7v3AiGgusRp4bqZACZ', + schemaId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0', + issuerId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ', schemaName: 'Employee Credential', schemaVersion: '1.0.0', }) @@ -192,7 +192,7 @@ describe('AnonCreds API', () => { keyType: KeyType.Ed25519, }) - const issuerId = 'VsKV7grR1BUE29mG2Fm2kX' + const issuerId = 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX' const credentialDefinitionResult = await agent.modules.anoncreds.registerCredentialDefinition({ credentialDefinition: { @@ -209,7 +209,7 @@ describe('AnonCreds API', () => { credentialDefinitionState: { state: 'finished', credentialDefinition: { - issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + issuerId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX', tag: 'TAG', schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', type: 'CL', @@ -227,7 +227,7 @@ describe('AnonCreds API', () => { }, }, }, - credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + credentialDefinitionId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG', }, }) @@ -237,13 +237,13 @@ describe('AnonCreds API', () => { ) const credentialDefinitionRecord = await anonCredsCredentialDefinitionRepository.getByCredentialDefinitionId( agent.context, - 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG' + 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG' ) expect(credentialDefinitionRecord).toMatchObject({ - credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + credentialDefinitionId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG', credentialDefinition: { - issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + issuerId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX', tag: 'TAG', schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', type: 'CL', @@ -264,9 +264,9 @@ describe('AnonCreds API', () => { }) expect(credentialDefinitionRecord.getTags()).toEqual({ - credentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', + credentialDefinitionId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG', schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', - issuerId: 'VsKV7grR1BUE29mG2Fm2kX', + issuerId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX', tag: 'TAG', }) }) diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 32a876fd1a..5895e5c075 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -48,10 +48,17 @@ import { import testLogger from '../../core/tests/logger' import { IndySdkAnonCredsRegistry, + IndySdkIndyDidRegistrar, + IndySdkIndyDidResolver, IndySdkModule, - IndySdkSovDidRegistrar, IndySdkSovDidResolver, } from '../../indy-sdk/src' +import { + getLegacyCredentialDefinitionId, + getLegacySchemaId, + parseCredentialDefinitionId, + parseSchemaId, +} from '../../indy-sdk/src/anoncreds/utils/identifiers' import { getIndySdkModuleConfig } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrAnonCredsRegistry, IndyVdrSovDidResolver, IndyVdrModule } from '../../indy-vdr/src' import { @@ -98,8 +105,8 @@ export const getLegacyAnonCredsModules = ({ registries: [new IndySdkAnonCredsRegistry()], }), dids: new DidsModule({ - resolvers: [new IndySdkSovDidResolver()], - registrars: [new IndySdkSovDidRegistrar()], + resolvers: [new IndySdkSovDidResolver(), new IndySdkIndyDidResolver()], + registrars: [new IndySdkIndyDidRegistrar()], }), indySdk: new IndySdkModule(getIndySdkModuleConfig()), cache: new CacheModule({ @@ -444,14 +451,15 @@ export async function setupAnonCredsTests< export async function prepareForAnonCredsIssuance(agent: Agent, { attributeNames }: { attributeNames: string[] }) { // Add existing endorser did to the wallet - const issuerId = await importExistingIndyDidFromPrivateKey(agent, TypedArrayEncoder.fromString(publicDidSeed)) + const unqualifiedDid = await importExistingIndyDidFromPrivateKey(agent, TypedArrayEncoder.fromString(publicDidSeed)) + const didIndyDid = `did:indy:pool:localtest:${unqualifiedDid}` const schema = await registerSchema(agent, { // TODO: update attrNames to attributeNames attrNames: attributeNames, name: `Schema ${randomUUID()}`, version: '1.0', - issuerId, + issuerId: didIndyDid, }) // Wait some time pass to let ledger settle the object @@ -459,16 +467,31 @@ export async function prepareForAnonCredsIssuance(agent: Agent, { attributeNames const credentialDefinition = await registerCredentialDefinition(agent, { schemaId: schema.schemaId, - issuerId, + issuerId: didIndyDid, tag: 'default', }) + const s = parseSchemaId(schema.schemaId) + const cd = parseCredentialDefinitionId(credentialDefinition.credentialDefinitionId) + + const legacySchemaId = getLegacySchemaId(s.didIdentifier, s.schemaName, s.schemaVersion) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.didIdentifier, cd.schemaSeqNo, cd.tag) + // Wait some time pass to let ledger settle the object await sleep(1000) + // NOTE: we return the legacy schema and credential definition ids here because that's what currently expected + // in all tests. If we also support did:indy in tests we probably want to return the qualified identifiers here + // and transform them to the legacy variant in the specific tests that need it. return { - schema, - credentialDefinition, + schema: { + ...schema, + schemaId: legacySchemaId, + }, + credentialDefinition: { + ...credentialDefinition, + credentialDefinitionId: legacyCredentialDefinitionId, + }, } } @@ -478,9 +501,7 @@ async function registerSchema( ): Promise { const { schemaState } = await agent.modules.anoncreds.registerSchema({ schema, - options: { - didIndyNamespace: 'pool:localtest', - }, + options: {}, }) testLogger.test(`created schema with id ${schemaState.schemaId}`, schema) @@ -500,9 +521,7 @@ async function registerCredentialDefinition( ): Promise { const { credentialDefinitionState } = await agent.modules.anoncreds.registerCredentialDefinition({ credentialDefinition, - options: { - didIndyNamespace: 'pool:localtest', - }, + options: {}, }) if (credentialDefinitionState.state !== 'finished') { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 8c0956a145..6c514127ce 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -12,8 +12,9 @@ import { import { prepareForAnonCredsIssuance } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import { IndySdkAnonCredsRegistry, + IndySdkIndyDidRegistrar, + IndySdkIndyDidResolver, IndySdkModule, - IndySdkSovDidRegistrar, IndySdkSovDidResolver, } from '../../../../../../../indy-sdk/src' import { indySdk } from '../../../../../../../indy-sdk/tests/setupIndySdkModule' @@ -107,8 +108,8 @@ const getIndyJsonLdModules = () => registries: [new IndySdkAnonCredsRegistry()], }), dids: new DidsModule({ - resolvers: [new IndySdkSovDidResolver(), new KeyDidResolver()], - registrars: [new IndySdkSovDidRegistrar(), new KeyDidRegistrar()], + resolvers: [new IndySdkSovDidResolver(), new IndySdkIndyDidResolver(), new KeyDidResolver()], + registrars: [new IndySdkIndyDidRegistrar(), new KeyDidRegistrar()], }), indySdk: new IndySdkModule({ indySdk, diff --git a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts index ae0c98e6d7..846a139a87 100644 --- a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { IndySdkSovDidCreateOptions } from '@aries-framework/indy-sdk' +import type { IndySdkIndyDidCreateOptions } from '@aries-framework/indy-sdk' import { getLegacyAnonCredsModules } from '../../../../../anoncreds/tests/legacyAnonCredsSetup' import { setupSubjectTransports } from '../../../../tests' @@ -198,10 +198,10 @@ describe('out of band implicit', () => { }) async function createPublicDid(agent: Agent, unqualifiedSubmitterDid: string, endpoint: string) { - const createResult = await agent.dids.create({ - method: 'sov', + const createResult = await agent.dids.create({ + method: 'indy', options: { - submitterVerificationMethod: `did:sov:${unqualifiedSubmitterDid}#key-1`, + submitterDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, alias: 'Alias', endpoints: { endpoint, diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 2dbf79cd53..fe0d30e35b 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -1,3 +1,4 @@ +import type { IndySdkPool } from '../../ledger' import type { IndySdk } from '../../types' import type { AnonCredsRegistry, @@ -13,23 +14,22 @@ import type { import type { AgentContext } from '@aries-framework/core' import type { Schema as IndySdkSchema } from 'indy-sdk' -import { DidsApi, getKeyFromVerificationMethod } from '@aries-framework/core' - +import { parseIndyDid, verificationKeyForIndyDid } from '../../dids/didIndyUtil' import { IndySdkError, isIndyError } from '../../error' import { IndySdkPoolService } from '../../ledger' import { IndySdkSymbol } from '../../types' import { - didFromCredentialDefinitionId, - didFromRevocationRegistryDefinitionId, - didFromSchemaId, + getDidIndyCredentialDefinitionId, + getDidIndySchemaId, getLegacyCredentialDefinitionId, + getLegacyRevocationRegistryId, getLegacySchemaId, indySdkAnonCredsRegistryIdentifierRegex, + parseCredentialDefinitionId, + parseRevocationRegistryId, + parseSchemaId, } from '../utils/identifiers' -import { - anonCredsRevocationStatusListFromIndySdk, - anonCredsRevocationRegistryDefinitionFromIndySdk, -} from '../utils/transform' +import { anonCredsRevocationStatusListFromIndySdk } from '../utils/transform' /** * TODO: validation of the identifiers. The Indy SDK classes only support the legacy (unqualified) identifiers. @@ -47,11 +47,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const did = didFromSchemaId(schemaId) + // parse schema id (supports did:indy and legacy) + const { did, didIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`) - const request = await indySdk.buildGetSchemaRequest(null, schemaId) + // even though we support did:indy and legacy identifiers we always need to fetch using the legacy identifier + const legacySchemaId = getLegacySchemaId(didIdentifier, schemaName, schemaVersion) + const request = await indySdk.buildGetSchemaRequest(null, legacySchemaId) agentContext.config.logger.trace( `Submitting get schema request for schema '${schemaId}' to ledger '${pool.didIndyNamespace}'` @@ -67,16 +70,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { schema, }) - const issuerId = didFromSchemaId(schema.id) - return { schema: { attrNames: schema.attrNames, name: schema.name, version: schema.version, - issuerId: issuerId, + issuerId: did, }, - schemaId: schema.id, + schemaId, resolutionMetadata: {}, schemaMetadata: { didIndyNamespace: pool.didIndyNamespace, @@ -104,62 +105,37 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { public async registerSchema( agentContext: AgentContext, - options: IndySdkRegisterSchemaOptions + options: RegisterSchemaOptions ): Promise { - // Make sure didIndyNamespace is passed - if (!options.options.didIndyNamespace) { - return { - schemaMetadata: {}, - registrationMetadata: {}, - schemaState: { - reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy SDK', - schema: options.schema, - state: 'failed', - }, - } - } - try { + // This will throw an error if trying to register a schema with a legacy indy identifier. We only support did:indy identifiers + // for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. + const { id: unqualifiedDid, namespace } = parseIndyDid(options.schema.issuerId) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const pool = indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) + const pool = indySdkPoolService.getPoolForNamespace(namespace) agentContext.config.logger.debug( `Register schema on ledger '${pool.didIndyNamespace}' with did '${options.schema.issuerId}'`, options.schema ) + const didIndySchemaId = getDidIndySchemaId(namespace, unqualifiedDid, options.schema.name, options.schema.version) + const legacySchemaId = getLegacySchemaId(unqualifiedDid, options.schema.name, options.schema.version) + const schema = { attrNames: options.schema.attrNames, name: options.schema.name, version: options.schema.version, - id: getLegacySchemaId(options.schema.issuerId, options.schema.name, options.schema.version), + id: legacySchemaId, ver: '1.0', // Casted as because the type expect a seqNo, but that's not actually required for the input of // buildSchemaRequest (seqNo is not yet known) } as IndySdkSchema - const request = await indySdk.buildSchemaRequest(options.schema.issuerId, schema) - - // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did - // from the ledger to know which key is associated with the did - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - const didResult = await didsApi.resolve(`did:sov:${options.schema.issuerId}`) - - if (!didResult.didDocument) { - return { - schemaMetadata: {}, - registrationMetadata: {}, - schemaState: { - schema: options.schema, - state: 'failed', - reason: `didNotFound: unable to resolve did did:sov:${options.schema.issuerId}: ${didResult.didResolutionMetadata.message}`, - }, - } - } - - const verificationMethod = didResult.didDocument.dereferenceKey(`did:sov:${options.schema.issuerId}#key-1`) - const submitterKey = getKeyFromVerificationMethod(verificationMethod) + const request = await indySdk.buildSchemaRequest(unqualifiedDid, schema) + const submitterKey = await verificationKeyForIndyDid(agentContext, options.schema.issuerId) const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) agentContext.config.logger.debug(`Registered schema '${schema.id}' on ledger '${pool.didIndyNamespace}'`, { @@ -176,14 +152,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { name: schema.name, version: schema.version, }, - schemaId: schema.id, + schemaId: didIndySchemaId, }, registrationMetadata: {}, schemaMetadata: { // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. // For this reason we return it in the metadata. indyLedgerSeqNo: response.result.txnMetadata.seqNo, - didIndyNamespace: pool.didIndyNamespace, }, } } catch (error) { @@ -213,13 +188,16 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const did = didFromCredentialDefinitionId(credentialDefinitionId) + // we support did:indy and legacy identifiers + const { did, didIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve credential definition '${credentialDefinitionId}'` ) - const request = await indySdk.buildGetCredDefRequest(null, credentialDefinitionId) + + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(didIdentifier, schemaSeqNo, tag) + const request = await indySdk.buildGetCredDefRequest(null, legacyCredentialDefinitionId) agentContext.config.logger.trace( `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.didIndyNamespace}'` @@ -234,7 +212,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) const [, credentialDefinition] = await indySdk.parseGetCredDefResponse(response) - const { schema } = await this.fetchIndySchemaWithSeqNo(agentContext, Number(credentialDefinition.schemaId), did) + const { schema } = await this.fetchIndySchemaWithSeqNo(agentContext, pool, Number(credentialDefinition.schemaId)) if (credentialDefinition && schema) { agentContext.config.logger.debug( @@ -244,11 +222,16 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } ) + // Format the schema id based on the type of the credential definition id + const schemaId = credentialDefinitionId.startsWith('did:indy') + ? getDidIndySchemaId(pool.didIndyNamespace, didIdentifier, schema.name, schema.version) + : schema.schemaId + return { - credentialDefinitionId: credentialDefinition.id, + credentialDefinitionId, credentialDefinition: { - issuerId: didFromCredentialDefinitionId(credentialDefinition.id), - schemaId: schema.schemaId, + issuerId: did, + schemaId, tag: credentialDefinition.tag, type: 'CL', value: credentialDefinition.value, @@ -291,31 +274,23 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { public async registerCredentialDefinition( agentContext: AgentContext, - options: IndySdkRegisterCredentialDefinitionOptions + options: RegisterCredentialDefinitionOptions ): Promise { - // Make sure didIndyNamespace is passed - if (!options.options.didIndyNamespace) { - return { - credentialDefinitionMetadata: {}, - registrationMetadata: {}, - credentialDefinitionState: { - reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy SDK', - credentialDefinition: options.credentialDefinition, - state: 'failed', - }, - } - } - try { + // This will throw an error if trying to register a credential defintion with a legacy indy identifier. We only support did:indy + // identifiers for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. + const { id: unqualifiedDid, namespace } = parseIndyDid(options.credentialDefinition.issuerId) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const pool = indySdkPoolService.getPoolForNamespace(options.options.didIndyNamespace) + const pool = indySdkPoolService.getPoolForNamespace(namespace) agentContext.config.logger.debug( `Registering credential definition on ledger '${pool.didIndyNamespace}' with did '${options.credentialDefinition.issuerId}'`, options.credentialDefinition ) + // TODO: check structure of the schemaId // TODO: this will bypass caching if done on a higher level. const { schema, schemaMetadata, resolutionMetadata } = await this.getSchema( agentContext, @@ -336,14 +311,20 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } } - const credentialDefinitionId = getLegacyCredentialDefinitionId( - options.credentialDefinition.issuerId, + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( + unqualifiedDid, + schemaMetadata.indyLedgerSeqNo, + options.credentialDefinition.tag + ) + const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( + namespace, + unqualifiedDid, schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag ) - const request = await indySdk.buildCredDefRequest(options.credentialDefinition.issuerId, { - id: credentialDefinitionId, + const request = await indySdk.buildCredDefRequest(unqualifiedDid, { + id: legacyCredentialDefinitionId, // Indy ledger requires the credential schemaId to be a string of the schema seqNo. schemaId: schemaMetadata.indyLedgerSeqNo.toString(), tag: options.credentialDefinition.tag, @@ -352,32 +333,12 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ver: '1.0', }) - // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did - // from the ledger to know which key is associated with the did - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - const didResult = await didsApi.resolve(`did:sov:${options.credentialDefinition.issuerId}`) - - if (!didResult.didDocument) { - return { - credentialDefinitionMetadata: {}, - registrationMetadata: {}, - credentialDefinitionState: { - credentialDefinition: options.credentialDefinition, - state: 'failed', - reason: `didNotFound: unable to resolve did did:sov:${options.credentialDefinition.issuerId}: ${didResult.didResolutionMetadata.message}`, - }, - } - } - - const verificationMethod = didResult.didDocument.dereferenceKey( - `did:sov:${options.credentialDefinition.issuerId}#key-1` - ) - const submitterKey = getKeyFromVerificationMethod(verificationMethod) + const submitterKey = await verificationKeyForIndyDid(agentContext, options.credentialDefinition.issuerId) const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) agentContext.config.logger.debug( - `Registered credential definition '${credentialDefinitionId}' on ledger '${pool.didIndyNamespace}'`, + `Registered credential definition '${didIndyCredentialDefinitionId}' on ledger '${pool.didIndyNamespace}'`, { response, credentialDefinition: options.credentialDefinition, @@ -385,12 +346,10 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) return { - credentialDefinitionMetadata: { - didIndyNamespace: pool.didIndyNamespace, - }, + credentialDefinitionMetadata: {}, credentialDefinitionState: { credentialDefinition: options.credentialDefinition, - credentialDefinitionId, + credentialDefinitionId: didIndyCredentialDefinitionId, state: 'finished', }, registrationMetadata: {}, @@ -417,13 +376,21 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) + const { did, didIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = + parseRevocationRegistryId(revocationRegistryDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` ) - const request = await indySdk.buildGetRevocRegDefRequest(null, revocationRegistryDefinitionId) + + const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + didIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ) + const request = await indySdk.buildGetRevocRegDefRequest(null, legacyRevocationRegistryId) agentContext.config.logger.trace( `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` @@ -445,9 +412,24 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } ) + const credentialDefinitionId = revocationRegistryDefinitionId.startsWith('did:indy:') + ? getDidIndyCredentialDefinitionId(pool.didIndyNamespace, didIdentifier, schemaSeqNo, credentialDefinitionTag) + : getLegacyCredentialDefinitionId(didIdentifier, schemaSeqNo, credentialDefinitionTag) + return { resolutionMetadata: {}, - revocationRegistryDefinition: anonCredsRevocationRegistryDefinitionFromIndySdk(revocationRegistryDefinition), + revocationRegistryDefinition: { + issuerId: did, + credDefId: credentialDefinitionId, + value: { + maxCredNum: revocationRegistryDefinition.value.maxCredNum, + publicKeys: revocationRegistryDefinition.value.publicKeys, + tailsHash: revocationRegistryDefinition.value.tailsHash, + tailsLocation: revocationRegistryDefinition.value.tailsLocation, + }, + tag: revocationRegistryDefinition.tag, + revocDefType: 'CL_ACCUM', + }, revocationRegistryDefinitionId, revocationRegistryDefinitionMetadata: { issuanceType: revocationRegistryDefinition.value.issuanceType, @@ -483,15 +465,23 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const did = didFromRevocationRegistryDefinitionId(revocationRegistryId) + const { did, didIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = + parseRevocationRegistryId(revocationRegistryId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` ) + const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + didIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ) + // TODO: implement caching for returned deltas - const request = await indySdk.buildGetRevocRegDeltaRequest(null, revocationRegistryId, 0, timestamp) + const request = await indySdk.buildGetRevocRegDeltaRequest(null, legacyRevocationRegistryId, 0, timestamp) agentContext.config.logger.trace( `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` @@ -567,14 +557,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } } - private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, seqNo: number, did: string) { + private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, pool: IndySdkPool, seqNo: number) { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting transaction with seqNo '${seqNo}' from ledger '${pool.didIndyNamespace}'`) - const request = await indySdk.buildGetTxnRequest(did, 'DOMAIN', seqNo) + const request = await indySdk.buildGetTxnRequest(null, 'DOMAIN', seqNo) agentContext.config.logger.trace(`Submitting get transaction request to ledger '${pool.didIndyNamespace}'`) const response = await indySdkPoolService.submitReadRequest(pool, request) @@ -586,15 +575,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { return {} } - const schemaId = getLegacySchemaId(did, schema.txn.data.data.name, schema.txn.data.data.version) - return { schema: { - schemaId, + // txnId is the schema id + schemaId: schema.txnMetadata.txnId, attr_name: schema.txn.data.data.attr_names, name: schema.txn.data.data.name, version: schema.txn.data.data.version, - issuerId: did, + issuerId: schema.txn.metadata.from, seqNo, }, indyNamespace: pool.didIndyNamespace, @@ -603,7 +591,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } interface SchemaType { + txnMetadata: { + txnId: string + } txn: { + metadata: { + from: string + } data: { data: { attr_names: string[] @@ -615,15 +609,3 @@ interface SchemaType { type: string } } - -export interface IndySdkRegisterSchemaOptions extends RegisterSchemaOptions { - options: { - didIndyNamespace: string - } -} - -export interface IndySdkRegisterCredentialDefinitionOptions extends RegisterCredentialDefinitionOptions { - options: { - didIndyNamespace: string - } -} diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index e00718c62c..d54a46a016 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -31,7 +31,7 @@ import { AriesFrameworkError, injectable, inject, utils } from '@aries-framework import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' -import { getIndySeqNoFromUnqualifiedCredentialDefinitionId } from '../utils/identifiers' +import { parseCredentialDefinitionId } from '../utils/identifiers' import { indySdkCredentialDefinitionFromAnonCreds, indySdkRevocationRegistryDefinitionFromAnonCreds, @@ -105,8 +105,8 @@ export class IndySdkHolderService implements AnonCredsHolderService { ) // Get the seqNo for the schemas so we can use it when transforming the schemas - const schemaSeqNo = getIndySeqNoFromUnqualifiedCredentialDefinitionId(credentialDefinitionId) - seqNoMap[credentialDefinition.schemaId] = schemaSeqNo + const { schemaSeqNo } = parseCredentialDefinitionId(credentialDefinitionId) + seqNoMap[credentialDefinition.schemaId] = Number(schemaSeqNo) } // Convert AnonCreds schemas to Indy schemas diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index d0d0a796d1..2b5365522b 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -15,9 +15,11 @@ import type { AgentContext } from '@aries-framework/core' import { generateLegacyProverDidLikeString } from '@aries-framework/anoncreds' import { injectable, AriesFrameworkError, inject } from '@aries-framework/core' +import { parseIndyDid } from '../../dids/didIndyUtil' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' +import { getLegacySchemaId } from '../utils/identifiers' import { createTailsReader } from '../utils/tails' import { indySdkSchemaFromAnonCreds } from '../utils/transform' @@ -30,11 +32,14 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { } public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { - const { issuerId, name, version, attrNames } = options + // We only support passing qualified did:indy issuer ids in the indy issuer service when creating objects + const { id: unqualifiedDid } = parseIndyDid(options.issuerId) + + const { name, version, attrNames, issuerId } = options assertIndySdkWallet(agentContext.wallet) try { - const [, schema] = await this.indySdk.issuerCreateSchema(issuerId, name, version, attrNames) + const [, schema] = await this.indySdk.issuerCreateSchema(unqualifiedDid, name, version, attrNames) return { issuerId, @@ -54,6 +59,12 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { ): Promise { const { tag, supportRevocation, schema, issuerId, schemaId } = options + // We only support passing qualified did:indy issuer ids in the indy issuer service when creating objects + const { id: unqualifiedDid } = parseIndyDid(options.issuerId) + + // parse schema in a way that supports both unqualified and qualified identifiers + const legacySchemaId = getLegacySchemaId(unqualifiedDid, schema.name, schema.version) + if (!metadata) throw new AriesFrameworkError('The metadata parameter is required when using Indy, but received undefined.') @@ -61,8 +72,8 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { assertIndySdkWallet(agentContext.wallet) const [, credentialDefinition] = await this.indySdk.issuerCreateAndStoreCredentialDef( agentContext.wallet.handle, - issuerId, - indySdkSchemaFromAnonCreds(schemaId, schema, metadata.indyLedgerSchemaSeqNo), + unqualifiedDid, + indySdkSchemaFromAnonCreds(legacySchemaId, schema, metadata.indyLedgerSchemaSeqNo), tag, 'CL', { diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts index e4e4cb1d2d..b280256229 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -6,7 +6,7 @@ import { inject, injectable } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' -import { getIndySeqNoFromUnqualifiedCredentialDefinitionId } from '../utils/identifiers' +import { parseCredentialDefinitionId } from '../utils/identifiers' import { indySdkCredentialDefinitionFromAnonCreds, indySdkRevocationRegistryDefinitionFromAnonCreds, @@ -39,8 +39,8 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { ) // Get the seqNo for the schemas so we can use it when transforming the schemas - const schemaSeqNo = getIndySeqNoFromUnqualifiedCredentialDefinitionId(credentialDefinitionId) - seqNoMap[credentialDefinition.schemaId] = schemaSeqNo + const { schemaSeqNo } = parseCredentialDefinitionId(credentialDefinitionId) + seqNoMap[credentialDefinition.schemaId] = Number(schemaSeqNo) } // Convert AnonCreds schemas to Indy schemas diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts index 76454b615a..ca1751c4e2 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -1,31 +1,55 @@ import { - didFromSchemaId, - didFromCredentialDefinitionId, - didFromRevocationRegistryDefinitionId, - getIndySeqNoFromUnqualifiedCredentialDefinitionId, + getDidIndyCredentialDefinitionId, + getDidIndyRevocationRegistryId, + getDidIndySchemaId, getLegacyCredentialDefinitionId, + getLegacyRevocationRegistryId, getLegacySchemaId, indySdkAnonCredsRegistryIdentifierRegex, + parseCredentialDefinitionId, + parseRevocationRegistryId, + parseSchemaId, } from '../identifiers' describe('identifiers', () => { - it('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { - const did = '7Tqg6BwSSWapxgUDm9KKgg' - const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' - const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' - const revocationRegistryId = - 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' - - const anotherId = 'some:id' - - expect(indySdkAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) - expect(indySdkAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + describe('indySdkAnonCredsRegistryIdentifierRegex', () => { + test('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { + const did = '7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' + const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' + const revocationRegistryId = + 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + + const anotherId = 'some:id' + + // unqualified issuerId not in regex on purpose. See note in implementation. + expect(indySdkAnonCredsRegistryIdentifierRegex.test(did)).toEqual(false) + + expect(indySdkAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + + test('matches against a did indy did, schema id, credential definition id and revocation registry id', () => { + const did = 'did:indy:local:7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0' + const credentialDefinitionId = + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID' + const revocationRegistryId = + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/REV_REG_DEF/100669/SCH Employee ID/1-1024' + + const anotherId = 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/SOME_DEF' + + expect(indySdkAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indySdkAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) }) - it('getLegacySchemaId should return a valid schema id given a did, name, and version', () => { + test('getLegacySchemaId returns a valid schema id given a did, name, and version', () => { const did = '12345' const name = 'backbench' const version = '420' @@ -33,7 +57,7 @@ describe('identifiers', () => { expect(getLegacySchemaId(did, name, version)).toEqual('12345:2:backbench:420') }) - it('getLegacyCredentialDefinitionId should return a valid credential definition id given a did, seqNo, and tag', () => { + test('getLegacyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { const did = '12345' const seqNo = 420 const tag = 'someTag' @@ -41,25 +65,121 @@ describe('identifiers', () => { expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') }) - it('getIndySeqNoFromUnqualifiedCredentialDefinitionId should return the seqNo from the credential definition id', () => { - expect(getIndySeqNoFromUnqualifiedCredentialDefinitionId('12345:3:CL:420:someTag')).toEqual(420) + test('getLegacyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { + const did = '12345' + const seqNo = 420 + const credentialDefinitionTag = 'someTag' + const tag = 'anotherTag' + + expect(getLegacyRevocationRegistryId(did, seqNo, credentialDefinitionTag, tag)).toEqual( + '12345:4:12345:3:CL:420:someTag:CL_ACCUM:anotherTag' + ) + }) + + test('getDidIndySchemaId returns a valid schema id given a did, name, and version', () => { + const namespace = 'sovrin:test' + const did = '12345' + const name = 'backbench' + const version = '420' + + expect(getDidIndySchemaId(namespace, did, name, version)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/SCHEMA/backbench/420' + ) + }) + + test('getDidIndyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { + const namespace = 'sovrin:test' + const did = '12345' + const seqNo = 420 + const tag = 'someTag' + + expect(getDidIndyCredentialDefinitionId(namespace, did, seqNo, tag)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/CLAIM_DEF/420/someTag' + ) + }) + + test('getDidIndyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { + const namespace = 'sovrin:test' + const did = '12345' + const seqNo = 420 + const credentialDefinitionTag = 'someTag' + const tag = 'anotherTag' + + expect(getDidIndyRevocationRegistryId(namespace, did, seqNo, credentialDefinitionTag, tag)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag' + ) }) - it('didFromSchemaId should return the did from the schema id', () => { - const schemaId = '12345:2:backbench:420' + describe('parseSchemaId', () => { + test('parses legacy schema id', () => { + expect(parseSchemaId('SDqTzbVuCowusqGBNbNDjH:2:schema-name:1.0')).toEqual({ + did: 'SDqTzbVuCowusqGBNbNDjH', + didIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + schemaName: 'schema-name', + schemaVersion: '1.0', + }) + }) - expect(didFromSchemaId(schemaId)).toEqual('12345') + test('parses did:indy schema id', () => { + expect(parseSchemaId('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0')).toEqual( + { + didIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + did: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + schemaName: 'schema-name', + schemaVersion: '1.0', + namespace: 'bcovrin:test', + } + ) + }) }) - it('didFromCredentialDefinitionId should return the did from the credential definition id', () => { - const credentialDefinitionId = '12345:3:CL:420:someTag' + describe('parseCredentialDefinitionId', () => { + test('parses legacy credential definition id', () => { + expect(parseCredentialDefinitionId('TL1EaPFCZ8Si5aUrqScBDt:3:CL:10:TAG')).toEqual({ + did: 'TL1EaPFCZ8Si5aUrqScBDt', + didIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + schemaSeqNo: '10', + tag: 'TAG', + }) + }) - expect(didFromCredentialDefinitionId(credentialDefinitionId)).toEqual('12345') + test('parses did:indy credential definition id', () => { + expect( + parseCredentialDefinitionId('did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/10/TAG') + ).toEqual({ + didIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + did: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', + namespace: 'pool:localtest', + schemaSeqNo: '10', + tag: 'TAG', + }) + }) }) - it('didFromRevocationRegistryDefinitionId should return the did from the revocation registry id', () => { - const revocationRegistryId = '12345:3:CL:420:someTag' + describe('parseRevocationRegistryId', () => { + test('parses legacy revocation registry id', () => { + expect( + parseRevocationRegistryId('5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1') + ).toEqual({ + did: '5nDyJVP1NrcPAttP3xwMB9', + didIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + schemaSeqNo: '56495', + credentialDefinitionTag: 'npdb', + revocationRegistryTag: 'TAG1', + }) + }) - expect(didFromRevocationRegistryDefinitionId(revocationRegistryId)).toEqual('12345') + test('parses did:indy revocation registry id', () => { + expect( + parseRevocationRegistryId('did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_DEF/56495/npdb/TAG1') + ).toEqual({ + namespace: 'sovrin', + didIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + did: 'did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9', + schemaSeqNo: '56495', + credentialDefinitionTag: 'npdb', + revocationRegistryTag: 'TAG1', + }) + }) }) }) diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts index 7930bfb2fb..f94060d8fd 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/transform.test.ts @@ -12,7 +12,7 @@ describe('transform', () => { it('anonCredsSchemaFromIndySdk should return a valid anoncreds schema', () => { const schema: Schema = { attrNames: ['hello'], - id: '12345:2:Example Schema:1.0.0', + id: 'TL1EaPFCZ8Si5aUrqScBDt:2:Example Schema:1.0.0', name: 'Example Schema', seqNo: 150, ver: '1.0', @@ -21,24 +21,24 @@ describe('transform', () => { expect(anonCredsSchemaFromIndySdk(schema)).toEqual({ attrNames: ['hello'], - issuerId: '12345', + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', name: 'Example Schema', version: '1.0.0', }) }) it('indySdkSchemaFromAnonCreds should return a valid indy sdk schema', () => { - const schemaId = '12345:2:Example Schema:1.0.0' + const schemaId = 'TL1EaPFCZ8Si5aUrqScBDt:2:Example Schema:1.0.0' const schema: AnonCredsSchema = { attrNames: ['hello'], - issuerId: '12345', + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', name: 'Example Schema', version: '1.0.0', } expect(indySdkSchemaFromAnonCreds(schemaId, schema, 150)).toEqual({ attrNames: ['hello'], - id: '12345:2:Example Schema:1.0.0', + id: 'TL1EaPFCZ8Si5aUrqScBDt:2:Example Schema:1.0.0', name: 'Example Schema', seqNo: 150, ver: '1.0', @@ -48,7 +48,7 @@ describe('transform', () => { it('anonCredsCredentialDefinitionFromIndySdk should return a valid anoncreds credential definition', () => { const credDef: CredDef = { - id: '12345:3:CL:420:someTag', + id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:420:someTag', schemaId: '8910:2:Example Schema:1.0.0', tag: 'someTag', type: 'CL', @@ -61,7 +61,7 @@ describe('transform', () => { } expect(anonCredsCredentialDefinitionFromIndySdk(credDef)).toEqual({ - issuerId: '12345', + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', schemaId: '8910:2:Example Schema:1.0.0', tag: 'someTag', type: 'CL', @@ -74,9 +74,9 @@ describe('transform', () => { }) it('indySdkCredentialDefinitionFromAnonCreds should return a valid indy sdk credential definition', () => { - const credentialDefinitionId = '12345:3:CL:420:someTag' + const credentialDefinitionId = 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:420:someTag' const credentialDefinition: AnonCredsCredentialDefinition = { - issuerId: '12345', + issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', schemaId: '8910:2:Example Schema:1.0.0', tag: 'someTag', type: 'CL', @@ -88,7 +88,7 @@ describe('transform', () => { } expect(indySdkCredentialDefinitionFromAnonCreds(credentialDefinitionId, credentialDefinition)).toEqual({ - id: '12345:3:CL:420:someTag', + id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:420:someTag', schemaId: '8910:2:Example Schema:1.0.0', tag: 'someTag', type: 'CL', diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts index 62d2650602..8300a7ea29 100644 --- a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -1,52 +1,151 @@ -export const legacyIndyIssuerIdRegex = /^[a-zA-Z0-9]{21,22}$/ -export const legacyIndySchemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ -export const legacyIndyCredentialDefinitionIdRegex = - /^[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ -export const legacyIndyRevocationRegistryIdRegex = - /^[a-zA-Z0-9]{21,22}:4:[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)/ +import { DID_INDY_REGEX } from '../../utils/did' -export const indySdkAnonCredsRegistryIdentifierRegex = new RegExp( - `${legacyIndyIssuerIdRegex.source}|${legacyIndySchemaIdRegex.source}|${legacyIndyCredentialDefinitionIdRegex.source}|${legacyIndyRevocationRegistryIdRegex.source}` +const didIndyAnonCredsBase = + /(?did:indy:(?((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?)):(?([1-9A-HJ-NP-Za-km-z]{21,22})))\/anoncreds\/v0/ + +// did:indy::/anoncreds/v0/SCHEMA// +const didIndySchemaIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/SCHEMA/(?.+)/(?[0-9.]+)$` +) + +// :2:: +const legacyIndySchemaIdRegex = + /^(?(?[a-zA-Z0-9]{21,22})):2:(?.+):(?[0-9.]+)$/ + +// did:indy::/anoncreds/v0/CLAIM_DEF// +const didIndyCredentialDefinitionIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/CLAIM_DEF/(?[1-9][0-9]*)/(?.+)$` +) + +// :3:CL:: +const legacyIndyCredentialDefinitionIdRegex = + /^(?(?[a-zA-Z0-9]{21,22})):3:CL:(?[1-9][0-9]*):(?.+)$/ + +// did:indy::/anoncreds/v0/REV_REG_DEF/// +const didIndyRevocationRegistryIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/REV_REG_DEF/(?[1-9][0-9]*)/(?.+)/(?.+)$` ) -export function getIndySeqNoFromUnqualifiedCredentialDefinitionId(unqualifiedCredentialDefinitionId: string): number { - // 5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npbd - const [, , , seqNo] = unqualifiedCredentialDefinitionId.split(':') +// :4::3:CL::CL_ACCUM: +const legacyIndyRevocationRegistryIdRegex = + /^(?(?[a-zA-Z0-9]{21,22})):4:[a-zA-Z0-9]{21,22}:3:CL:(?[1-9][0-9]*):(?.+):CL_ACCUM:(?.+)$/ + +// combines both legacy and did:indy anoncreds identifiers and also the issuer id +const indySdkAnonCredsRegexes = [ + // NOTE: we only include the qualified issuer id here, as we don't support registering objects based on legacy issuer ids. + // you can still resolve using legacy issuer ids, but you need to use the full did:indy identifier when registering. + // As we find a matching anoncreds registry based on the issuerId only when creating an object, this will make sure + // it will throw an no registry found for identifier error. + // issuer id + DID_INDY_REGEX, + + // schema + didIndySchemaIdRegex, + legacyIndySchemaIdRegex, + + // credential definition + didIndyCredentialDefinitionIdRegex, + legacyIndyCredentialDefinitionIdRegex, + + // revocation registry + legacyIndyRevocationRegistryIdRegex, + didIndyRevocationRegistryIdRegex, +] + +export const indySdkAnonCredsRegistryIdentifierRegex = new RegExp( + indySdkAnonCredsRegexes.map((r) => r.source.replace(/(\?<[a-zA-Z]+>)?/g, '')).join('|') +) - return Number(seqNo) +export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, name: string, version: string) { + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/SCHEMA/${name}/${version}` } export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { return `${unqualifiedDid}:2:${name}:${version}` } -export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: number, tag: string) { +export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: string | number, tag: string) { return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` } -/** - * Extract did from schema id - */ -export function didFromSchemaId(schemaId: string) { - const [did] = schemaId.split(':') +export function getDidIndyCredentialDefinitionId( + namespace: string, + unqualifiedDid: string, + seqNo: string | number, + tag: string +) { + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/CLAIM_DEF/${seqNo}/${tag}` +} + +// TZQuLp43UcYTdtc3HewcDz:4:TZQuLp43UcYTdtc3HewcDz:3:CL:98158:BaustellenzertifikateNU1:CL_ACCUM:1-100 +export function getLegacyRevocationRegistryId( + unqualifiedDid: string, + seqNo: string | number, + credentialDefinitionTag: string, + revocationRegistryTag: string +) { + return `${unqualifiedDid}:4:${unqualifiedDid}:3:CL:${seqNo}:${credentialDefinitionTag}:CL_ACCUM:${revocationRegistryTag}` +} + +export function getDidIndyRevocationRegistryId( + namespace: string, + unqualifiedDid: string, + seqNo: string | number, + credentialDefinitionTag: string, + revocationRegistryTag: string +) { + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_DEF/${seqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` +} - return did +interface ParsedSchemaId { + did: string + didIdentifier: string + schemaName: string + schemaVersion: string + namespace?: string } -/** - * Extract did from credential definition id - */ -export function didFromCredentialDefinitionId(credentialDefinitionId: string) { - const [did] = credentialDefinitionId.split(':') +export function parseSchemaId(schemaId: string) { + const match = schemaId.match(didIndySchemaIdRegex) ?? schemaId.match(legacyIndySchemaIdRegex) + + if (!match) throw new Error(`Invalid schema id: ${schemaId}`) + + return match.groups as unknown as ParsedSchemaId +} - return did +interface ParsedCredentialDefinitionId { + did: string + didIdentifier: string + schemaSeqNo: string + tag: string + namespace?: string } -/** - * Extract did from revocation registry definition id - */ -export function didFromRevocationRegistryDefinitionId(revocationRegistryId: string) { - const [did] = revocationRegistryId.split(':') +export function parseCredentialDefinitionId(credentialDefinitionId: string) { + const match = + credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) ?? + credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) + + if (!match) throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) + + return match.groups as unknown as ParsedCredentialDefinitionId +} + +interface ParsedRevocationRegistryId { + did: string + didIdentifier: string + schemaSeqNo: string + credentialDefinitionTag: string + revocationRegistryTag: string + namespace?: string +} + +export function parseRevocationRegistryId(revocationRegistryId: string) { + const match = + revocationRegistryId.match(didIndyRevocationRegistryIdRegex) ?? + revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) + + if (!match) throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) - return did + return match.groups as unknown as ParsedRevocationRegistryId } diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index e976d514e4..a993475349 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -6,12 +6,12 @@ import type { } from '@aries-framework/anoncreds' import type { CredDef, RevocReg, RevocRegDef, RevocRegDelta, Schema } from 'indy-sdk' -import { didFromCredentialDefinitionId, didFromRevocationRegistryDefinitionId, didFromSchemaId } from './identifiers' +import { parseCredentialDefinitionId, parseSchemaId } from './identifiers' export function anonCredsSchemaFromIndySdk(schema: Schema): AnonCredsSchema { - const issuerId = didFromSchemaId(schema.id) + const { did } = parseSchemaId(schema.id) return { - issuerId, + issuerId: did, name: schema.name, version: schema.version, attrNames: schema.attrNames, @@ -30,10 +30,10 @@ export function indySdkSchemaFromAnonCreds(schemaId: string, schema: AnonCredsSc } export function anonCredsCredentialDefinitionFromIndySdk(credentialDefinition: CredDef): AnonCredsCredentialDefinition { - const issuerId = didFromCredentialDefinitionId(credentialDefinition.id) + const { did } = parseCredentialDefinitionId(credentialDefinition.id) return { - issuerId, + issuerId: did, schemaId: credentialDefinition.schemaId, tag: credentialDefinition.tag, type: 'CL', @@ -55,25 +55,6 @@ export function indySdkCredentialDefinitionFromAnonCreds( } } -export function anonCredsRevocationRegistryDefinitionFromIndySdk( - revocationRegistryDefinition: RevocRegDef -): AnonCredsRevocationRegistryDefinition { - const issuerId = didFromRevocationRegistryDefinitionId(revocationRegistryDefinition.id) - - return { - issuerId, - credDefId: revocationRegistryDefinition.credDefId, - value: { - maxCredNum: revocationRegistryDefinition.value.maxCredNum, - publicKeys: revocationRegistryDefinition.value.publicKeys, - tailsHash: revocationRegistryDefinition.value.tailsHash, - tailsLocation: revocationRegistryDefinition.value.tailsLocation, - }, - tag: revocationRegistryDefinition.tag, - revocDefType: 'CL_ACCUM', - } -} - export function indySdkRevocationRegistryDefinitionFromAnonCreds( revocationRegistryDefinitionId: string, revocationRegistryDefinition: AnonCredsRevocationRegistryDefinition diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts new file mode 100644 index 0000000000..77689dcde6 --- /dev/null +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts @@ -0,0 +1,315 @@ +import type { IndyEndpointAttrib } from './didSovUtil' +import type { IndySdkPool } from '../ledger' +import type { IndySdk } from '../types' +import type { + AgentContext, + Buffer, + DidCreateOptions, + DidCreateResult, + DidDeactivateResult, + DidRegistrar, + DidUpdateResult, +} from '@aries-framework/core' +import type { NymRole } from 'indy-sdk' + +import { DidDocumentRole, DidRecord, DidRepository, KeyType, Key } from '@aries-framework/core' + +import { IndySdkError } from '../error' +import { isIndyError } from '../error/indyError' +import { IndySdkPoolService } from '../ledger' +import { IndySdkSymbol } from '../types' +import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' +import { isLegacySelfCertifiedDid, legacyIndyDidFromPublicKeyBase58 } from '../utils/did' + +import { createKeyAgreementKey, indyDidDocumentFromDid, parseIndyDid, verificationKeyForIndyDid } from './didIndyUtil' +import { addServicesFromEndpointsAttrib } from './didSovUtil' + +export class IndySdkIndyDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['indy'] + + public async create(agentContext: AgentContext, options: IndySdkIndyDidCreateOptions): Promise { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + + const { alias, role, submitterDid, endpoints } = options.options + let did = options.did + let didIdentifier: string + let verificationKey: Key + const privateKey = options.secret?.privateKey + + if (did && privateKey) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Only one of 'privateKey' or 'did' must be provided`, + }, + } + } + + try { + assertIndySdkWallet(agentContext.wallet) + + // Parse submitterDid and extract namespace based on the submitter did + const { namespace: submitterNamespace, id: submitterDidIdentifier } = parseIndyDid(submitterDid) + const submitterSigningKey = await verificationKeyForIndyDid(agentContext, submitterDid) + + // Only supports version 1 did identifier (which is same as did:sov) + if (did) { + if (!options.options.verkey) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'If a did is defined, a matching verkey must be provided', + }, + } + } + + const { namespace, id } = parseIndyDid(did) + didIdentifier = id + verificationKey = Key.fromPublicKeyBase58(options.options.verkey, KeyType.Ed25519) + + if (!isLegacySelfCertifiedDid(didIdentifier, options.options.verkey)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Did must be first 16 bytes of the the verkey base58 encoded.`, + }, + } + } + + if (submitterNamespace !== namespace) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `The submitter did uses namespace ${submitterNamespace} and the did to register uses namespace ${namespace}. Namespaces must match.`, + }, + } + } + } else { + // Create a new key and calculate did according to the rules for indy did method + verificationKey = await agentContext.wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) + didIdentifier = legacyIndyDidFromPublicKeyBase58(verificationKey.publicKeyBase58) + did = `did:indy:${submitterNamespace}:${didIdentifier}` + } + + const pool = indySdkPoolService.getPoolForNamespace(submitterNamespace) + await this.registerPublicDid( + agentContext, + pool, + submitterDidIdentifier, + submitterSigningKey, + didIdentifier, + verificationKey, + alias, + role + ) + + // Create did document + const didDocumentBuilder = indyDidDocumentFromDid(did, verificationKey.publicKeyBase58) + + // Add services if endpoints object was passed. + if (endpoints) { + const keyAgreementId = `${did}#key-agreement-1` + + await this.setEndpointsForDid(agentContext, pool, didIdentifier, verificationKey, endpoints) + + didDocumentBuilder + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(verificationKey.publicKeyBase58), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) + + // Process endpoint attrib following the same rules as for did:sov + addServicesFromEndpointsAttrib(didDocumentBuilder, did, endpoints, keyAgreementId) + } + + // Build did document. + const didDocument = didDocumentBuilder.build() + + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + did, + role: DidDocumentRole.Created, + tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), + }, + }) + await didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did, + didDocument, + secret: { + // FIXME: the uni-registrar creates the seed in the registrar method + // if it doesn't exist so the seed can always be returned. Currently + // we can only return it if the seed was passed in by the user. Once + // we have a secure method for generating seeds we should use the same + // approach + privateKey: options.secret?.privateKey, + }, + }, + } + } catch (error) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async update(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:indy not implemented yet`, + }, + } + } + + public async deactivate(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:indy not implemented yet`, + }, + } + } + + public async registerPublicDid( + agentContext: AgentContext, + pool: IndySdkPool, + unqualifiedSubmitterDid: string, + submitterSigningKey: Key, + unqualifiedDid: string, + signingKey: Key, + alias: string, + role?: NymRole + ) { + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + + try { + agentContext.config.logger.debug(`Register public did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`) + + const request = await indySdk.buildNymRequest( + unqualifiedSubmitterDid, + unqualifiedDid, + signingKey.publicKeyBase58, + alias, + role || null + ) + + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterSigningKey) + + agentContext.config.logger.debug( + `Registered public did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, + { + response, + } + ) + } catch (error) { + agentContext.config.logger.error( + `Error registering public did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, + { + error, + unqualifiedSubmitterDid, + unqualifiedDid, + verkey: signingKey.publicKeyBase58, + alias, + role, + pool: pool.didIndyNamespace, + } + ) + + throw error + } + } + + public async setEndpointsForDid( + agentContext: AgentContext, + pool: IndySdkPool, + unqualifiedDid: string, + signingKey: Key, + endpoints: IndyEndpointAttrib + ): Promise { + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + + try { + agentContext.config.logger.debug( + `Set endpoints for did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, + endpoints + ) + + const request = await indySdk.buildAttribRequest( + unqualifiedDid, + unqualifiedDid, + null, + { endpoint: endpoints }, + null + ) + + const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, signingKey) + agentContext.config.logger.debug( + `Successfully set endpoints for did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, + { + response, + endpoints, + } + ) + } catch (error) { + agentContext.config.logger.error( + `Error setting endpoints for did '${unqualifiedDid}' on ledger '${pool.didIndyNamespace}'`, + { + error, + unqualifiedDid, + endpoints, + } + ) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} + +export interface IndySdkIndyDidCreateOptions extends DidCreateOptions { + method: 'indy' + did?: string + // The indy sdk can only publish a very limited did document (what is mostly known as a legacy did:sov did) and thus we require everything + // needed to construct the did document to be passed through the options object. + didDocument?: never + options: { + alias: string + role?: NymRole + verkey?: string + endpoints?: IndyEndpointAttrib + submitterDid: string + } + secret?: { + privateKey?: Buffer + } +} diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts new file mode 100644 index 0000000000..836ed8040b --- /dev/null +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts @@ -0,0 +1,122 @@ +import type { IndyEndpointAttrib } from './didSovUtil' +import type { IndySdkPool } from '../ledger' +import type { IndySdk } from '../types' +import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' + +import { isIndyError, IndySdkError } from '../error' +import { IndySdkPoolService } from '../ledger/IndySdkPoolService' +import { IndySdkSymbol } from '../types' +import { getFullVerkey } from '../utils/did' + +import { createKeyAgreementKey, indyDidDocumentFromDid, parseIndyDid } from './didIndyUtil' +import { addServicesFromEndpointsAttrib } from './didSovUtil' + +export class IndySdkIndyDidResolver implements DidResolver { + public readonly supportedMethods = ['indy'] + + public async resolve(agentContext: AgentContext, did: string): Promise { + const didDocumentMetadata = {} + + try { + const { id: unqualifiedDid, namespace } = parseIndyDid(did) + + const poolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const pool = poolService.getPoolForNamespace(namespace) + + const nym = await this.getPublicDid(agentContext, pool, unqualifiedDid) + const endpoints = await this.getEndpointsForDid(agentContext, pool, unqualifiedDid) + + // For modern did:indy DIDs, we assume that GET_NYM is always a full verkey in base58. + // For backwards compatibility, we accept a shortened verkey and convert it using previous convention + const verkey = getFullVerkey(did, nym.verkey) + const builder = indyDidDocumentFromDid(did, verkey) + + // NOTE: we don't support the `diddocContent` field in the GET_NYM response using the indy-sdk. So if the did would have the `diddocContent` field + // we will ignore it without knowing if it is present. We may be able to extract the diddocContent from the GET_NYM response in the future, but need + // some dids registered with diddocContent to test with. + if (endpoints) { + const keyAgreementId = `${did}#key-agreement-1` + + builder + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(verkey), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) + addServicesFromEndpointsAttrib(builder, did, endpoints, keyAgreementId) + } + + return { + didDocument: builder.build(), + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } + + private async getPublicDid(agentContext: AgentContext, pool: IndySdkPool, unqualifiedDid: string) { + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + + const request = await indySdk.buildGetNymRequest(null, unqualifiedDid) + const response = await indySdkPoolService.submitReadRequest(pool, request) + + return await indySdk.parseGetNymResponse(response) + } + + private async getEndpointsForDid(agentContext: AgentContext, pool: IndySdkPool, unqualifiedDid: string) { + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) + const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + + try { + agentContext.config.logger.debug( + `Get endpoints for did '${unqualifiedDid}' from ledger '${pool.didIndyNamespace}'` + ) + + const request = await indySdk.buildGetAttribRequest(null, unqualifiedDid, 'endpoint', null, null) + + agentContext.config.logger.debug( + `Submitting get endpoint ATTRIB request for did '${unqualifiedDid}' to ledger '${pool.didIndyNamespace}'` + ) + const response = await indySdkPoolService.submitReadRequest(pool, request) + + if (!response.result.data) { + return null + } + + const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib + agentContext.config.logger.debug( + `Got endpoints '${JSON.stringify(endpoints)}' for did '${unqualifiedDid}' from ledger '${ + pool.didIndyNamespace + }'`, + { + response, + endpoints, + } + ) + + return endpoints + } catch (error) { + agentContext.config.logger.error( + `Error retrieving endpoints for did '${unqualifiedDid}' from ledger '${pool.didIndyNamespace}'`, + { + error, + } + ) + + throw isIndyError(error) ? new IndySdkError(error) : error + } + } +} diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts deleted file mode 100644 index 86fb440e67..0000000000 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ /dev/null @@ -1,302 +0,0 @@ -import type { IndyEndpointAttrib } from './didSovUtil' -import type { IndySdkPool } from '../ledger' -import type { IndySdk } from '../types' -import type { - AgentContext, - DidRegistrar, - DidCreateOptions, - DidCreateResult, - DidDeactivateResult, - DidUpdateResult, - Buffer, - Key, -} from '@aries-framework/core' -import type { NymRole } from 'indy-sdk' - -import { - DidsApi, - KeyType, - isValidPrivateKey, - DidDocumentRole, - DidRecord, - DidRepository, - getKeyFromVerificationMethod, -} from '@aries-framework/core' - -import { IndySdkError } from '../error' -import { isIndyError } from '../error/indyError' -import { IndySdkPoolService } from '../ledger' -import { IndySdkSymbol } from '../types' -import { indyDidFromPublicKeyBase58 } from '../utils/did' - -import { addServicesFromEndpointsAttrib, sovDidDocumentFromDid } from './didSovUtil' - -export class IndySdkSovDidRegistrar implements DidRegistrar { - public readonly supportedMethods = ['sov'] - - public async create(agentContext: AgentContext, options: IndySdkSovDidCreateOptions): Promise { - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const didRepository = agentContext.dependencyManager.resolve(DidRepository) - - const { alias, role, submitterVerificationMethod, indyNamespace } = options.options - const privateKey = options.secret?.privateKey - - if (privateKey && !isValidPrivateKey(privateKey, KeyType.Ed25519)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid private key provided', - }, - } - } - - if (!submitterVerificationMethod.startsWith('did:sov:')) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Submitter did must be a valid did:sov did', - }, - } - } - - try { - const signingKey = await agentContext.wallet.createKey({ - privateKey, - keyType: KeyType.Ed25519, - }) - const verkey = signingKey.publicKeyBase58 - - const unqualifiedIndyDid = indyDidFromPublicKeyBase58(verkey) - - // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did - // from the ledger to know which key is associated with the did - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - const didResult = await didsApi.resolve(submitterVerificationMethod) - - if (!didResult.didDocument) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `didNotFound: unable to resolve did ${submitterVerificationMethod}: ${didResult.didResolutionMetadata.message}`, - }, - } - } - - const verificationMethod = didResult.didDocument.dereferenceKey(submitterVerificationMethod) - const submitterSigningKey = getKeyFromVerificationMethod(verificationMethod) - - const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` - const [unqualifiedSubmitterDid] = submitterVerificationMethod.replace('did:sov:', '').split('#') - - const pool = indySdkPoolService.getPoolForNamespace(indyNamespace) - await this.registerPublicDid( - agentContext, - unqualifiedSubmitterDid, - submitterSigningKey, - unqualifiedIndyDid, - signingKey, - alias, - pool, - role - ) - - // Create did document - const didDocumentBuilder = sovDidDocumentFromDid(qualifiedSovDid, verkey) - - // Add services if endpoints object was passed. - if (options.options.endpoints) { - await this.setEndpointsForDid(agentContext, unqualifiedIndyDid, signingKey, options.options.endpoints, pool) - addServicesFromEndpointsAttrib( - didDocumentBuilder, - qualifiedSovDid, - options.options.endpoints, - `${qualifiedSovDid}#key-agreement-1` - ) - } - - // Build did document. - const didDocument = didDocumentBuilder.build() - - const didIndyNamespace = pool.config.indyNamespace - const qualifiedIndyDid = `did:indy:${didIndyNamespace}:${unqualifiedIndyDid}` - - // Save the did so we know we created it and can issue with it - const didRecord = new DidRecord({ - did: qualifiedSovDid, - role: DidDocumentRole.Created, - tags: { - recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), - qualifiedIndyDid, - }, - }) - await didRepository.save(agentContext, didRecord) - - return { - didDocumentMetadata: { - qualifiedIndyDid, - }, - didRegistrationMetadata: { - didIndyNamespace, - }, - didState: { - state: 'finished', - did: qualifiedSovDid, - didDocument, - secret: { - // FIXME: the uni-registrar creates the seed in the registrar method - // if it doesn't exist so the seed can always be returned. Currently - // we can only return it if the seed was passed in by the user. Once - // we have a secure method for generating seeds we should use the same - // approach - privateKey: options.secret?.privateKey, - }, - }, - } - } catch (error) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `unknownError: ${error.message}`, - }, - } - } - } - - public async update(): Promise { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: updating did:sov not implemented yet`, - }, - } - } - - public async deactivate(): Promise { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: deactivating did:sov not implemented yet`, - }, - } - } - - public async registerPublicDid( - agentContext: AgentContext, - submitterDid: string, - submitterSigningKey: Key, - targetDid: string, - signingKey: Key, - alias: string, - pool: IndySdkPool, - role?: NymRole - ) { - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - - try { - agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`) - - const request = await indySdk.buildNymRequest( - submitterDid, - targetDid, - signingKey.publicKeyBase58, - alias, - role || null - ) - - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterSigningKey) - - agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`, { - response, - }) - - return targetDid - } catch (error) { - agentContext.config.logger.error( - `Error registering public did '${targetDid}' on ledger '${pool.didIndyNamespace}'`, - { - error, - submitterDid, - targetDid, - verkey: signingKey.publicKeyBase58, - alias, - role, - pool: pool.didIndyNamespace, - } - ) - - throw error - } - } - - public async setEndpointsForDid( - agentContext: AgentContext, - did: string, - signingKey: Key, - endpoints: IndyEndpointAttrib, - pool: IndySdkPool - ): Promise { - const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - - try { - agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, endpoints) - - const request = await indySdk.buildAttribRequest(did, did, null, { endpoint: endpoints }, null) - - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, signingKey) - agentContext.config.logger.debug( - `Successfully set endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, - { - response, - endpoints, - } - ) - } catch (error) { - agentContext.config.logger.error( - `Error setting endpoints for did '${did}' on ledger '${pool.didIndyNamespace}'`, - { - error, - did, - endpoints, - } - ) - - throw isIndyError(error) ? new IndySdkError(error) : error - } - } -} - -export interface IndySdkSovDidCreateOptions extends DidCreateOptions { - method: 'sov' - did?: undefined - // As did:sov is so limited, we require everything needed to construct the did document to be passed - // through the options object. Once we support did:indy we can allow the didDocument property. - didDocument?: never - options: { - alias: string - role?: NymRole - endpoints?: IndyEndpointAttrib - indyNamespace?: string - submitterVerificationMethod: string - } - secret?: { - privateKey?: Buffer - } -} - -// Update and Deactivate not supported for did:sov -export type IndySdkSovDidUpdateOptions = never -export type IndySdkSovDidDeactivateOptions = never diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts index 98007e5166..bbbeec71f8 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts @@ -1,4 +1,5 @@ import type { IndyEndpointAttrib } from './didSovUtil' +import type { IndySdkPool } from '../ledger' import type { IndySdk } from '../types' import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' @@ -15,8 +16,10 @@ export class IndySdkSovDidResolver implements DidResolver { const didDocumentMetadata = {} try { - const nym = await this.getPublicDid(agentContext, parsed.id) - const endpoints = await this.getEndpointsForDid(agentContext, parsed.id) + const poolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const { pool, nymResponse } = await poolService.getPoolForDid(agentContext, parsed.id) + const nym = nymResponse ?? (await this.getPublicDid(agentContext, pool, parsed.id)) + const endpoints = await this.getEndpointsForDid(agentContext, pool, parsed.id) const keyAgreementId = `${parsed.did}#key-agreement-1` const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) @@ -39,21 +42,20 @@ export class IndySdkSovDidResolver implements DidResolver { } } - private async getPublicDid(agentContext: AgentContext, did: string) { + private async getPublicDid(agentContext: AgentContext, pool: IndySdkPool, did: string) { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) + const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - // Getting the pool for a did also retrieves the DID. We can just use that - const { did: didResponse } = await indySdkPoolService.getPoolForDid(agentContext, did) + const request = await indySdk.buildGetNymRequest(null, did) + const response = await indySdkPoolService.submitReadRequest(pool, request) - return didResponse + return await indySdk.parseGetNymResponse(response) } - private async getEndpointsForDid(agentContext: AgentContext, did: string) { + private async getEndpointsForDid(agentContext: AgentContext, pool: IndySdkPool, did: string) { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) - const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) - try { agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.didIndyNamespace}'`) diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts new file mode 100644 index 0000000000..4d4390bd24 --- /dev/null +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts @@ -0,0 +1,494 @@ +import type { IndySdkPool } from '../../ledger/IndySdkPool' +import type { DidRecord, RecordSavedEvent } from '@aries-framework/core' + +import { + SigningProviderRegistry, + DidsApi, + DidDocument, + VerificationMethod, + KeyType, + Key, + TypedArrayEncoder, + DidRepository, + JsonTransformer, + DidDocumentRole, + EventEmitter, + RepositoryEventTypes, +} from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { mockFunction, getAgentConfig, getAgentContext, agentDependencies, indySdk } from '../../../../core/tests' +import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' +import { IndySdkWallet } from '../../wallet' +import { IndySdkIndyDidRegistrar } from '../IndySdkIndyDidRegistrar' + +jest.mock('../../ledger/IndySdkPoolService') +const IndySdkPoolServiceMock = IndySdkPoolService as jest.Mock +const indySdkPoolServiceMock = new IndySdkPoolServiceMock() + +const pool = { + config: { indyNamespace: 'pool1' }, +} as IndySdkPool +mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue(pool) + +const agentConfig = getAgentConfig('IndySdkIndyDidRegistrar') +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + +jest + .spyOn(wallet, 'createKey') + .mockResolvedValue(Key.fromPublicKeyBase58('E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', KeyType.Ed25519)) +const storageService = new InMemoryStorageService() +const eventEmitter = new EventEmitter(agentDependencies, new Subject()) +const didRepository = new DidRepository(storageService, eventEmitter) + +const agentContext = getAgentContext({ + wallet, + registerInstances: [ + [DidRepository, didRepository], + [IndySdkPoolService, indySdkPoolServiceMock], + [ + DidsApi, + { + resolve: jest.fn().mockResolvedValue({ + didDocument: new DidDocument({ + id: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + authentication: [ + new VerificationMethod({ + id: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }), + ], + }), + }), + }, + ], + ], + agentConfig, +}) + +const indySdkIndyDidRegistrar = new IndySdkIndyDidRegistrar() + +describe('IndySdkIndyDidRegistrar', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + test('returns an error state if both did and privateKey are provided', async () => { + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + did: 'did:indy:pool1:did-value', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + secret: { + privateKey: TypedArrayEncoder.fromString('key'), + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Only one of 'privateKey' or 'did' must be provided`, + }, + }) + }) + + test('returns an error state if the submitter did is not a valid did:indy did', async () => { + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'unknownError: BzCbsNYhMrjHiqZDTUASHg is not a valid did:indy did', + }, + }) + }) + + test('returns an error state if did is provided, but it is not a valid did:indy did', async () => { + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + did: 'BzCbsNYhMrjHiqZDTUASHg', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + verkey: 'verkey', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'unknownError: BzCbsNYhMrjHiqZDTUASHg is not a valid did:indy did', + }, + }) + }) + + test('returns an error state if did is provided, but no verkey', async () => { + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + did: 'BzCbsNYhMrjHiqZDTUASHg', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'If a did is defined, a matching verkey must be provided', + }, + }) + }) + + test('returns an error state if did and verkey are provided, but the did is not self certifying', async () => { + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + did: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + verkey: 'verkey', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Did must be first 16 bytes of the the verkey base58 encoded.', + }, + }) + }) + + test('returns an error state if did is provided, but does not match with the namespace from the submitterDid', async () => { + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + did: 'did:indy:pool2:R1xKJw17sUoXhejEpugMYJ', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: + 'The submitter did uses namespace pool1 and the did to register uses namespace pool2. Namespaces must match.', + }, + }) + }) + + test('creates a did:indy document without services', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + }, + secret: { + privateKey, + }, + }) + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + pool, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'R1xKJw17sUoXhejEpugMYJ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD' + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + verificationMethod: [ + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + ], + authentication: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey'], + assertionMethod: undefined, + keyAgreement: undefined, + }, + secret: { + privateKey, + }, + }, + }) + }) + + test('creates a did:indy document by passing did', async () => { + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + options: { + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + }, + secret: {}, + }) + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + pool, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'R1xKJw17sUoXhejEpugMYJ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD' + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + verificationMethod: [ + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + ], + authentication: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey'], + assertionMethod: undefined, + keyAgreement: undefined, + }, + secret: {}, + }, + }) + }) + + test('creates a did:indy document with services', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + const result = await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + endpoints: { + endpoint: 'https://example.com/endpoint', + routingKeys: ['key-1'], + types: ['DIDComm', 'did-communication', 'endpoint'], + }, + }, + secret: { + privateKey, + }, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + pool, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'R1xKJw17sUoXhejEpugMYJ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD' + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + verificationMethod: [ + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + }, + ], + service: [ + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#endpoint', + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#did-communication', + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + priority: 0, + recipientKeys: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], + routingKeys: ['key-1'], + accept: ['didcomm/aip2;env=rfc19'], + }, + { + id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#didcomm-1', + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + routingKeys: ['key-1'], + accept: ['didcomm/v2'], + }, + ], + authentication: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey'], + assertionMethod: undefined, + keyAgreement: ['did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], + }, + secret: { + privateKey, + }, + }, + }) + }) + + test('stores the did document', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + const saveCalled = jest.fn() + eventEmitter.on>(RepositoryEventTypes.RecordSaved, saveCalled) + + await indySdkIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + endpoints: { + endpoint: 'https://example.com/endpoint', + routingKeys: ['key-1'], + types: ['DIDComm', 'did-communication', 'endpoint'], + }, + }, + secret: { + privateKey, + }, + }) + + expect(saveCalled).toHaveBeenCalledTimes(1) + const [saveEvent] = saveCalled.mock.calls[0] + + expect(saveEvent.payload.record).toMatchObject({ + did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', + role: DidDocumentRole.Created, + _tags: { + recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], + }, + didDocument: undefined, + }) + }) + + test('returns an error state when calling update', async () => { + const result = await indySdkIndyDidRegistrar.update() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:indy not implemented yet`, + }, + }) + }) + + test('returns an error state when calling deactivate', async () => { + const result = await indySdkIndyDidRegistrar.deactivate() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:indy not implemented yet`, + }, + }) + }) +}) diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts new file mode 100644 index 0000000000..7c4f294286 --- /dev/null +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts @@ -0,0 +1,127 @@ +import type { IndySdkPool } from '../../ledger' +import type { IndyEndpointAttrib } from '../didSovUtil' +import type { GetNymResponse } from 'indy-sdk' + +import { SigningProviderRegistry, JsonTransformer } from '@aries-framework/core' +import indySdk from 'indy-sdk' + +import { mockFunction, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' +import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' +import { IndySdkSymbol } from '../../types' +import { IndySdkWallet } from '../../wallet' +import { IndySdkIndyDidResolver } from '../IndySdkIndyDidResolver' + +import didIndyPool1R1xKJw17sUoXhejEpugMYJFixture from './__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json' +import didIndyPool1WJz9mHyW9BZksioQnRsrAoFixture from './__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json' + +jest.mock('../../ledger/IndySdkPoolService') +const IndySdkPoolServiceMock = IndySdkPoolService as jest.Mock +const indySdkPoolServiceMock = new IndySdkPoolServiceMock() + +mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ + config: { indyNamespace: 'pool1' }, +} as IndySdkPool) + +const agentConfig = getAgentConfig('IndySdkIndyDidResolver') + +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + +const agentContext = getAgentContext({ + wallet, + agentConfig, + registerInstances: [ + [IndySdkPoolService, indySdkPoolServiceMock], + [IndySdkSymbol, indySdk], + ], +}) + +const indySdkSovDidResolver = new IndySdkIndyDidResolver() + +describe('IndySdkIndyDidResolver', () => { + it('should correctly resolve a did:indy document', async () => { + const did = 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ' + + const nymResponse: GetNymResponse = { + did: 'R1xKJw17sUoXhejEpugMYJ', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + role: 'ENDORSER', + } + + const endpoints: IndyEndpointAttrib = { + endpoint: 'https://ssi.com', + profile: 'https://profile.com', + hub: 'https://hub.com', + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) + + const result = await indySdkSovDidResolver.resolve(agentContext, did) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didIndyPool1R1xKJw17sUoXhejEpugMYJFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should resolve a did:indy document with routingKeys and types entries in the attrib', async () => { + const did = 'did:indy:pool1:WJz9mHyW9BZksioQnRsrAo' + + const nymResponse: GetNymResponse = { + did: 'WJz9mHyW9BZksioQnRsrAo', + verkey: 'GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8', + role: 'ENDORSER', + } + + const endpoints: IndyEndpointAttrib = { + endpoint: 'https://agent.com', + types: ['endpoint', 'did-communication', 'DIDComm'], + routingKeys: ['routingKey1', 'routingKey2'], + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockResolvedValue(nymResponse) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getEndpointsForDid').mockResolvedValue(endpoints) + + const result = await indySdkSovDidResolver.resolve(agentContext, did) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: didIndyPool1WJz9mHyW9BZksioQnRsrAoFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { + const did = 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ' + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jest.spyOn(indySdkSovDidResolver, 'getPublicDid').mockRejectedValue(new Error('Error retrieving did')) + + const result = await indySdkSovDidResolver.resolve(agentContext, did) + + expect(result).toMatchObject({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ': Error: Error retrieving did`, + }, + }) + }) +}) diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts deleted file mode 100644 index 2f63d2a91a..0000000000 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidRegistrar.test.ts +++ /dev/null @@ -1,372 +0,0 @@ -import type { IndySdkPool } from '../../ledger/IndySdkPool' -import type { Wallet, DidRecord, RecordSavedEvent } from '@aries-framework/core' - -import { - DidsApi, - DidDocument, - VerificationMethod, - KeyType, - Key, - TypedArrayEncoder, - DidRepository, - JsonTransformer, - DidDocumentRole, - EventEmitter, - RepositoryEventTypes, -} from '@aries-framework/core' -import { Subject } from 'rxjs' - -import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' -import { mockFunction, getAgentConfig, getAgentContext, agentDependencies } from '../../../../core/tests' -import { IndySdkPoolService } from '../../ledger/IndySdkPoolService' -import { IndySdkSovDidRegistrar } from '../IndySdkSovDidRegistrar' - -jest.mock('../../ledger/IndySdkPoolService') -const IndySdkPoolServiceMock = IndySdkPoolService as jest.Mock -const indySdkPoolServiceMock = new IndySdkPoolServiceMock() - -mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ - config: { indyNamespace: 'pool1' }, -} as IndySdkPool) - -const agentConfig = getAgentConfig('IndySdkSovDidRegistrar') - -const wallet = { - createKey: jest - .fn() - .mockResolvedValue(Key.fromPublicKeyBase58('E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', KeyType.Ed25519)), -} as unknown as Wallet -const storageService = new InMemoryStorageService() -const eventEmitter = new EventEmitter(agentDependencies, new Subject()) -const didRepository = new DidRepository(storageService, eventEmitter) - -const agentContext = getAgentContext({ - wallet, - registerInstances: [ - [DidRepository, didRepository], - [IndySdkPoolService, indySdkPoolServiceMock], - [ - DidsApi, - { - resolve: jest.fn().mockResolvedValue({ - didDocument: new DidDocument({ - id: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - authentication: [ - new VerificationMethod({ - id: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', - type: 'Ed25519VerificationKey2018', - controller: 'did:sov:BzCbsNYhMrjHiqZDTUASHg', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }), - ], - }), - }), - }, - ], - ], - agentConfig, -}) - -const indySdkSovDidRegistrar = new IndySdkSovDidRegistrar() - -describe('IndySdkSovDidRegistrar', () => { - afterEach(() => { - jest.clearAllMocks() - }) - - it('should return an error state if an invalid private key is provided', async () => { - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - - options: { - submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', - alias: 'Hello', - }, - secret: { - privateKey: TypedArrayEncoder.fromString('invalid'), - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Invalid private key provided', - }, - }) - }) - - it('should return an error state if the submitter did is not qualified with did:sov', async () => { - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - submitterVerificationMethod: 'BzCbsNYhMrjHiqZDTUASHg', - alias: 'Hello', - }, - }) - - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Submitter did must be a valid did:sov did', - }, - }) - }) - - it('should correctly create a did:sov document without services', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) - - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - alias: 'Hello', - submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', - role: 'STEWARD', - }, - secret: { - privateKey, - }, - }) - expect(registerPublicDidSpy).toHaveBeenCalledWith( - agentContext, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'R1xKJw17sUoXhejEpugMYJ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Pool - { config: { indyNamespace: 'pool1' } }, - // Role - 'STEWARD' - ) - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: { - qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - }, - didRegistrationMetadata: { - didIndyNamespace: 'pool1', - }, - didState: { - state: 'finished', - did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - ], - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - verificationMethod: [ - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', - type: 'Ed25519VerificationKey2018', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', - type: 'X25519KeyAgreementKey2019', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', - }, - ], - authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - }, - secret: { - privateKey, - }, - }, - }) - }) - - it('should correctly create a did:sov document with services', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) - - const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') - setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) - - const result = await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - alias: 'Hello', - submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', - role: 'STEWARD', - endpoints: { - endpoint: 'https://example.com/endpoint', - routingKeys: ['key-1'], - types: ['DIDComm', 'did-communication', 'endpoint'], - }, - }, - secret: { - privateKey, - }, - }) - - expect(registerPublicDidSpy).toHaveBeenCalledWith( - agentContext, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'R1xKJw17sUoXhejEpugMYJ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Pool - { config: { indyNamespace: 'pool1' } }, - // Role - 'STEWARD' - ) - expect(JsonTransformer.toJSON(result)).toMatchObject({ - didDocumentMetadata: { - qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - }, - didRegistrationMetadata: { - didIndyNamespace: 'pool1', - }, - didState: { - state: 'finished', - did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - 'https://didcomm.org/messaging/contexts/v2', - ], - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - verificationMethod: [ - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-1', - type: 'Ed25519VerificationKey2018', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1', - type: 'X25519KeyAgreementKey2019', - controller: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', - }, - ], - service: [ - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#endpoint', - serviceEndpoint: 'https://example.com/endpoint', - type: 'endpoint', - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#did-communication', - serviceEndpoint: 'https://example.com/endpoint', - type: 'did-communication', - priority: 0, - recipientKeys: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - routingKeys: ['key-1'], - accept: ['didcomm/aip2;env=rfc19'], - }, - { - id: 'did:sov:R1xKJw17sUoXhejEpugMYJ#didcomm-1', - serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', - routingKeys: ['key-1'], - accept: ['didcomm/v2'], - }, - ], - authentication: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - assertionMethod: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-1'], - keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], - }, - secret: { - privateKey, - }, - }, - }) - }) - - it('should store the did document', async () => { - const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - - const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') - registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('did')) - - const setEndpointsForDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'setEndpointsForDid') - setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) - - const saveCalled = jest.fn() - eventEmitter.on>(RepositoryEventTypes.RecordSaved, saveCalled) - - await indySdkSovDidRegistrar.create(agentContext, { - method: 'sov', - options: { - alias: 'Hello', - submitterVerificationMethod: 'did:sov:BzCbsNYhMrjHiqZDTUASHg#key-1', - role: 'STEWARD', - endpoints: { - endpoint: 'https://example.com/endpoint', - routingKeys: ['key-1'], - types: ['DIDComm', 'did-communication', 'endpoint'], - }, - }, - secret: { - privateKey, - }, - }) - - expect(saveCalled).toHaveBeenCalledTimes(1) - const [saveEvent] = saveCalled.mock.calls[0] - - expect(saveEvent.payload.record).toMatchObject({ - did: 'did:sov:R1xKJw17sUoXhejEpugMYJ', - role: DidDocumentRole.Created, - _tags: { - recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], - qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', - }, - didDocument: undefined, - }) - }) - - it('should return an error state when calling update', async () => { - const result = await indySdkSovDidRegistrar.update() - - expect(result).toEqual({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: updating did:sov not implemented yet`, - }, - }) - }) - - it('should return an error state when calling deactivate', async () => { - const result = await indySdkSovDidRegistrar.deactivate() - - expect(result).toEqual({ - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notImplemented: deactivating did:sov not implemented yet`, - }, - }) - }) -}) diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts index c9bb1bbc93..6f4eabab97 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts @@ -23,6 +23,10 @@ mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ config: { indyNamespace: 'pool1' }, } as IndySdkPool) +mockFunction(indySdkPoolServiceMock.getPoolForDid).mockResolvedValue({ + pool: { config: { indyNamespace: 'pool1' } } as IndySdkPool, +}) + const agentConfig = getAgentConfig('IndySdkSovDidResolver') const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) diff --git a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json new file mode 100644 index 0000000000..c0bd51aa30 --- /dev/null +++ b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1R1xKJw17sUoXhejEpugMYJ.json @@ -0,0 +1,50 @@ +{ + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1" + ], + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ", + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ", + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey", + "publicKeyBase58": "E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu" + }, + { + "type": "X25519KeyAgreementKey2019", + "controller": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ", + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1", + "publicKeyBase58": "Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt" + } + ], + "authentication": ["did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#verkey"], + "keyAgreement": ["did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], + "service": [ + { + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#endpoint", + "type": "endpoint", + "serviceEndpoint": "https://ssi.com" + }, + { + "accept": ["didcomm/aip2;env=rfc19"], + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#did-communication", + "priority": 0, + "recipientKeys": ["did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#key-agreement-1"], + "routingKeys": [], + "serviceEndpoint": "https://ssi.com", + "type": "did-communication" + }, + { + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#profile", + "serviceEndpoint": "https://profile.com", + "type": "profile" + }, + { + "id": "did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#hub", + "serviceEndpoint": "https://hub.com", + "type": "hub" + } + ] +} diff --git a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json new file mode 100644 index 0000000000..a943f3bf9e --- /dev/null +++ b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json @@ -0,0 +1,48 @@ +{ + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1", + "https://didcomm.org/messaging/contexts/v2" + ], + "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo", + "verificationMethod": [ + { + "type": "Ed25519VerificationKey2018", + "controller": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo", + "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#verkey", + "publicKeyBase58": "GyYtYWU1vjwd5PFJM4VSX5aUiSV3TyZMuLBJBTQvfdF8" + }, + { + "type": "X25519KeyAgreementKey2019", + "controller": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo", + "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1", + "publicKeyBase58": "S3AQEEKkGYrrszT9D55ozVVX2XixYp8uynqVm4okbud" + } + ], + "authentication": ["did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#verkey"], + "keyAgreement": ["did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "service": [ + { + "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#endpoint", + "type": "endpoint", + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#did-communication", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#key-agreement-1"], + "routingKeys": ["routingKey1", "routingKey2"], + "accept": ["didcomm/aip2;env=rfc19"], + "serviceEndpoint": "https://agent.com" + }, + { + "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#didcomm-1", + "type": "DIDComm", + "serviceEndpoint": "https://agent.com", + "accept": ["didcomm/v2"], + "routingKeys": ["routingKey1", "routingKey2"] + } + ] +} diff --git a/packages/indy-sdk/src/dids/didIndyUtil.ts b/packages/indy-sdk/src/dids/didIndyUtil.ts new file mode 100644 index 0000000000..af8adacf77 --- /dev/null +++ b/packages/indy-sdk/src/dids/didIndyUtil.ts @@ -0,0 +1,67 @@ +import type { AgentContext } from '@aries-framework/core' + +import { + getKeyFromVerificationMethod, + AriesFrameworkError, + convertPublicKeyToX25519, + DidDocumentBuilder, + DidsApi, + TypedArrayEncoder, +} from '@aries-framework/core' + +import { DID_INDY_REGEX } from '../utils/did' + +export function parseIndyDid(did: string) { + const match = did.match(DID_INDY_REGEX) + if (match) { + const [, namespace, id] = match + return { namespace, id } + } else { + throw new AriesFrameworkError(`${did} is not a valid did:indy did`) + } +} + +// Create a base DIDDoc template according to https://hyperledger.github.io/indy-did-method/#base-diddoc-template +export function indyDidDocumentFromDid(did: string, publicKeyBase58: string) { + const verificationMethodId = `${did}#verkey` + + const builder = new DidDocumentBuilder(did) + .addContext('https://w3id.org/security/suites/ed25519-2018/v1') + .addVerificationMethod({ + controller: did, + id: verificationMethodId, + publicKeyBase58, + type: 'Ed25519VerificationKey2018', + }) + .addAuthentication(verificationMethodId) + + return builder +} + +export function createKeyAgreementKey(verkey: string) { + return TypedArrayEncoder.toBase58(convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(verkey))) +} + +/** + * Fetches the verification key for a given did:indy did and returns the key as a {@link Key} object. + * + * @throws {@link AriesFrameworkError} if the did could not be resolved or the key could not be extracted + */ +export async function verificationKeyForIndyDid(agentContext: AgentContext, did: string) { + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(did) + + if (!didResult.didDocument) { + throw new AriesFrameworkError( + `Could not resolve did ${did}. ${didResult.didResolutionMetadata.error} ${didResult.didResolutionMetadata.message}` + ) + } + + // did:indy dids MUST have a verificationMethod with #verkey + const verificationMethod = didResult.didDocument.dereferenceKey(`${did}#verkey`) + const key = getKeyFromVerificationMethod(verificationMethod) + + return key +} diff --git a/packages/indy-sdk/src/dids/didSovUtil.ts b/packages/indy-sdk/src/dids/didSovUtil.ts index b5af6ee3f0..989e09f432 100644 --- a/packages/indy-sdk/src/dids/didSovUtil.ts +++ b/packages/indy-sdk/src/dids/didSovUtil.ts @@ -9,6 +9,8 @@ import { import { getFullVerkey } from '../utils/did' +export type DidCommServicesEndpointType = 'endpoint' | 'did-communication' | 'DIDComm' + export interface IndyEndpointAttrib { endpoint?: string types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> @@ -16,6 +18,10 @@ export interface IndyEndpointAttrib { [key: string]: unknown } +/** + * Get a base did:sov did document based on the provided did and verkey + * https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html#crud-operation-definitions + */ export function sovDidDocumentFromDid(fullDid: string, verkey: string) { const verificationMethodId = `${fullDid}#key-1` const keyAgreementId = `${fullDid}#key-agreement-1` diff --git a/packages/indy-sdk/src/dids/index.ts b/packages/indy-sdk/src/dids/index.ts index 68eabe204d..8017bf8749 100644 --- a/packages/indy-sdk/src/dids/index.ts +++ b/packages/indy-sdk/src/dids/index.ts @@ -1,7 +1,3 @@ -export { - IndySdkSovDidRegistrar, - IndySdkSovDidCreateOptions, - IndySdkSovDidDeactivateOptions, - IndySdkSovDidUpdateOptions, -} from './IndySdkSovDidRegistrar' +export { IndySdkIndyDidRegistrar, IndySdkIndyDidCreateOptions } from './IndySdkIndyDidRegistrar' export { IndySdkSovDidResolver } from './IndySdkSovDidResolver' +export { IndySdkIndyDidResolver } from './IndySdkIndyDidResolver' diff --git a/packages/indy-sdk/src/index.ts b/packages/indy-sdk/src/index.ts index 64ed474b75..5857f00da2 100644 --- a/packages/indy-sdk/src/index.ts +++ b/packages/indy-sdk/src/index.ts @@ -1,11 +1,5 @@ // Dids -export { - IndySdkSovDidRegistrar, - IndySdkSovDidCreateOptions, - IndySdkSovDidDeactivateOptions, - IndySdkSovDidUpdateOptions, - IndySdkSovDidResolver, -} from './dids' +export * from './dids' // Ledger export { IndySdkPoolConfig } from './ledger' diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index be9217b0ed..23928fafa5 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -17,7 +17,7 @@ import { Subject } from 'rxjs' import { IndySdkModuleConfig } from '../IndySdkModuleConfig' import { IndySdkError, isIndyError } from '../error' import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' -import { isSelfCertifiedDid } from '../utils/did' +import { DID_INDY_REGEX, isLegacySelfCertifiedDid } from '../utils/did' import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' import { IndySdkPool } from './IndySdkPool' @@ -56,13 +56,39 @@ export class IndySdkPoolService { } /** - * Get the most appropriate pool for the given did. The algorithm is based on the approach as described in this document: + * Get the most appropriate pool for the given did. + * If the did is a qualified indy did, the pool will be determined based on the namespace. + * If it is a legacy unqualified indy did, the pool will be determined based on the algorithm as described in this document: * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit + * + * This method will optionally return a nym response when the did has been resolved to determine the ledger + * either now or in the past. The nymResponse can be used to prevent multiple ledger quries fetching the same + * did */ public async getPoolForDid( agentContext: AgentContext, did: string - ): Promise<{ pool: IndySdkPool; did: GetNymResponse }> { + ): Promise<{ pool: IndySdkPool; nymResponse?: GetNymResponse }> { + // Check if the did starts with did:indy + const match = did.match(DID_INDY_REGEX) + + if (match) { + const [, namespace] = match + + const pool = this.getPoolForNamespace(namespace) + + if (pool) return { pool } + + throw new IndySdkPoolError(`Pool for indy namespace '${namespace}' not found`) + } else { + return await this.getPoolForLegacyDid(agentContext, did) + } + } + + private async getPoolForLegacyDid( + agentContext: AgentContext, + did: string + ): Promise<{ pool: IndySdkPool; nymResponse: GetNymResponse }> { const pools = this.pools if (pools.length === 0) { @@ -78,7 +104,7 @@ export class IndySdkPoolService { // If we have the nym response with associated pool in the cache, we'll use that if (cachedNymResponse && pool) { this.logger.trace(`Found ledger '${pool.didIndyNamespace}' for did '${did}' in cache`) - return { did: cachedNymResponse.nymResponse, pool } + return { nymResponse: cachedNymResponse.nymResponse, pool } } const { successful, rejected } = await this.getSettledDidResponsesFromPools(did, pools) @@ -103,7 +129,7 @@ export class IndySdkPoolService { // We take the first self certifying DID as we take the order in the // indyLedgers config as the order of preference of ledgers let value = successful.find((response) => - isSelfCertifiedDid(response.value.did.did, response.value.did.verkey) + isLegacySelfCertifiedDid(response.value.did.did, response.value.did.verkey) )?.value if (!value) { @@ -123,7 +149,8 @@ export class IndySdkPoolService { nymResponse: value.did, indyNamespace: value.pool.didIndyNamespace, } satisfies CachedDidResponse) - return { pool: value.pool, did: value.did } + + return { pool: value.pool, nymResponse: value.did } } private async getSettledDidResponsesFromPools(did: string, pools: IndySdkPool[]) { diff --git a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts index ee595c31ec..20d79e0564 100644 --- a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts +++ b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts @@ -115,6 +115,12 @@ describe('IndySdkPoolService', () => { expect(poolService.getPoolForDid(agentContext, did)).rejects.toThrowError(IndySdkPoolNotFoundError) }) + it('should return the pool based on namespace if did is a valid did:indy did', async () => { + const { pool } = await poolService.getPoolForDid(agentContext, 'did:indy:sovrin:Y5bj4SjCiTM9PgeheKAiXx') + + expect(pool.didIndyNamespace).toBe('sovrin') + }) + it('should return the pool if the did was only found on one ledger', async () => { const did = 'TL1EaPFCZ8Si5aUrqScBDt' // Only found on one ledger diff --git a/packages/indy-sdk/src/utils/__tests__/did.test.ts b/packages/indy-sdk/src/utils/__tests__/did.test.ts index 45344136d9..222f9898fd 100644 --- a/packages/indy-sdk/src/utils/__tests__/did.test.ts +++ b/packages/indy-sdk/src/utils/__tests__/did.test.ts @@ -1,4 +1,4 @@ -import { isAbbreviatedVerkey, isFullVerkey, isSelfCertifiedDid } from '../did' +import { isAbbreviatedVerkey, isFullVerkey, isLegacySelfCertifiedDid } from '../did' const validAbbreviatedVerkeys = [ '~PKAYz8Ev4yoQgr2LaMAWFx', @@ -36,15 +36,19 @@ const invalidFullVerkeys = [ describe('Utils | Did', () => { describe('isSelfCertifiedDid()', () => { test('returns true if the verkey is abbreviated', () => { - expect(isSelfCertifiedDid('PW8ZHpNupeWXbmpPWog6Ki', '~QQ5jiH1dgXPAnvHdJvazn9')).toBe(true) + expect(isLegacySelfCertifiedDid('PW8ZHpNupeWXbmpPWog6Ki', '~QQ5jiH1dgXPAnvHdJvazn9')).toBe(true) }) test('returns true if the verkey is not abbreviated and the did is generated from the verkey', () => { - expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'HyEoPRNvC7q4jj5joUo8AWYtxbNccbEnTAeuMYkpmNS2')).toBe(true) + expect(isLegacySelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'HyEoPRNvC7q4jj5joUo8AWYtxbNccbEnTAeuMYkpmNS2')).toBe( + true + ) }) test('returns false if the verkey is not abbreviated and the did is not generated from the verkey', () => { - expect(isSelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'AcU7DnRqoXGYATD6VqsRq4eHuz55gdM3uzFBEhFd6rGh')).toBe(false) + expect(isLegacySelfCertifiedDid('Y8q4Aq6gRAcmB6jjKk3Z7t', 'AcU7DnRqoXGYATD6VqsRq4eHuz55gdM3uzFBEhFd6rGh')).toBe( + false + ) }) }) diff --git a/packages/indy-sdk/src/utils/did.ts b/packages/indy-sdk/src/utils/did.ts index 90b465a0f6..7d78cd09e2 100644 --- a/packages/indy-sdk/src/utils/did.ts +++ b/packages/indy-sdk/src/utils/did.ts @@ -19,6 +19,7 @@ import { Buffer, TypedArrayEncoder } from '@aries-framework/core' export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ +export const DID_INDY_REGEX = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22})$/ /** * Check whether the did is a self certifying did. If the verkey is abbreviated this method @@ -27,14 +28,14 @@ export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ * * @return Boolean indicating whether the did is self certifying */ -export function isSelfCertifiedDid(did: string, verkey: string): boolean { +export function isLegacySelfCertifiedDid(did: string, verkey: string): boolean { // If the verkey is Abbreviated, it means the full verkey // is the did + the verkey if (isAbbreviatedVerkey(verkey)) { return true } - const didFromVerkey = indyDidFromPublicKeyBase58(verkey) + const didFromVerkey = legacyIndyDidFromPublicKeyBase58(verkey) if (didFromVerkey === did) { return true @@ -43,7 +44,7 @@ export function isSelfCertifiedDid(did: string, verkey: string): boolean { return false } -export function indyDidFromPublicKeyBase58(publicKeyBase58: string): string { +export function legacyIndyDidFromPublicKeyBase58(publicKeyBase58: string): string { const buffer = TypedArrayEncoder.fromBase58(publicKeyBase58) const did = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) diff --git a/packages/indy-sdk/tests/__fixtures__/anoncreds.ts b/packages/indy-sdk/tests/__fixtures__/anoncreds.ts new file mode 100644 index 0000000000..eb978ec748 --- /dev/null +++ b/packages/indy-sdk/tests/__fixtures__/anoncreds.ts @@ -0,0 +1,30 @@ +export const credentialDefinitionValue = { + primary: { + n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', + s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', + r: { + master_secret: + '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', + name: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', + }, + rctxt: + '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', + z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', + }, + revocation: { + g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + g_dash: + '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + htilde: + '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h_cap: + '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + }, +} diff --git a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts b/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts similarity index 68% rename from packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts rename to packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts index 4518010326..c007eaa561 100644 --- a/packages/indy-sdk/tests/sov-did-registrar.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts @@ -1,14 +1,14 @@ -import type { IndySdkSovDidCreateOptions } from '../src/dids/IndySdkSovDidRegistrar' +import type { IndySdkIndyDidCreateOptions } from '../src' import { Agent, TypedArrayEncoder, convertPublicKeyToX25519, JsonTransformer } from '@aries-framework/core' import { generateKeyPairFromSeed } from '@stablelib/ed25519' import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests' -import { indyDidFromPublicKeyBase58 } from '../src/utils/did' +import { legacyIndyDidFromPublicKeyBase58 } from '../src/utils/did' import { getIndySdkModules } from './setupIndySdkModule' -const agentOptions = getAgentOptions('Faber Dids Registrar', {}, getIndySdkModules()) +const agentOptions = getAgentOptions('Indy Sdk Indy Did Registrar', {}, getIndySdkModules()) describe('dids', () => { let agent: Agent> @@ -23,7 +23,7 @@ describe('dids', () => { await agent.wallet.delete() }) - it('should create a did:sov did', async () => { + it('should create a did:indy did', async () => { // Add existing endorser did to the wallet const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( agent, @@ -41,12 +41,12 @@ describe('dids', () => { const publicKeyEd25519 = generateKeyPairFromSeed(privateKey).publicKey const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(publicKeyEd25519)) const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) - const indyDid = indyDidFromPublicKeyBase58(ed25519PublicKeyBase58) + const unqualifiedDid = legacyIndyDidFromPublicKeyBase58(ed25519PublicKeyBase58) - const did = await agent.dids.create({ - method: 'sov', + const did = await agent.dids.create({ + method: 'indy', options: { - submitterVerificationMethod: `did:sov:${unqualifiedSubmitterDid}#key-1`, + submitterDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, alias: 'Alias', endpoints: { endpoint: 'https://example.com/endpoint', @@ -60,15 +60,11 @@ describe('dids', () => { }) expect(JsonTransformer.toJSON(did)).toMatchObject({ - didDocumentMetadata: { - qualifiedIndyDid: `did:indy:pool:localtest:${indyDid}`, - }, - didRegistrationMetadata: { - didIndyNamespace: 'pool:localtest', - }, + didDocumentMetadata: {}, + didRegistrationMetadata: {}, didState: { state: 'finished', - did: `did:sov:${indyDid}`, + did: `did:indy:pool:localtest:${unqualifiedDid}`, didDocument: { '@context': [ 'https://w3id.org/did/v1', @@ -80,47 +76,47 @@ describe('dids', () => { controller: undefined, verificationMethod: [ { - id: `did:sov:${indyDid}#key-1`, + id: `did:indy:pool:localtest:${unqualifiedDid}#verkey`, type: 'Ed25519VerificationKey2018', - controller: `did:sov:${indyDid}`, + controller: `did:indy:pool:localtest:${unqualifiedDid}`, publicKeyBase58: ed25519PublicKeyBase58, }, { - id: `did:sov:${indyDid}#key-agreement-1`, + id: `did:indy:pool:localtest:${unqualifiedDid}#key-agreement-1`, type: 'X25519KeyAgreementKey2019', - controller: `did:sov:${indyDid}`, + controller: `did:indy:pool:localtest:${unqualifiedDid}`, publicKeyBase58: x25519PublicKeyBase58, }, ], service: [ { - id: `did:sov:${indyDid}#endpoint`, + id: `did:indy:pool:localtest:${unqualifiedDid}#endpoint`, serviceEndpoint: 'https://example.com/endpoint', type: 'endpoint', }, { accept: ['didcomm/aip2;env=rfc19'], - id: `did:sov:${indyDid}#did-communication`, + id: `did:indy:pool:localtest:${unqualifiedDid}#did-communication`, priority: 0, - recipientKeys: [`did:sov:${indyDid}#key-agreement-1`], + recipientKeys: [`did:indy:pool:localtest:${unqualifiedDid}#key-agreement-1`], routingKeys: ['a-routing-key'], serviceEndpoint: 'https://example.com/endpoint', type: 'did-communication', }, { accept: ['didcomm/v2'], - id: `did:sov:${indyDid}#didcomm-1`, + id: `did:indy:pool:localtest:${unqualifiedDid}#didcomm-1`, routingKeys: ['a-routing-key'], serviceEndpoint: 'https://example.com/endpoint', type: 'DIDComm', }, ], - authentication: [`did:sov:${indyDid}#key-1`], - assertionMethod: [`did:sov:${indyDid}#key-1`], - keyAgreement: [`did:sov:${indyDid}#key-agreement-1`], + authentication: [`did:indy:pool:localtest:${unqualifiedDid}#verkey`], + assertionMethod: undefined, + keyAgreement: [`did:indy:pool:localtest:${unqualifiedDid}#key-agreement-1`], capabilityInvocation: undefined, capabilityDelegation: undefined, - id: `did:sov:${indyDid}`, + id: `did:indy:pool:localtest:${unqualifiedDid}`, }, secret: { privateKey, diff --git a/packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts new file mode 100644 index 0000000000..839db5e4df --- /dev/null +++ b/packages/indy-sdk/tests/indy-did-resolver.e2e.test.ts @@ -0,0 +1,99 @@ +import type { IndySdkIndyDidCreateOptions } from '../src' + +import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' + +import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests/helpers' + +import { getIndySdkModules } from './setupIndySdkModule' + +const agent = new Agent(getAgentOptions('Indy SDK Indy DID resolver', {}, getIndySdkModules())) + +describe('Indy SDK Indy DID resolver', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should resolve a did:indy did', async () => { + // Add existing endorser did to the wallet + const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + agent, + TypedArrayEncoder.fromString(publicDidSeed) + ) + + const createResult = await agent.dids.create({ + method: 'indy', + options: { + submitterDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, + alias: 'Alias', + role: 'TRUSTEE', + endpoints: { + endpoint: 'http://localhost:3000', + }, + }, + }) + + // Terrible, but the did can't be immediately resolved, so we need to wait a bit + await new Promise((res) => setTimeout(res, 1000)) + + if (!createResult.didState.did) throw new AriesFrameworkError('Unable to register did') + + const didResult = await agent.dids.resolve(createResult.didState.did) + + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: createResult.didState.did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: createResult.didState.did, + id: `${createResult.didState.did}#verkey`, + publicKeyBase58: expect.any(String), + }, + { + controller: createResult.didState.did, + type: 'X25519KeyAgreementKey2019', + id: `${createResult.didState.did}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${createResult.didState.did}#verkey`], + assertionMethod: undefined, + keyAgreement: [`${createResult.didState.did}#key-agreement-1`], + service: [ + { + id: `${createResult.didState.did}#endpoint`, + serviceEndpoint: 'http://localhost:3000', + type: 'endpoint', + }, + { + id: `${createResult.didState.did}#did-communication`, + accept: ['didcomm/aip2;env=rfc19'], + priority: 0, + recipientKeys: [`${createResult.didState.did}#key-agreement-1`], + routingKeys: [], + serviceEndpoint: 'http://localhost:3000', + type: 'did-communication', + }, + ], + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) +}) diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index 046767b8cc..77e034941d 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -1,4 +1,4 @@ -import { Agent, TypedArrayEncoder } from '@aries-framework/core' +import { Agent, Key, KeyType, TypedArrayEncoder } from '@aries-framework/core' import { agentDependencies, @@ -7,8 +7,11 @@ import { publicDidSeed, } from '../../core/tests/helpers' import { IndySdkAnonCredsRegistry } from '../src/anoncreds/services/IndySdkAnonCredsRegistry' +import { IndySdkPoolService } from '../src/ledger' +import { assertIndySdkWallet } from '../src/utils/assertIndySdkWallet' -import { getIndySdkModules } from './setupIndySdkModule' +import { credentialDefinitionValue } from './__fixtures__/anoncreds' +import { getIndySdkModules, indySdk } from './setupIndySdkModule' const agentConfig = getAgentConfig('IndySdkAnonCredsRegistry') const indySdkModules = getIndySdkModules() @@ -20,6 +23,8 @@ const agent = new Agent({ }) const indySdkAnonCredsRegistry = new IndySdkAnonCredsRegistry() +const indySdkPoolService = agent.dependencyManager.resolve(IndySdkPoolService) +const pool = indySdkPoolService.getPoolForNamespace('pool:localtest') describe('IndySdkAnonCredsRegistry', () => { beforeAll(async () => { @@ -34,20 +39,26 @@ describe('IndySdkAnonCredsRegistry', () => { await agent.wallet.delete() }) + // TODO: use different issuer for schema and credential definition to catch possible bugs // One test as the credential definition depends on the schema test('register and resolve a schema and credential definition', async () => { const dynamicVersion = `1.${Math.random() * 100}` + const legacyIssuerId = 'TL1EaPFCZ8Si5aUrqScBDt' + const signingKey = Key.fromPublicKeyBase58('FMGcFuU3QwAQLywxvmEnSorQT3NwU9wgDMMTaDFtvswm', KeyType.Ed25519) + const didIndyIssuerId = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' + + const legacySchemaId = `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}` + const didIndySchemaId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/SCHEMA/test/${dynamicVersion}` + const schemaResult = await indySdkAnonCredsRegistry.registerSchema(agent.context, { schema: { attrNames: ['name'], - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - name: 'test - 11', + issuerId: didIndyIssuerId, + name: 'test', version: dynamicVersion, }, - options: { - didIndyNamespace: 'pool:localtest', - }, + options: {}, }) expect(schemaResult).toMatchObject({ @@ -55,35 +66,31 @@ describe('IndySdkAnonCredsRegistry', () => { state: 'finished', schema: { attrNames: ['name'], - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - name: 'test - 11', + issuerId: didIndyIssuerId, + name: 'test', version: dynamicVersion, }, - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + schemaId: didIndySchemaId, }, registrationMetadata: {}, schemaMetadata: { indyLedgerSeqNo: expect.any(Number), - didIndyNamespace: 'pool:localtest', }, }) // Wait some time before resolving credential definition object await new Promise((res) => setTimeout(res, 1000)) - const schemaResponse = await indySdkAnonCredsRegistry.getSchema( - agent.context, - `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}` - ) - - expect(schemaResponse).toMatchObject({ + // Resolve using legacy schema id + const legacySchema = await indySdkAnonCredsRegistry.getSchema(agent.context, legacySchemaId) + expect(legacySchema).toMatchObject({ schema: { attrNames: ['name'], - name: 'test - 11', + name: 'test', version: dynamicVersion, issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', }, - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, resolutionMetadata: {}, schemaMetadata: { didIndyNamespace: 'pool:localtest', @@ -91,58 +98,47 @@ describe('IndySdkAnonCredsRegistry', () => { }, }) + // Resolve using did indy schema id + const didIndySchema = await indySdkAnonCredsRegistry.getSchema(agent.context, didIndySchemaId) + expect(didIndySchema).toMatchObject({ + schema: { + attrNames: ['name'], + name: 'test', + version: dynamicVersion, + issuerId: didIndyIssuerId, + }, + schemaId: didIndySchemaId, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: 'pool:localtest', + indyLedgerSeqNo: expect.any(Number), + }, + }) + + const legacyCredentialDefinitionId = `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG` + const didIndyCredentialDefinitionId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG` const credentialDefinitionResult = await indySdkAnonCredsRegistry.registerCredentialDefinition(agent.context, { credentialDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, tag: 'TAG', - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + schemaId: didIndySchemaId, type: 'CL', - value: { - primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', - r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', - master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', - }, - rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', - }, - }, - }, - options: { - didIndyNamespace: 'pool:localtest', + value: credentialDefinitionValue, }, + options: {}, }) expect(credentialDefinitionResult).toMatchObject({ - credentialDefinitionMetadata: { - didIndyNamespace: 'pool:localtest', - }, + credentialDefinitionMetadata: {}, credentialDefinitionState: { credentialDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, tag: 'TAG', - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + schemaId: didIndySchemaId, type: 'CL', - value: { - primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', - r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', - master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', - }, - rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', - }, - }, + value: {}, }, - credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + credentialDefinitionId: didIndyCredentialDefinitionId, state: 'finished', }, registrationMetadata: {}, @@ -151,37 +147,202 @@ describe('IndySdkAnonCredsRegistry', () => { // Wait some time before resolving credential definition object await new Promise((res) => setTimeout(res, 1000)) - const credentialDefinitionResponse = await indySdkAnonCredsRegistry.getCredentialDefinition( + // Resolve using legacy credential definition id + const legacyCredentialDefinition = await indySdkAnonCredsRegistry.getCredentialDefinition( agent.context, - credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId as string + legacyCredentialDefinitionId + ) + expect(legacyCredentialDefinition).toMatchObject({ + credentialDefinitionId: legacyCredentialDefinitionId, + credentialDefinition: { + issuerId: legacyIssuerId, + schemaId: legacySchemaId, + tag: 'TAG', + type: 'CL', + value: credentialDefinitionValue, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + // resolve using did indy credential definition id + const didIndyCredentialDefinition = await indySdkAnonCredsRegistry.getCredentialDefinition( + agent.context, + didIndyCredentialDefinitionId ) - expect(credentialDefinitionResponse).toMatchObject({ - credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + expect(didIndyCredentialDefinition).toMatchObject({ + credentialDefinitionId: didIndyCredentialDefinitionId, credentialDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test - 11:${dynamicVersion}`, + issuerId: didIndyIssuerId, + schemaId: didIndySchemaId, tag: 'TAG', type: 'CL', + value: credentialDefinitionValue, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + assertIndySdkWallet(agent.context.wallet) + + // We don't support creating a revocation registry using AFJ yet, so we directly use indy-sdk to register the revocation registry + const legacyRevocationRegistryId = `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` + const didIndyRevocationRegistryId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/REV_REG_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` + const revocationRegistryRequest = await indySdk.buildRevocRegDefRequest('TL1EaPFCZ8Si5aUrqScBDt', { + id: legacyRevocationRegistryId, + credDefId: legacyCredentialDefinitionId, + revocDefType: 'CL_ACCUM', + tag: 'tag', + value: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + maxCredNum: 100, + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + }, + ver: '1.0', + }) + + await indySdkPoolService.submitWriteRequest(agent.context, pool, revocationRegistryRequest, signingKey) + + const legacyRevocationRegistryDefinition = await indySdkAnonCredsRegistry.getRevocationRegistryDefinition( + agent.context, + legacyRevocationRegistryId + ) + expect(legacyRevocationRegistryDefinition).toMatchObject({ + revocationRegistryDefinitionId: legacyRevocationRegistryId, + revocationRegistryDefinition: { + issuerId: legacyIssuerId, + revocDefType: 'CL_ACCUM', value: { - primary: { - n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', - r: { - age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', - master_secret: - '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + maxCredNum: 100, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', }, - rctxt: - '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', - s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', - z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', }, }, + tag: 'tag', + credDefId: legacyCredentialDefinitionId, }, - credentialDefinitionMetadata: { + revocationRegistryDefinitionMetadata: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + const didIndyRevocationRegistryDefinition = await indySdkAnonCredsRegistry.getRevocationRegistryDefinition( + agent.context, + didIndyRevocationRegistryId + ) + expect(didIndyRevocationRegistryDefinition).toMatchObject({ + revocationRegistryDefinitionId: didIndyRevocationRegistryId, + revocationRegistryDefinition: { + issuerId: didIndyIssuerId, + revocDefType: 'CL_ACCUM', + value: { + maxCredNum: 100, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + }, + tag: 'tag', + credDefId: didIndyCredentialDefinitionId, + }, + revocationRegistryDefinitionMetadata: { + issuanceType: 'ISSUANCE_BY_DEFAULT', didIndyNamespace: 'pool:localtest', }, resolutionMetadata: {}, }) + + // indySdk.buildRevRegEntry panics, so we just pass a custom request directly + const entryResponse = await indySdkPoolService.submitWriteRequest( + agent.context, + pool, + { + identifier: legacyIssuerId, + operation: { + revocDefType: 'CL_ACCUM', + revocRegDefId: legacyRevocationRegistryId, + type: '114', + value: { + accum: + '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', + }, + }, + protocolVersion: 2, + reqId: Math.floor(Math.random() * 1000000), + }, + signingKey + ) + + const legacyRevocationStatusList = await indySdkAnonCredsRegistry.getRevocationStatusList( + agent.context, + legacyRevocationRegistryId, + entryResponse.result.txnMetadata.txnTime + ) + + expect(legacyRevocationStatusList).toMatchObject({ + resolutionMetadata: {}, + revocationStatusList: { + issuerId: legacyIssuerId, + currentAccumulator: + '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', + revRegId: legacyRevocationRegistryId, + revocationList: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + timestamp: entryResponse.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { + didIndyNamespace: 'pool:localtest', + }, + }) + + const didIndyRevocationStatusList = await indySdkAnonCredsRegistry.getRevocationStatusList( + agent.context, + didIndyRevocationRegistryId, + entryResponse.result.txnMetadata.txnTime + ) + + expect(didIndyRevocationStatusList).toMatchObject({ + resolutionMetadata: {}, + revocationStatusList: { + issuerId: didIndyIssuerId, + currentAccumulator: + '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', + revRegId: didIndyRevocationRegistryId, + revocationList: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + timestamp: entryResponse.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { + didIndyNamespace: 'pool:localtest', + }, + }) }) }) diff --git a/packages/indy-sdk/tests/setupIndySdkModule.ts b/packages/indy-sdk/tests/setupIndySdkModule.ts index c0e8ee1313..b4a30f799a 100644 --- a/packages/indy-sdk/tests/setupIndySdkModule.ts +++ b/packages/indy-sdk/tests/setupIndySdkModule.ts @@ -2,7 +2,13 @@ import { DidsModule, KeyDidRegistrar, KeyDidResolver, utils } from '@aries-frame import indySdk from 'indy-sdk' import { genesisPath, taaVersion, taaAcceptanceMechanism } from '../../core/tests/helpers' -import { IndySdkModule, IndySdkModuleConfig, IndySdkSovDidRegistrar, IndySdkSovDidResolver } from '../src' +import { + IndySdkModule, + IndySdkModuleConfig, + IndySdkIndyDidRegistrar, + IndySdkSovDidResolver, + IndySdkIndyDidResolver, +} from '../src' export { indySdk } @@ -23,7 +29,7 @@ export const getIndySdkModuleConfig = () => export const getIndySdkModules = () => ({ indySdk: new IndySdkModule(getIndySdkModuleConfig()), dids: new DidsModule({ - registrars: [new IndySdkSovDidRegistrar(), new KeyDidRegistrar()], - resolvers: [new IndySdkSovDidResolver(), new KeyDidResolver()], + registrars: [new IndySdkIndyDidRegistrar(), new KeyDidRegistrar()], + resolvers: [new IndySdkSovDidResolver(), new IndySdkIndyDidResolver(), new KeyDidResolver()], }), }) diff --git a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts index b1847a3f6d..ef220a59a3 100644 --- a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts +++ b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts @@ -1,8 +1,9 @@ -import type { IndySdkSovDidCreateOptions } from '../src/dids/IndySdkSovDidRegistrar' +import type { IndySdkIndyDidCreateOptions } from '../src' import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests/helpers' +import { parseIndyDid } from '../src/dids/didIndyUtil' import { getIndySdkModules } from './setupIndySdkModule' @@ -25,12 +26,15 @@ describe('Indy SDK Sov DID resolver', () => { TypedArrayEncoder.fromString(publicDidSeed) ) - const createResult = await agent.dids.create({ - method: 'sov', + const createResult = await agent.dids.create({ + method: 'indy', options: { - submitterVerificationMethod: `did:sov:${unqualifiedSubmitterDid}#key-1`, + submitterDid: `did:indy:pool:localtest:${unqualifiedSubmitterDid}`, alias: 'Alias', role: 'TRUSTEE', + endpoints: { + endpoint: 'http://localhost:3000', + }, }, }) @@ -38,7 +42,10 @@ describe('Indy SDK Sov DID resolver', () => { await new Promise((res) => setTimeout(res, 1000)) if (!createResult.didState.did) throw new AriesFrameworkError('Unable to register did') - const didResult = await agent.dids.resolve(createResult.didState.did) + + const { id: unqualifiedDid } = parseIndyDid(createResult.didState.did) + const sovDid = `did:sov:${unqualifiedDid}` + const didResult = await agent.dids.resolve(sovDid) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: { @@ -47,29 +54,44 @@ describe('Indy SDK Sov DID resolver', () => { 'https://w3id.org/security/suites/ed25519-2018/v1', 'https://w3id.org/security/suites/x25519-2019/v1', ], - id: createResult.didState.did, + id: sovDid, alsoKnownAs: undefined, controller: undefined, verificationMethod: [ { type: 'Ed25519VerificationKey2018', - controller: createResult.didState.did, - id: `${createResult.didState.did}#key-1`, + controller: sovDid, + id: `${sovDid}#key-1`, publicKeyBase58: expect.any(String), }, { - controller: createResult.didState.did, + controller: sovDid, type: 'X25519KeyAgreementKey2019', - id: `${createResult.didState.did}#key-agreement-1`, + id: `${sovDid}#key-agreement-1`, publicKeyBase58: expect.any(String), }, ], capabilityDelegation: undefined, capabilityInvocation: undefined, - authentication: [`${createResult.didState.did}#key-1`], - assertionMethod: [`${createResult.didState.did}#key-1`], - keyAgreement: [`${createResult.didState.did}#key-agreement-1`], - service: undefined, + authentication: [`${sovDid}#key-1`], + assertionMethod: [`${sovDid}#key-1`], + keyAgreement: [`${sovDid}#key-agreement-1`], + service: [ + { + id: `${sovDid}#endpoint`, + serviceEndpoint: 'http://localhost:3000', + type: 'endpoint', + }, + { + id: `${sovDid}#did-communication`, + accept: ['didcomm/aip2;env=rfc19'], + priority: 0, + recipientKeys: [`${sovDid}#key-agreement-1`], + routingKeys: [], + serviceEndpoint: 'http://localhost:3000', + type: 'did-communication', + }, + ], }, didDocumentMetadata: {}, didResolutionMetadata: { From c133538356471a6a0887322a3f6245aa5193e7e4 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 6 Mar 2023 08:26:42 -0300 Subject: [PATCH 077/139] fix(anoncreds): Buffer not imported from core (#1367) Signed-off-by: Ariel Gentile --- packages/anoncreds/src/utils/credential.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/anoncreds/src/utils/credential.ts b/packages/anoncreds/src/utils/credential.ts index eee27cccab..33a7a05c41 100644 --- a/packages/anoncreds/src/utils/credential.ts +++ b/packages/anoncreds/src/utils/credential.ts @@ -1,7 +1,7 @@ import type { AnonCredsSchema, AnonCredsCredentialValues } from '../models' import type { CredentialPreviewAttributeOptions, LinkedAttachment } from '@aries-framework/core' -import { AriesFrameworkError, Hasher, encodeAttachment } from '@aries-framework/core' +import { AriesFrameworkError, Hasher, encodeAttachment, Buffer } from '@aries-framework/core' import BigNumber from 'bn.js' const isString = (value: unknown): value is string => typeof value === 'string' From 953069a785f2a6b8d1e11123aab3a09aab1e65ff Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 6 Mar 2023 10:28:30 -0300 Subject: [PATCH 078/139] fix(core): repository event when calling deleteById (#1356) Signed-off-by: Ariel Gentile --- packages/core/src/storage/Repository.ts | 9 +++++++- packages/core/src/storage/RepositoryEvents.ts | 2 +- .../src/storage/__tests__/Repository.test.ts | 22 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/core/src/storage/Repository.ts b/packages/core/src/storage/Repository.ts index 30cc980d7a..f2cd0ed671 100644 --- a/packages/core/src/storage/Repository.ts +++ b/packages/core/src/storage/Repository.ts @@ -68,12 +68,19 @@ export class Repository> { } /** - * Delete record by id. Returns null if no record is found + * Delete record by id. Throws {RecordNotFoundError} if no record is found * @param id the id of the record to delete * @returns */ public async deleteById(agentContext: AgentContext, id: string): Promise { await this.storageService.deleteById(agentContext, this.recordClass, id) + + this.eventEmitter.emit>(agentContext, { + type: RepositoryEventTypes.RecordDeleted, + payload: { + record: { id, type: this.recordClass.type }, + }, + }) } /** @inheritDoc {StorageService#getById} */ diff --git a/packages/core/src/storage/RepositoryEvents.ts b/packages/core/src/storage/RepositoryEvents.ts index 3e3b1e2952..ac6524eb01 100644 --- a/packages/core/src/storage/RepositoryEvents.ts +++ b/packages/core/src/storage/RepositoryEvents.ts @@ -27,6 +27,6 @@ export interface RecordUpdatedEvent> extends export interface RecordDeletedEvent> extends BaseEvent { type: typeof RepositoryEventTypes.RecordDeleted payload: { - record: T + record: T | { id: string; type: string } } } diff --git a/packages/core/src/storage/__tests__/Repository.test.ts b/packages/core/src/storage/__tests__/Repository.test.ts index a5ae5fd52f..ae56c636af 100644 --- a/packages/core/src/storage/__tests__/Repository.test.ts +++ b/packages/core/src/storage/__tests__/Repository.test.ts @@ -146,6 +146,28 @@ describe('Repository', () => { expect(storageMock.deleteById).toBeCalledWith(agentContext, TestRecord, 'test-id') }) + + it(`should emit deleted event`, async () => { + const eventListenerMock = jest.fn() + eventEmitter.on>(RepositoryEventTypes.RecordDeleted, eventListenerMock) + + const record = getRecord({ id: 'test-id' }) + + await repository.deleteById(agentContext, record.id) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: 'RecordDeleted', + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + record: expect.objectContaining({ + id: record.id, + type: record.type, + }), + }, + }) + }) }) describe('getById()', () => { From 01669a7f9bf60fe6e4a57927d43f1513a64c31ab Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 8 Mar 2023 14:48:34 +0100 Subject: [PATCH 079/139] test: increase timeout to 120 seconds (#1375) Signed-off-by: Timo Glastra --- packages/action-menu/tests/setup.ts | 2 +- packages/anoncreds-rs/tests/setup.ts | 2 +- packages/anoncreds/tests/setup.ts | 2 +- packages/askar/tests/setup.ts | 5 ++--- packages/bbs-signatures/tests/setup.ts | 2 +- packages/indy-sdk/tests/setup.ts | 2 +- packages/indy-vdr/tests/setup.ts | 2 +- packages/openid4vc-client/tests/setup.ts | 2 +- packages/question-answer/tests/setup.ts | 2 +- packages/tenants/tests/setup.ts | 2 +- samples/extension-module/tests/setup.ts | 2 +- 11 files changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/action-menu/tests/setup.ts b/packages/action-menu/tests/setup.ts index 4955aeb601..78143033f2 100644 --- a/packages/action-menu/tests/setup.ts +++ b/packages/action-menu/tests/setup.ts @@ -1,3 +1,3 @@ import 'reflect-metadata' -jest.setTimeout(20000) +jest.setTimeout(120000) diff --git a/packages/anoncreds-rs/tests/setup.ts b/packages/anoncreds-rs/tests/setup.ts index a5fef0aec8..0254a395ff 100644 --- a/packages/anoncreds-rs/tests/setup.ts +++ b/packages/anoncreds-rs/tests/setup.ts @@ -1,3 +1,3 @@ import '@hyperledger/anoncreds-nodejs' -jest.setTimeout(60000) +jest.setTimeout(120000) diff --git a/packages/anoncreds/tests/setup.ts b/packages/anoncreds/tests/setup.ts index 869a89f5d8..34e38c9705 100644 --- a/packages/anoncreds/tests/setup.ts +++ b/packages/anoncreds/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(50000) +jest.setTimeout(120000) diff --git a/packages/askar/tests/setup.ts b/packages/askar/tests/setup.ts index a09e05318c..a0c3ce9de9 100644 --- a/packages/askar/tests/setup.ts +++ b/packages/askar/tests/setup.ts @@ -1,11 +1,10 @@ import 'reflect-metadata' +jest.setTimeout(180000) + try { // eslint-disable-next-line import/no-extraneous-dependencies require('@hyperledger/aries-askar-nodejs') } catch (error) { throw new Error('Could not load aries-askar bindings') } - -// FIXME: Remove when Askar JS Wrapper performance issues are solved -jest.setTimeout(180000) diff --git a/packages/bbs-signatures/tests/setup.ts b/packages/bbs-signatures/tests/setup.ts index 00b77cc0fe..78143033f2 100644 --- a/packages/bbs-signatures/tests/setup.ts +++ b/packages/bbs-signatures/tests/setup.ts @@ -1,3 +1,3 @@ import 'reflect-metadata' -jest.setTimeout(30000) +jest.setTimeout(120000) diff --git a/packages/indy-sdk/tests/setup.ts b/packages/indy-sdk/tests/setup.ts index 7f0aeddaa3..34e38c9705 100644 --- a/packages/indy-sdk/tests/setup.ts +++ b/packages/indy-sdk/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(60000) +jest.setTimeout(120000) diff --git a/packages/indy-vdr/tests/setup.ts b/packages/indy-vdr/tests/setup.ts index a570fb8396..e766b0e868 100644 --- a/packages/indy-vdr/tests/setup.ts +++ b/packages/indy-vdr/tests/setup.ts @@ -3,4 +3,4 @@ import '../src/index' require('@hyperledger/indy-vdr-nodejs') -jest.setTimeout(60000) +jest.setTimeout(120000) diff --git a/packages/openid4vc-client/tests/setup.ts b/packages/openid4vc-client/tests/setup.ts index 226f7031fa..34e38c9705 100644 --- a/packages/openid4vc-client/tests/setup.ts +++ b/packages/openid4vc-client/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(20000) +jest.setTimeout(120000) diff --git a/packages/question-answer/tests/setup.ts b/packages/question-answer/tests/setup.ts index 4955aeb601..78143033f2 100644 --- a/packages/question-answer/tests/setup.ts +++ b/packages/question-answer/tests/setup.ts @@ -1,3 +1,3 @@ import 'reflect-metadata' -jest.setTimeout(20000) +jest.setTimeout(120000) diff --git a/packages/tenants/tests/setup.ts b/packages/tenants/tests/setup.ts index 1a4c59d4ff..78143033f2 100644 --- a/packages/tenants/tests/setup.ts +++ b/packages/tenants/tests/setup.ts @@ -1,3 +1,3 @@ import 'reflect-metadata' -jest.setTimeout(50000) +jest.setTimeout(120000) diff --git a/samples/extension-module/tests/setup.ts b/samples/extension-module/tests/setup.ts index 226f7031fa..34e38c9705 100644 --- a/samples/extension-module/tests/setup.ts +++ b/samples/extension-module/tests/setup.ts @@ -1 +1 @@ -jest.setTimeout(20000) +jest.setTimeout(120000) From 39c4ed0e88a59a7a8bbe763edea7445e9fb20bc4 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 8 Mar 2023 16:52:12 +0100 Subject: [PATCH 080/139] feat(indy-vdr)!: extend did:indy support (#1362) Signed-off-by: Timo Glastra --- .../legacy-indy-format-services.test.ts | 4 +- .../tests/InMemoryAnonCredsRegistry.ts | 25 +- .../anoncreds/tests/legacyAnonCredsSetup.ts | 15 +- packages/core/tests/helpers.ts | 2 +- .../services/IndySdkAnonCredsRegistry.ts | 54 +- .../services/IndySdkIssuerService.ts | 10 +- .../utils/__tests__/identifiers.test.ts | 12 +- .../src/anoncreds/utils/identifiers.ts | 31 +- .../src/dids/IndySdkIndyDidRegistrar.ts | 42 +- .../src/dids/IndySdkIndyDidResolver.ts | 6 +- .../src/dids/IndySdkSovDidResolver.ts | 25 +- .../__tests__/IndySdkIndyDidRegistrar.test.ts | 30 +- packages/indy-sdk/src/dids/didIndyUtil.ts | 4 +- .../tests/indy-did-registrar.e2e.test.ts | 6 +- .../indy-sdk-anoncreds-registry.e2e.test.ts | 47 +- .../tests/sov-did-resolver.e2e.test.ts | 6 +- packages/indy-vdr/package.json | 4 +- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 343 ++++----- .../utils/_tests_/identifier.test.ts | 52 -- .../utils/_tests_/identifiers.test.ts | 185 +++++ .../src/anoncreds/utils/identifiers.ts | 160 +++- .../src/dids/IndyVdrIndyDidRegistrar.ts | 186 +++-- .../src/dids/IndyVdrIndyDidResolver.ts | 76 +- .../src/dids/IndyVdrSovDidResolver.ts | 33 +- .../__tests__/IndyVdrIndyDidRegistrar.test.ts | 706 ++++++++++++++++++ .../__tests__/IndyVdrSovDidResolver.test.ts | 2 +- .../didIndyR1xKJw17sUoXhejEpugMYJ.json | 2 +- .../didIndyWJz9mHyW9BZksioQnRsrAo.json | 7 +- packages/indy-vdr/src/dids/didIndyUtil.ts | 33 +- packages/indy-vdr/src/dids/didSovUtil.ts | 13 +- .../indy-vdr/src/pool/IndyVdrPoolService.ts | 22 +- .../indy-vdr/tests/__fixtures__/anoncreds.ts | 30 + packages/indy-vdr/tests/helpers.ts | 78 +- .../indy-vdr-anoncreds-registry.e2e.test.ts | 309 ++++---- .../tests/indy-vdr-did-registrar.e2e.test.ts | 136 ++-- .../tests/indy-vdr-did-resolver.e2e.test.ts | 181 ----- .../indy-vdr-indy-did-resolver.e2e.test.ts | 143 ++++ .../indy-vdr-sov-did-resolver.e2e.test.ts | 157 ++++ yarn.lock | 18 +- 39 files changed, 2210 insertions(+), 985 deletions(-) delete mode 100644 packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts create mode 100644 packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts create mode 100644 packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts create mode 100644 packages/indy-vdr/tests/__fixtures__/anoncreds.ts delete mode 100644 packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts create mode 100644 packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts create mode 100644 packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index 4606899650..af5686ad5d 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -163,10 +163,10 @@ describe('Legacy indy format services', () => { ] const cd = parseCredentialDefinitionId(credentialDefinitionState.credentialDefinitionId) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.didIdentifier, cd.schemaSeqNo, cd.tag) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.namespaceIdentifier, cd.schemaSeqNo, cd.tag) const s = parseSchemaId(schemaState.schemaId) - const legacySchemaId = getLegacySchemaId(s.didIdentifier, s.schemaName, s.schemaVersion) + const legacySchemaId = getLegacySchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion) // Holder creates proposal holderCredentialRecord.credentialAttributes = credentialAttributes diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index abd3ddd6ed..5c2fc9954a 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -64,7 +64,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { const parsed = parseSchemaId(schemaId) - const legacySchemaId = getLegacySchemaId(parsed.didIdentifier, parsed.schemaName, parsed.schemaVersion) + const legacySchemaId = getLegacySchemaId(parsed.namespaceIdentifier, parsed.schemaName, parsed.schemaVersion) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) if (!schema) { @@ -94,16 +94,21 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { agentContext: AgentContext, options: RegisterSchemaOptions ): Promise { - const { id: didIdentifier, namespace } = parseIndyDid(options.schema.issuerId) - const didIndySchemaId = getDidIndySchemaId(namespace, didIdentifier, options.schema.name, options.schema.version) - const legacySchemaId = getLegacySchemaId(didIdentifier, options.schema.name, options.schema.version) + const { namespaceIdentifier, namespace } = parseIndyDid(options.schema.issuerId) + const didIndySchemaId = getDidIndySchemaId( + namespace, + namespaceIdentifier, + options.schema.name, + options.schema.version + ) + const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) this.schemas[didIndySchemaId] = options.schema this.schemas[legacySchemaId] = { ...options.schema, - issuerId: didIdentifier, + issuerId: namespaceIdentifier, } return { @@ -152,22 +157,22 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { ): Promise { const parsedSchema = parseSchemaId(options.credentialDefinition.schemaId) const legacySchemaId = getLegacySchemaId( - parsedSchema.didIdentifier, + parsedSchema.namespaceIdentifier, parsedSchema.schemaName, parsedSchema.schemaVersion ) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) - const { id: didIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) + const { namespaceIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( namespace, - didIdentifier, + namespaceIdentifier, indyLedgerSeqNo, options.credentialDefinition.tag ) const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( - didIdentifier, + namespaceIdentifier, indyLedgerSeqNo, options.credentialDefinition.tag ) @@ -175,7 +180,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition this.credentialDefinitions[legacyCredentialDefinitionId] = { ...options.credentialDefinition, - issuerId: didIdentifier, + issuerId: namespaceIdentifier, schemaId: legacySchemaId, } diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 5895e5c075..d0e40a33a6 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -60,7 +60,13 @@ import { parseSchemaId, } from '../../indy-sdk/src/anoncreds/utils/identifiers' import { getIndySdkModuleConfig } from '../../indy-sdk/tests/setupIndySdkModule' -import { IndyVdrAnonCredsRegistry, IndyVdrSovDidResolver, IndyVdrModule } from '../../indy-vdr/src' +import { + IndyVdrAnonCredsRegistry, + IndyVdrSovDidResolver, + IndyVdrModule, + IndyVdrIndyDidResolver, + IndyVdrIndyDidRegistrar, +} from '../../indy-vdr/src' import { V1CredentialProtocol, V1ProofProtocol, @@ -163,7 +169,8 @@ export const getAskarAnonCredsIndyModules = ({ networks: [indyNetworkConfig], }), dids: new DidsModule({ - resolvers: [new IndyVdrSovDidResolver()], // TODO: Support Registrar for tests + resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()], + registrars: [new IndyVdrIndyDidRegistrar()], }), askar: new AskarModule(), cache: new CacheModule({ @@ -474,8 +481,8 @@ export async function prepareForAnonCredsIssuance(agent: Agent, { attributeNames const s = parseSchemaId(schema.schemaId) const cd = parseCredentialDefinitionId(credentialDefinition.credentialDefinitionId) - const legacySchemaId = getLegacySchemaId(s.didIdentifier, s.schemaName, s.schemaVersion) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.didIdentifier, cd.schemaSeqNo, cd.tag) + const legacySchemaId = getLegacySchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.namespaceIdentifier, cd.schemaSeqNo, cd.tag) // Wait some time pass to let ledger settle the object await sleep(1000) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 2d11ae4109..034c046aef 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -133,7 +133,7 @@ export async function importExistingIndyDidFromPrivateKey(agent: Agent, privateK const unqualifiedIndyDid = TypedArrayEncoder.toBase58(key.publicKey.slice(0, 16)) // import the did in the wallet so it can be used - await agent.dids.import({ did: `did:sov:${unqualifiedIndyDid}` }) + await agent.dids.import({ did: `did:indy:pool:localtest:${unqualifiedIndyDid}` }) return unqualifiedIndyDid } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index fe0d30e35b..8c222b1a18 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -31,12 +31,9 @@ import { } from '../utils/identifiers' import { anonCredsRevocationStatusListFromIndySdk } from '../utils/transform' -/** - * TODO: validation of the identifiers. The Indy SDK classes only support the legacy (unqualified) identifiers. - */ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { /** - * This class only supports resolving and registering objects with legacy indy identifiers. + * This class supports resolving and registering objects with did:indy as well as legacy indy identifiers. * It needs to include support for the schema, credential definition, revocation registry as well * as the issuer id (which is needed when registering objects). */ @@ -48,12 +45,12 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) // parse schema id (supports did:indy and legacy) - const { did, didIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId) + const { did, namespaceIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`) // even though we support did:indy and legacy identifiers we always need to fetch using the legacy identifier - const legacySchemaId = getLegacySchemaId(didIdentifier, schemaName, schemaVersion) + const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schemaName, schemaVersion) const request = await indySdk.buildGetSchemaRequest(null, legacySchemaId) agentContext.config.logger.trace( @@ -110,7 +107,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { try { // This will throw an error if trying to register a schema with a legacy indy identifier. We only support did:indy identifiers // for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. - const { id: unqualifiedDid, namespace } = parseIndyDid(options.schema.issuerId) + const { namespaceIdentifier, namespace } = parseIndyDid(options.schema.issuerId) const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) @@ -121,8 +118,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { options.schema ) - const didIndySchemaId = getDidIndySchemaId(namespace, unqualifiedDid, options.schema.name, options.schema.version) - const legacySchemaId = getLegacySchemaId(unqualifiedDid, options.schema.name, options.schema.version) + const didIndySchemaId = getDidIndySchemaId( + namespace, + namespaceIdentifier, + options.schema.name, + options.schema.version + ) + const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version) const schema = { attrNames: options.schema.attrNames, @@ -134,7 +136,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { // buildSchemaRequest (seqNo is not yet known) } as IndySdkSchema - const request = await indySdk.buildSchemaRequest(unqualifiedDid, schema) + const request = await indySdk.buildSchemaRequest(namespaceIdentifier, schema) const submitterKey = await verificationKeyForIndyDid(agentContext, options.schema.issuerId) const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) @@ -189,14 +191,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) // we support did:indy and legacy identifiers - const { did, didIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId) + const { did, namespaceIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve credential definition '${credentialDefinitionId}'` ) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(didIdentifier, schemaSeqNo, tag) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) const request = await indySdk.buildGetCredDefRequest(null, legacyCredentialDefinitionId) agentContext.config.logger.trace( @@ -224,7 +226,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { // Format the schema id based on the type of the credential definition id const schemaId = credentialDefinitionId.startsWith('did:indy') - ? getDidIndySchemaId(pool.didIndyNamespace, didIdentifier, schema.name, schema.version) + ? getDidIndySchemaId(pool.didIndyNamespace, namespaceIdentifier, schema.name, schema.version) : schema.schemaId return { @@ -279,7 +281,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { try { // This will throw an error if trying to register a credential defintion with a legacy indy identifier. We only support did:indy // identifiers for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. - const { id: unqualifiedDid, namespace } = parseIndyDid(options.credentialDefinition.issuerId) + const { namespaceIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) @@ -312,18 +314,18 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( - unqualifiedDid, + namespaceIdentifier, schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag ) const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( namespace, - unqualifiedDid, + namespaceIdentifier, schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag ) - const request = await indySdk.buildCredDefRequest(unqualifiedDid, { + const request = await indySdk.buildCredDefRequest(namespaceIdentifier, { id: legacyCredentialDefinitionId, // Indy ledger requires the credential schemaId to be a string of the schema seqNo. schemaId: schemaMetadata.indyLedgerSeqNo.toString(), @@ -334,7 +336,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { }) const submitterKey = await verificationKeyForIndyDid(agentContext, options.credentialDefinition.issuerId) - const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey) agentContext.config.logger.debug( @@ -376,7 +377,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const { did, didIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = + const { did, namespaceIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = parseRevocationRegistryId(revocationRegistryDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) @@ -385,7 +386,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) const legacyRevocationRegistryId = getLegacyRevocationRegistryId( - didIdentifier, + namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag @@ -413,8 +414,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) const credentialDefinitionId = revocationRegistryDefinitionId.startsWith('did:indy:') - ? getDidIndyCredentialDefinitionId(pool.didIndyNamespace, didIdentifier, schemaSeqNo, credentialDefinitionTag) - : getLegacyCredentialDefinitionId(didIdentifier, schemaSeqNo, credentialDefinitionTag) + ? getDidIndyCredentialDefinitionId( + pool.didIndyNamespace, + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag + ) + : getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag) return { resolutionMetadata: {}, @@ -465,7 +471,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) - const { did, didIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = + const { did, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = parseRevocationRegistryId(revocationRegistryId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) @@ -474,7 +480,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { ) const legacyRevocationRegistryId = getLegacyRevocationRegistryId( - didIdentifier, + namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index 2b5365522b..72abbbdea8 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -33,13 +33,13 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { // We only support passing qualified did:indy issuer ids in the indy issuer service when creating objects - const { id: unqualifiedDid } = parseIndyDid(options.issuerId) + const { namespaceIdentifier } = parseIndyDid(options.issuerId) const { name, version, attrNames, issuerId } = options assertIndySdkWallet(agentContext.wallet) try { - const [, schema] = await this.indySdk.issuerCreateSchema(unqualifiedDid, name, version, attrNames) + const [, schema] = await this.indySdk.issuerCreateSchema(namespaceIdentifier, name, version, attrNames) return { issuerId, @@ -60,10 +60,10 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { const { tag, supportRevocation, schema, issuerId, schemaId } = options // We only support passing qualified did:indy issuer ids in the indy issuer service when creating objects - const { id: unqualifiedDid } = parseIndyDid(options.issuerId) + const { namespaceIdentifier } = parseIndyDid(options.issuerId) // parse schema in a way that supports both unqualified and qualified identifiers - const legacySchemaId = getLegacySchemaId(unqualifiedDid, schema.name, schema.version) + const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schema.name, schema.version) if (!metadata) throw new AriesFrameworkError('The metadata parameter is required when using Indy, but received undefined.') @@ -72,7 +72,7 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { assertIndySdkWallet(agentContext.wallet) const [, credentialDefinition] = await this.indySdk.issuerCreateAndStoreCredentialDef( agentContext.wallet.handle, - unqualifiedDid, + namespaceIdentifier, indySdkSchemaFromAnonCreds(legacySchemaId, schema, metadata.indyLedgerSchemaSeqNo), tag, 'CL', diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts index ca1751c4e2..74488d4108 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -114,7 +114,7 @@ describe('identifiers', () => { test('parses legacy schema id', () => { expect(parseSchemaId('SDqTzbVuCowusqGBNbNDjH:2:schema-name:1.0')).toEqual({ did: 'SDqTzbVuCowusqGBNbNDjH', - didIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', schemaName: 'schema-name', schemaVersion: '1.0', }) @@ -123,7 +123,7 @@ describe('identifiers', () => { test('parses did:indy schema id', () => { expect(parseSchemaId('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0')).toEqual( { - didIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', did: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', schemaName: 'schema-name', schemaVersion: '1.0', @@ -137,7 +137,7 @@ describe('identifiers', () => { test('parses legacy credential definition id', () => { expect(parseCredentialDefinitionId('TL1EaPFCZ8Si5aUrqScBDt:3:CL:10:TAG')).toEqual({ did: 'TL1EaPFCZ8Si5aUrqScBDt', - didIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', schemaSeqNo: '10', tag: 'TAG', }) @@ -147,7 +147,7 @@ describe('identifiers', () => { expect( parseCredentialDefinitionId('did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/10/TAG') ).toEqual({ - didIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', did: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', namespace: 'pool:localtest', schemaSeqNo: '10', @@ -162,7 +162,7 @@ describe('identifiers', () => { parseRevocationRegistryId('5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1') ).toEqual({ did: '5nDyJVP1NrcPAttP3xwMB9', - didIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', schemaSeqNo: '56495', credentialDefinitionTag: 'npdb', revocationRegistryTag: 'TAG1', @@ -174,7 +174,7 @@ describe('identifiers', () => { parseRevocationRegistryId('did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_DEF/56495/npdb/TAG1') ).toEqual({ namespace: 'sovrin', - didIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', did: 'did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9', schemaSeqNo: '56495', credentialDefinitionTag: 'npdb', diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts index 8300a7ea29..19f1df864c 100644 --- a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -1,34 +1,39 @@ +/** + * NOTE: this file is availalbe in both the indy-sdk and indy-vdr packages. If making changes to + * this file, make sure to update both files if applicable. + */ + import { DID_INDY_REGEX } from '../../utils/did' const didIndyAnonCredsBase = - /(?did:indy:(?((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?)):(?([1-9A-HJ-NP-Za-km-z]{21,22})))\/anoncreds\/v0/ + /(?did:indy:(?((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?)):(?([1-9A-HJ-NP-Za-km-z]{21,22})))\/anoncreds\/v0/ -// did:indy::/anoncreds/v0/SCHEMA// +// did:indy::/anoncreds/v0/SCHEMA// const didIndySchemaIdRegex = new RegExp( `^${didIndyAnonCredsBase.source}/SCHEMA/(?.+)/(?[0-9.]+)$` ) -// :2:: +// :2:: const legacyIndySchemaIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):2:(?.+):(?[0-9.]+)$/ + /^(?(?[a-zA-Z0-9]{21,22})):2:(?.+):(?[0-9.]+)$/ -// did:indy::/anoncreds/v0/CLAIM_DEF// +// did:indy::/anoncreds/v0/CLAIM_DEF// const didIndyCredentialDefinitionIdRegex = new RegExp( `^${didIndyAnonCredsBase.source}/CLAIM_DEF/(?[1-9][0-9]*)/(?.+)$` ) -// :3:CL:: +// :3:CL:: const legacyIndyCredentialDefinitionIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):3:CL:(?[1-9][0-9]*):(?.+)$/ + /^(?(?[a-zA-Z0-9]{21,22})):3:CL:(?[1-9][0-9]*):(?.+)$/ -// did:indy::/anoncreds/v0/REV_REG_DEF/// +// did:indy::/anoncreds/v0/REV_REG_DEF/// const didIndyRevocationRegistryIdRegex = new RegExp( `^${didIndyAnonCredsBase.source}/REV_REG_DEF/(?[1-9][0-9]*)/(?.+)/(?.+)$` ) -// :4::3:CL::CL_ACCUM: +// :4::3:CL::CL_ACCUM: const legacyIndyRevocationRegistryIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):4:[a-zA-Z0-9]{21,22}:3:CL:(?[1-9][0-9]*):(?.+):CL_ACCUM:(?.+)$/ + /^(?(?[a-zA-Z0-9]{21,22})):4:[a-zA-Z0-9]{21,22}:3:CL:(?[1-9][0-9]*):(?.+):CL_ACCUM:(?.+)$/ // combines both legacy and did:indy anoncreds identifiers and also the issuer id const indySdkAnonCredsRegexes = [ @@ -99,7 +104,7 @@ export function getDidIndyRevocationRegistryId( interface ParsedSchemaId { did: string - didIdentifier: string + namespaceIdentifier: string schemaName: string schemaVersion: string namespace?: string @@ -115,7 +120,7 @@ export function parseSchemaId(schemaId: string) { interface ParsedCredentialDefinitionId { did: string - didIdentifier: string + namespaceIdentifier: string schemaSeqNo: string tag: string namespace?: string @@ -133,7 +138,7 @@ export function parseCredentialDefinitionId(credentialDefinitionId: string) { interface ParsedRevocationRegistryId { did: string - didIdentifier: string + namespaceIdentifier: string schemaSeqNo: string credentialDefinitionTag: string revocationRegistryTag: string diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts index 77689dcde6..a7aba8eab1 100644 --- a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts @@ -33,7 +33,7 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { const { alias, role, submitterDid, endpoints } = options.options let did = options.did - let didIdentifier: string + let namespaceIdentifier: string let verificationKey: Key const privateKey = options.secret?.privateKey @@ -52,7 +52,8 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { assertIndySdkWallet(agentContext.wallet) // Parse submitterDid and extract namespace based on the submitter did - const { namespace: submitterNamespace, id: submitterDidIdentifier } = parseIndyDid(submitterDid) + const { namespace: submitterNamespace, namespaceIdentifier: submitterNamespaceIdentifier } = + parseIndyDid(submitterDid) const submitterSigningKey = await verificationKeyForIndyDid(agentContext, submitterDid) // Only supports version 1 did identifier (which is same as did:sov) @@ -68,11 +69,12 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { } } - const { namespace, id } = parseIndyDid(did) - didIdentifier = id + const { namespace, namespaceIdentifier: _namespaceIdentifier } = parseIndyDid(did) + namespaceIdentifier = _namespaceIdentifier + verificationKey = Key.fromPublicKeyBase58(options.options.verkey, KeyType.Ed25519) - if (!isLegacySelfCertifiedDid(didIdentifier, options.options.verkey)) { + if (!isLegacySelfCertifiedDid(namespaceIdentifier, options.options.verkey)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -96,17 +98,17 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { } else { // Create a new key and calculate did according to the rules for indy did method verificationKey = await agentContext.wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) - didIdentifier = legacyIndyDidFromPublicKeyBase58(verificationKey.publicKeyBase58) - did = `did:indy:${submitterNamespace}:${didIdentifier}` + namespaceIdentifier = legacyIndyDidFromPublicKeyBase58(verificationKey.publicKeyBase58) + did = `did:indy:${submitterNamespace}:${namespaceIdentifier}` } const pool = indySdkPoolService.getPoolForNamespace(submitterNamespace) await this.registerPublicDid( agentContext, pool, - submitterDidIdentifier, + submitterNamespaceIdentifier, submitterSigningKey, - didIdentifier, + namespaceIdentifier, verificationKey, alias, role @@ -119,7 +121,7 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { if (endpoints) { const keyAgreementId = `${did}#key-agreement-1` - await this.setEndpointsForDid(agentContext, pool, didIdentifier, verificationKey, endpoints) + await this.setEndpointsForDid(agentContext, pool, namespaceIdentifier, verificationKey, endpoints) didDocumentBuilder .addContext('https://w3id.org/security/suites/x25519-2019/v1') @@ -199,7 +201,7 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { } } - public async registerPublicDid( + private async registerPublicDid( agentContext: AgentContext, pool: IndySdkPool, unqualifiedSubmitterDid: string, @@ -249,7 +251,7 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { } } - public async setEndpointsForDid( + private async setEndpointsForDid( agentContext: AgentContext, pool: IndySdkPool, unqualifiedDid: string, @@ -296,9 +298,7 @@ export class IndySdkIndyDidRegistrar implements DidRegistrar { } } -export interface IndySdkIndyDidCreateOptions extends DidCreateOptions { - method: 'indy' - did?: string +interface IndySdkIndyDidCreateOptionsBase extends DidCreateOptions { // The indy sdk can only publish a very limited did document (what is mostly known as a legacy did:sov did) and thus we require everything // needed to construct the did document to be passed through the options object. didDocument?: never @@ -313,3 +313,15 @@ export interface IndySdkIndyDidCreateOptions extends DidCreateOptions { privateKey?: Buffer } } + +interface IndySdkIndyDidCreateOptionsWithDid extends IndySdkIndyDidCreateOptionsBase { + method?: never + did: string +} + +interface IndySdkIndyDidCreateOptionsWithoutDid extends IndySdkIndyDidCreateOptionsBase { + method: 'indy' + did?: never +} + +export type IndySdkIndyDidCreateOptions = IndySdkIndyDidCreateOptionsWithDid | IndySdkIndyDidCreateOptionsWithoutDid diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts index 836ed8040b..4aa0ddf1d3 100644 --- a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts @@ -18,13 +18,13 @@ export class IndySdkIndyDidResolver implements DidResolver { const didDocumentMetadata = {} try { - const { id: unqualifiedDid, namespace } = parseIndyDid(did) + const { namespaceIdentifier, namespace } = parseIndyDid(did) const poolService = agentContext.dependencyManager.resolve(IndySdkPoolService) const pool = poolService.getPoolForNamespace(namespace) - const nym = await this.getPublicDid(agentContext, pool, unqualifiedDid) - const endpoints = await this.getEndpointsForDid(agentContext, pool, unqualifiedDid) + const nym = await this.getPublicDid(agentContext, pool, namespaceIdentifier) + const endpoints = await this.getEndpointsForDid(agentContext, pool, namespaceIdentifier) // For modern did:indy DIDs, we assume that GET_NYM is always a full verkey in base58. // For backwards compatibility, we accept a shortened verkey and convert it using previous convention diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts index bbbeec71f8..ff6afc9571 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidResolver.ts @@ -23,7 +23,10 @@ export class IndySdkSovDidResolver implements DidResolver { const keyAgreementId = `${parsed.did}#key-agreement-1` const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) - addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) + + if (endpoints) { + addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) + } return { didDocument: builder.build(), @@ -52,35 +55,39 @@ export class IndySdkSovDidResolver implements DidResolver { return await indySdk.parseGetNymResponse(response) } - private async getEndpointsForDid(agentContext: AgentContext, pool: IndySdkPool, did: string) { + private async getEndpointsForDid(agentContext: AgentContext, pool: IndySdkPool, unqualifiedDid: string) { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService) try { - agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.didIndyNamespace}'`) + agentContext.config.logger.debug( + `Get endpoints for did '${unqualifiedDid}' from ledger '${pool.didIndyNamespace}'` + ) - const request = await indySdk.buildGetAttribRequest(null, did, 'endpoint', null, null) + const request = await indySdk.buildGetAttribRequest(null, unqualifiedDid, 'endpoint', null, null) agentContext.config.logger.debug( - `Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.didIndyNamespace}'` + `Submitting get endpoint ATTRIB request for did '${unqualifiedDid}' to ledger '${pool.didIndyNamespace}'` ) const response = await indySdkPoolService.submitReadRequest(pool, request) - if (!response.result.data) return {} + if (!response.result.data) return null const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib agentContext.config.logger.debug( - `Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.didIndyNamespace}'`, + `Got endpoints '${JSON.stringify(endpoints)}' for did '${unqualifiedDid}' from ledger '${ + pool.didIndyNamespace + }'`, { response, endpoints, } ) - return endpoints ?? {} + return endpoints ?? null } catch (error) { agentContext.config.logger.error( - `Error retrieving endpoints for did '${did}' from ledger '${pool.didIndyNamespace}'`, + `Error retrieving endpoints for did '${unqualifiedDid}' from ledger '${pool.didIndyNamespace}'`, { error, } diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts index 4d4390bd24..b087c499f5 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts @@ -78,7 +78,6 @@ describe('IndySdkIndyDidRegistrar', () => { test('returns an error state if both did and privateKey are provided', async () => { const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', did: 'did:indy:pool1:did-value', options: { submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', @@ -120,7 +119,6 @@ describe('IndySdkIndyDidRegistrar', () => { test('returns an error state if did is provided, but it is not a valid did:indy did', async () => { const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', did: 'BzCbsNYhMrjHiqZDTUASHg', options: { submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', @@ -141,7 +139,6 @@ describe('IndySdkIndyDidRegistrar', () => { test('returns an error state if did is provided, but no verkey', async () => { const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', did: 'BzCbsNYhMrjHiqZDTUASHg', options: { submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', @@ -161,7 +158,6 @@ describe('IndySdkIndyDidRegistrar', () => { test('returns an error state if did and verkey are provided, but the did is not self certifying', async () => { const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', did: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', options: { submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', @@ -182,7 +178,6 @@ describe('IndySdkIndyDidRegistrar', () => { test('returns an error state if did is provided, but does not match with the namespace from the submitterDid', async () => { const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', did: 'did:indy:pool2:R1xKJw17sUoXhejEpugMYJ', options: { submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', @@ -205,7 +200,9 @@ describe('IndySdkIndyDidRegistrar', () => { test('creates a did:indy document without services', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore method is private + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) const result = await indySdkIndyDidRegistrar.create(agentContext, { @@ -264,11 +261,12 @@ describe('IndySdkIndyDidRegistrar', () => { }) test('creates a did:indy document by passing did', async () => { - const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore method is private + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) const result = await indySdkIndyDidRegistrar.create(agentContext, { - method: 'indy', did: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ', options: { verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', @@ -323,10 +321,14 @@ describe('IndySdkIndyDidRegistrar', () => { test('creates a did:indy document with services', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore method is private + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore method is private + const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) const result = await indySdkIndyDidRegistrar.create(agentContext, { @@ -427,10 +429,14 @@ describe('IndySdkIndyDidRegistrar', () => { test('stores the did document', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore method is private + const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore method is private + const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) const saveCalled = jest.fn() diff --git a/packages/indy-sdk/src/dids/didIndyUtil.ts b/packages/indy-sdk/src/dids/didIndyUtil.ts index af8adacf77..928ae1007e 100644 --- a/packages/indy-sdk/src/dids/didIndyUtil.ts +++ b/packages/indy-sdk/src/dids/didIndyUtil.ts @@ -14,8 +14,8 @@ import { DID_INDY_REGEX } from '../utils/did' export function parseIndyDid(did: string) { const match = did.match(DID_INDY_REGEX) if (match) { - const [, namespace, id] = match - return { namespace, id } + const [, namespace, namespaceIdentifier] = match + return { namespace, namespaceIdentifier } } else { throw new AriesFrameworkError(`${did} is not a valid did:indy did`) } diff --git a/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts b/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts index c007eaa561..04781e2e62 100644 --- a/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts @@ -9,12 +9,10 @@ import { legacyIndyDidFromPublicKeyBase58 } from '../src/utils/did' import { getIndySdkModules } from './setupIndySdkModule' const agentOptions = getAgentOptions('Indy Sdk Indy Did Registrar', {}, getIndySdkModules()) +const agent = new Agent(agentOptions) -describe('dids', () => { - let agent: Agent> - +describe('Indy SDK Indy Did Registrar', () => { beforeAll(async () => { - agent = new Agent(agentOptions) await agent.initialize() }) diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index 77e034941d..12a7cd6848 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -8,7 +8,6 @@ import { } from '../../core/tests/helpers' import { IndySdkAnonCredsRegistry } from '../src/anoncreds/services/IndySdkAnonCredsRegistry' import { IndySdkPoolService } from '../src/ledger' -import { assertIndySdkWallet } from '../src/utils/assertIndySdkWallet' import { credentialDefinitionValue } from './__fixtures__/anoncreds' import { getIndySdkModules, indySdk } from './setupIndySdkModule' @@ -136,7 +135,7 @@ describe('IndySdkAnonCredsRegistry', () => { tag: 'TAG', schemaId: didIndySchemaId, type: 'CL', - value: {}, + value: credentialDefinitionValue, }, credentialDefinitionId: didIndyCredentialDefinitionId, state: 'finished', @@ -188,8 +187,6 @@ describe('IndySdkAnonCredsRegistry', () => { resolutionMetadata: {}, }) - assertIndySdkWallet(agent.context.wallet) - // We don't support creating a revocation registry using AFJ yet, so we directly use indy-sdk to register the revocation registry const legacyRevocationRegistryId = `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` const didIndyRevocationRegistryId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/REV_REG_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` @@ -214,6 +211,27 @@ describe('IndySdkAnonCredsRegistry', () => { await indySdkPoolService.submitWriteRequest(agent.context, pool, revocationRegistryRequest, signingKey) + // indySdk.buildRevRegEntry panics, so we just pass a custom request directly + const entryResponse = await indySdkPoolService.submitWriteRequest( + agent.context, + pool, + { + identifier: legacyIssuerId, + operation: { + revocDefType: 'CL_ACCUM', + revocRegDefId: legacyRevocationRegistryId, + type: '114', + value: { + accum: + '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', + }, + }, + protocolVersion: 2, + reqId: Math.floor(Math.random() * 1000000), + }, + signingKey + ) + const legacyRevocationRegistryDefinition = await indySdkAnonCredsRegistry.getRevocationRegistryDefinition( agent.context, legacyRevocationRegistryId @@ -274,27 +292,6 @@ describe('IndySdkAnonCredsRegistry', () => { resolutionMetadata: {}, }) - // indySdk.buildRevRegEntry panics, so we just pass a custom request directly - const entryResponse = await indySdkPoolService.submitWriteRequest( - agent.context, - pool, - { - identifier: legacyIssuerId, - operation: { - revocDefType: 'CL_ACCUM', - revocRegDefId: legacyRevocationRegistryId, - type: '114', - value: { - accum: - '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', - }, - }, - protocolVersion: 2, - reqId: Math.floor(Math.random() * 1000000), - }, - signingKey - ) - const legacyRevocationStatusList = await indySdkAnonCredsRegistry.getRevocationStatusList( agent.context, legacyRevocationRegistryId, diff --git a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts index ef220a59a3..fd33a35696 100644 --- a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts +++ b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts @@ -19,7 +19,7 @@ describe('Indy SDK Sov DID resolver', () => { await agent.wallet.delete() }) - it('should resolve a did:sov did', async () => { + test('resolve a did:sov did', async () => { // Add existing endorser did to the wallet const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( agent, @@ -43,8 +43,8 @@ describe('Indy SDK Sov DID resolver', () => { if (!createResult.didState.did) throw new AriesFrameworkError('Unable to register did') - const { id: unqualifiedDid } = parseIndyDid(createResult.didState.did) - const sovDid = `did:sov:${unqualifiedDid}` + const { namespaceIdentifier } = parseIndyDid(createResult.didState.did) + const sovDid = `did:sov:${namespaceIdentifier}` const didResult = await agent.dids.resolve(sovDid) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 088c8da018..98b6d946f5 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -26,10 +26,10 @@ "dependencies": { "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", - "@hyperledger/indy-vdr-shared": "^0.1.0-dev.6" + "@hyperledger/indy-vdr-shared": "^0.1.0-dev.10" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.6", + "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.10", "@stablelib/ed25519": "^1.0.2", "rimraf": "^4.0.7", "rxjs": "^7.2.0", diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 4b787414b6..ca2c1149f0 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -8,10 +8,10 @@ import type { RegisterCredentialDefinitionReturn, GetRevocationStatusListReturn, GetRevocationRegistryDefinitionReturn, + AnonCredsRevocationRegistryDefinition, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' -import { getKeyFromVerificationMethod, DidsApi } from '@aries-framework/core' import { GetSchemaRequest, SchemaRequest, @@ -22,15 +22,19 @@ import { GetRevocationRegistryDefinitionRequest, } from '@hyperledger/indy-vdr-shared' +import { parseIndyDid, verificationKeyForIndyDid } from '../dids/didIndyUtil' import { IndyVdrPoolService } from '../pool' import { - didFromSchemaId, - didFromCredentialDefinitionId, - didFromRevocationRegistryDefinitionId, getLegacySchemaId, getLegacyCredentialDefinitionId, indyVdrAnonCredsRegistryIdentifierRegex, + parseSchemaId, + getDidIndySchemaId, + parseCredentialDefinitionId, + getDidIndyCredentialDefinitionId, + parseRevocationRegistryId, + getLegacyRevocationRegistryId, } from './utils/identifiers' import { anonCredsRevocationStatusListFromIndyVdr } from './utils/transform' @@ -41,12 +45,14 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { try { const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const did = didFromSchemaId(schemaId) - - const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) - + // parse schema id (supports did:indy and legacy) + const { did, namespaceIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId) + const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.indyNamespace}'`) - const request = new GetSchemaRequest({ submitterDid: did, schemaId }) + + // even though we support did:indy and legacy identifiers we always need to fetch using the legacy identifier + const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schemaName, schemaVersion) + const request = new GetSchemaRequest({ schemaId: legacySchemaId }) agentContext.config.logger.trace( `Submitting get schema request for schema '${schemaId}' to ledger '${pool.indyNamespace}'` @@ -57,36 +63,34 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { response, }) - const issuerId = didFromSchemaId(schemaId) + if (!('attr_names' in response.result.data)) { + agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`) - if ('attr_names' in response.result.data) { return { - schema: { - attrNames: response.result.data.attr_names, - name: response.result.data.name, - version: response.result.data.version, - issuerId, - }, - schemaId: schemaId, - resolutionMetadata: {}, - schemaMetadata: { - didIndyNamespace: pool.indyNamespace, - // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. - // For this reason we return it in the metadata. - indyLedgerSeqNo: response.result.seqNo, + schemaId, + resolutionMetadata: { + error: 'notFound', + message: `unable to find schema with id ${schemaId}`, }, + schemaMetadata: {}, } } - agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`) - return { + schema: { + attrNames: response.result.data.attr_names, + name: response.result.data.name, + version: response.result.data.version, + issuerId: did, + }, schemaId, - resolutionMetadata: { - error: 'notFound', - message: `unable to find schema with id ${schemaId}`, + resolutionMetadata: {}, + schemaMetadata: { + didIndyNamespace: pool.indyNamespace, + // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. + // For this reason we return it in the metadata. + indyLedgerSeqNo: response.result.seqNo, }, - schemaMetadata: {}, } } catch (error) { agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`, { @@ -106,27 +110,33 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { public async registerSchema( agentContext: AgentContext, - options: IndyVdrRegisterSchemaOptions + options: RegisterSchemaOptions ): Promise { - if (!options.options.didIndyNamespace) { - return { - schemaMetadata: {}, - registrationMetadata: {}, - schemaState: { - reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy VDR', - schema: options.schema, - state: 'failed', - }, - } - } - try { + // This will throw an error if trying to register a schema with a legacy indy identifier. We only support did:indy identifiers + // for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. + const { namespaceIdentifier, namespace } = parseIndyDid(options.schema.issuerId) + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + const pool = indyVdrPoolService.getPoolForNamespace(namespace) + agentContext.config.logger.debug( + `Register schema on ledger '${pool.indyNamespace}' with did '${options.schema.issuerId}'`, + options.schema + ) + + const didIndySchemaId = getDidIndySchemaId( + namespace, + namespaceIdentifier, + options.schema.name, + options.schema.version + ) + const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version) + const schemaRequest = new SchemaRequest({ - submitterDid: options.schema.issuerId, + submitterDid: namespaceIdentifier, schema: { - id: getLegacySchemaId(options.schema.issuerId, options.schema.name, options.schema.version), + id: legacySchemaId, name: options.schema.name, ver: '1.0', version: options.schema.version, @@ -134,29 +144,12 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { }, }) - const pool = indyVdrPoolService.getPoolForNamespace(options.options.didIndyNamespace) - - // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did - // from the ledger to know which key is associated with the did - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - const didResult = await didsApi.resolve(`did:sov:${options.schema.issuerId}`) - - if (!didResult.didDocument) { - return { - schemaMetadata: {}, - registrationMetadata: {}, - schemaState: { - schema: options.schema, - state: 'failed', - reason: `didNotFound: unable to resolve did did:sov:${options.schema.issuerId}: ${didResult.didResolutionMetadata.message}`, - }, - } - } - - const verificationMethod = didResult.didDocument.dereferenceKey(`did:sov:${options.schema.issuerId}#key-1`) - const key = getKeyFromVerificationMethod(verificationMethod) - - const response = await pool.submitWriteRequest(agentContext, schemaRequest, key) + const submitterKey = await verificationKeyForIndyDid(agentContext, options.schema.issuerId) + const response = await pool.submitWriteRequest(agentContext, schemaRequest, submitterKey) + agentContext.config.logger.debug(`Registered schema '${didIndySchemaId}' on ledger '${pool.indyNamespace}'`, { + response, + schemaRequest, + }) return { schemaState: { @@ -167,14 +160,13 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { name: options.schema.name, version: options.schema.version, }, - schemaId: getLegacySchemaId(options.schema.issuerId, options.schema.name, options.schema.version), + schemaId: didIndySchemaId, }, registrationMetadata: {}, schemaMetadata: { // NOTE: the seqNo is required by the indy-sdk even though not present in AnonCreds v1. // For this reason we return it in the metadata. indyLedgerSeqNo: response.result.txnMetadata.seqNo, - didIndyNamespace: pool.indyNamespace, }, } } catch (error) { @@ -203,53 +195,58 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { try { const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const did = didFromCredentialDefinitionId(credentialDefinitionId) - - const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + // we support did:indy and legacy identifiers + const { did, namespaceIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId) + const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Getting credential definition '${credentialDefinitionId}' from ledger '${pool.indyNamespace}'` ) + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) const request = new GetCredentialDefinitionRequest({ - submitterDid: did, - credentialDefinitionId, + credentialDefinitionId: legacyCredentialDefinitionId, }) agentContext.config.logger.trace( `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.indyNamespace}'` ) - const response = await pool.submitReadRequest(request) - const schema = await this.fetchIndySchemaWithSeqNo(agentContext, response.result.ref, did) + // We need to fetch the schema to determine the schemaId (we only have the seqNo) + const schema = await this.fetchIndySchemaWithSeqNo(agentContext, response.result.ref, namespaceIdentifier) + + if (!schema || !response.result.data) { + agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`) - if (response.result.data && schema) { return { - credentialDefinitionId: credentialDefinitionId, - credentialDefinition: { - issuerId: didFromCredentialDefinitionId(credentialDefinitionId), - schemaId: schema.schema.schemaId, - tag: response.result.tag, - type: 'CL', - value: response.result.data, - }, - credentialDefinitionMetadata: { - didIndyNamespace: pool.indyNamespace, + credentialDefinitionId, + credentialDefinitionMetadata: {}, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition with id ${credentialDefinitionId}`, }, - resolutionMetadata: {}, } } - agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`) + // Format the schema id based on the type of the credential definition id + const schemaId = credentialDefinitionId.startsWith('did:indy') + ? getDidIndySchemaId(pool.indyNamespace, namespaceIdentifier, schema.schema.name, schema.schema.version) + : schema.schema.schemaId return { - credentialDefinitionId, - credentialDefinitionMetadata: {}, - resolutionMetadata: { - error: 'notFound', - message: `unable to resolve credential definition with id ${credentialDefinitionId}`, + credentialDefinitionId: credentialDefinitionId, + credentialDefinition: { + issuerId: did, + schemaId, + tag: response.result.tag, + type: 'CL', + value: response.result.data, + }, + credentialDefinitionMetadata: { + didIndyNamespace: pool.indyNamespace, }, + resolutionMetadata: {}, } } catch (error) { agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { @@ -270,26 +267,22 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { public async registerCredentialDefinition( agentContext: AgentContext, - options: IndyVdrRegisterCredentialDefinitionOptions + options: RegisterCredentialDefinitionOptions ): Promise { - // Make sure didIndyNamespace is passed - if (!options.options.didIndyNamespace) { - return { - credentialDefinitionMetadata: {}, - registrationMetadata: {}, - credentialDefinitionState: { - reason: 'no didIndyNamespace defined in the options. didIndyNamespace is required when using the Indy SDK', - credentialDefinition: options.credentialDefinition, - state: 'failed', - }, - } - } - try { + // This will throw an error if trying to register a credential defintion with a legacy indy identifier. We only support did:indy + // identifiers for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers. + const { namespaceIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const pool = indyVdrPoolService.getPoolForNamespace(options.options.didIndyNamespace) + const pool = indyVdrPoolService.getPoolForNamespace(namespace) + agentContext.config.logger.debug( + `Registering credential definition on ledger '${pool.indyNamespace}' with did '${options.credentialDefinition.issuerId}'`, + options.credentialDefinition + ) + // TODO: this will bypass caching if done on a higher level. const { schema, schemaMetadata, resolutionMetadata } = await this.getSchema( agentContext, options.credentialDefinition.schemaId @@ -309,17 +302,23 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } } - const credentialDefinitionId = getLegacyCredentialDefinitionId( + const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( options.credentialDefinition.issuerId, schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag ) + const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( + namespace, + namespaceIdentifier, + schemaMetadata.indyLedgerSeqNo, + options.credentialDefinition.tag + ) const credentialDefinitionRequest = new CredentialDefinitionRequest({ - submitterDid: options.credentialDefinition.issuerId, + submitterDid: namespaceIdentifier, credentialDefinition: { ver: '1.0', - id: credentialDefinitionId, + id: legacyCredentialDefinitionId, schemaId: `${schemaMetadata.indyLedgerSeqNo}`, type: 'CL', tag: options.credentialDefinition.tag, @@ -327,32 +326,10 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { }, }) - // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did - // from the ledger to know which key is associated with the did - const didsApi = agentContext.dependencyManager.resolve(DidsApi) - const didResult = await didsApi.resolve(`did:sov:${options.credentialDefinition.issuerId}`) - - if (!didResult.didDocument) { - return { - credentialDefinitionMetadata: {}, - registrationMetadata: {}, - credentialDefinitionState: { - credentialDefinition: options.credentialDefinition, - state: 'failed', - reason: `didNotFound: unable to resolve did did:sov:${options.credentialDefinition.issuerId}: ${didResult.didResolutionMetadata.message}`, - }, - } - } - - const verificationMethod = didResult.didDocument.dereferenceKey( - `did:sov:${options.credentialDefinition.issuerId}#key-1` - ) - const key = getKeyFromVerificationMethod(verificationMethod) - - const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, key) - + const submitterKey = await verificationKeyForIndyDid(agentContext, options.credentialDefinition.issuerId) + const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, submitterKey) agentContext.config.logger.debug( - `Registered credential definition '${credentialDefinitionId}' on ledger '${pool.indyNamespace}'`, + `Registered credential definition '${didIndyCredentialDefinitionId}' on ledger '${pool.indyNamespace}'`, { response, credentialDefinition: options.credentialDefinition, @@ -360,12 +337,10 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { ) return { - credentialDefinitionMetadata: { - didIndyNamespace: pool.indyNamespace, - }, + credentialDefinitionMetadata: {}, credentialDefinitionState: { credentialDefinition: options.credentialDefinition, - credentialDefinitionId, + credentialDefinitionId: didIndyCredentialDefinitionId, state: 'finished', }, registrationMetadata: {}, @@ -398,23 +373,28 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { ): Promise { try { const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const did = didFromRevocationRegistryDefinitionId(revocationRegistryDefinitionId) - const pool = await indySdkPoolService.getPoolForDid(agentContext, did) + const { did, namespaceIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = + parseRevocationRegistryId(revocationRegistryDefinitionId) + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.indyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` ) + const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ) const request = new GetRevocationRegistryDefinitionRequest({ - submitterDid: did, - revocationRegistryId: revocationRegistryDefinitionId, + revocationRegistryId: legacyRevocationRegistryId, }) agentContext.config.logger.trace( `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` ) - const response = await pool.submitReadRequest(request) if (!response.result.data) { @@ -435,28 +415,44 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } } + agentContext.config.logger.trace( + `Got revocation registry definition '${revocationRegistryDefinitionId}' from ledger '${pool.indyNamespace}'`, + { + response, + } + ) + + const credentialDefinitionId = revocationRegistryDefinitionId.startsWith('did:indy:') + ? getDidIndyCredentialDefinitionId( + pool.indyNamespace, + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag + ) + : getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag) + const revocationRegistryDefinition = { issuerId: did, - revocDefType: response.result.data?.revocDefType, + revocDefType: response.result.data.revocDefType, value: { - maxCredNum: response.result.data?.value.maxCredNum, - tailsHash: response.result.data?.value.tailsHash, - tailsLocation: response.result.data?.value.tailsLocation, + maxCredNum: response.result.data.value.maxCredNum, + tailsHash: response.result.data.value.tailsHash, + tailsLocation: response.result.data.value.tailsLocation, publicKeys: { accumKey: { - z: response.result.data?.value.publicKeys.accumKey.z, + z: response.result.data.value.publicKeys.accumKey.z, }, }, }, - tag: response.result.data?.tag, - credDefId: response.result.data?.credDefId, - } + tag: response.result.data.tag, + credDefId: credentialDefinitionId, + } satisfies AnonCredsRevocationRegistryDefinition return { revocationRegistryDefinitionId, revocationRegistryDefinition, revocationRegistryDefinitionMetadata: { - issuanceType: response.result.data?.value.issuanceType, + issuanceType: response.result.data.value.issuanceType, didIndyNamespace: pool.indyNamespace, }, resolutionMetadata: {}, @@ -488,24 +484,29 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { ): Promise { try { const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const did = didFromRevocationRegistryDefinitionId(revocationRegistryId) - const pool = await indySdkPoolService.getPoolForDid(agentContext, did) + const { did, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = + parseRevocationRegistryId(revocationRegistryId) + const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.indyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` ) + const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag + ) const request = new GetRevocationRegistryDeltaRequest({ - submitterDid: did, - revocationRegistryId, + revocationRegistryId: legacyRevocationRegistryId, toTs: timestamp, }) agentContext.config.logger.trace( `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` ) - const response = await pool.submitReadRequest(request) agentContext.config.logger.debug( @@ -543,9 +544,9 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } const revocationRegistryDelta = { - accum: response.result.data?.value.accum_to.value.accum, - issued: response.result.data?.value.issued, - revoked: response.result.data?.value.revoked, + accum: response.result.data.value.accum_to.value.accum, + issued: response.result.data.value.issued, + revoked: response.result.data.value.revoked, } return { @@ -583,7 +584,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { private async fetchIndySchemaWithSeqNo(agentContext: AgentContext, seqNo: number, did: string) { const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) + const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting transaction with seqNo '${seqNo}' from ledger '${pool.indyNamespace}'`) // ledgerType 1 is domain ledger @@ -622,15 +623,3 @@ interface SchemaType { name: string } } - -export interface IndyVdrRegisterSchemaOptions extends RegisterSchemaOptions { - options: { - didIndyNamespace: string - } -} - -export interface IndyVdrRegisterCredentialDefinitionOptions extends RegisterCredentialDefinitionOptions { - options: { - didIndyNamespace: string - } -} diff --git a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts deleted file mode 100644 index 62528a0075..0000000000 --- a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifier.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { - getLegacySchemaId, - getLegacyCredentialDefinitionId, - didFromSchemaId, - didFromCredentialDefinitionId, - indyVdrAnonCredsRegistryIdentifierRegex, -} from '../identifiers' - -describe('identifiers', () => { - it('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { - const did = '7Tqg6BwSSWapxgUDm9KKgg' - const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' - const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' - const revocationRegistryId = - 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' - - const anotherId = 'some:id' - - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) - }) - - it('getLegacySchemaId should return a valid schema Id', () => { - const did = '29347' - const name = 'starlinks' - const version = '321' - - expect(getLegacySchemaId(did, name, version)).toEqual(`29347:2:starlinks:321`) - }) - - it('getLegacyCredentialDefinition should return a valid Credential Id', () => { - const did = '15565' - const seqNo = 323 - const tag = 'indyTag' - expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('15565:3:CL:323:indyTag') - }) - - it('didFromSchemaId should return the valid did from the schema', () => { - const schemaId = '29347:2:starlinks:321' - - expect(didFromSchemaId(schemaId)).toEqual('29347') - }) - - it('didFromCredentialId should return the valid did from the schema', () => { - const credentialDefinitionId = '15565:3:CL:323:indyTag' - - expect(didFromCredentialDefinitionId(credentialDefinitionId)).toEqual('15565') - }) -}) diff --git a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts new file mode 100644 index 0000000000..555605a1d9 --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts @@ -0,0 +1,185 @@ +import { + getDidIndyCredentialDefinitionId, + getDidIndyRevocationRegistryId, + getDidIndySchemaId, + getLegacyCredentialDefinitionId, + getLegacyRevocationRegistryId, + getLegacySchemaId, + indyVdrAnonCredsRegistryIdentifierRegex, + parseCredentialDefinitionId, + parseRevocationRegistryId, + parseSchemaId, +} from '../identifiers' + +describe('identifiers', () => { + describe('indyVdrAnonCredsRegistryIdentifierRegex', () => { + test('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { + const did = '7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' + const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' + const revocationRegistryId = + 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + + const anotherId = 'some:id' + + // unqualified issuerId not in regex on purpose. See note in implementation. + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(false) + + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + + test('matches against a did indy did, schema id, credential definition id and revocation registry id', () => { + const did = 'did:indy:local:7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0' + const credentialDefinitionId = + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID' + const revocationRegistryId = + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/REV_REG_DEF/100669/SCH Employee ID/1-1024' + + const anotherId = 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/SOME_DEF' + + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + }) + + test('getLegacySchemaId returns a valid schema id given a did, name, and version', () => { + const did = '12345' + const name = 'backbench' + const version = '420' + + expect(getLegacySchemaId(did, name, version)).toEqual('12345:2:backbench:420') + }) + + test('getLegacyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { + const did = '12345' + const seqNo = 420 + const tag = 'someTag' + + expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') + }) + + test('getLegacyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { + const did = '12345' + const seqNo = 420 + const credentialDefinitionTag = 'someTag' + const tag = 'anotherTag' + + expect(getLegacyRevocationRegistryId(did, seqNo, credentialDefinitionTag, tag)).toEqual( + '12345:4:12345:3:CL:420:someTag:CL_ACCUM:anotherTag' + ) + }) + + test('getDidIndySchemaId returns a valid schema id given a did, name, and version', () => { + const namespace = 'sovrin:test' + const did = '12345' + const name = 'backbench' + const version = '420' + + expect(getDidIndySchemaId(namespace, did, name, version)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/SCHEMA/backbench/420' + ) + }) + + test('getDidIndyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { + const namespace = 'sovrin:test' + const did = '12345' + const seqNo = 420 + const tag = 'someTag' + + expect(getDidIndyCredentialDefinitionId(namespace, did, seqNo, tag)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/CLAIM_DEF/420/someTag' + ) + }) + + test('getDidIndyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { + const namespace = 'sovrin:test' + const did = '12345' + const seqNo = 420 + const credentialDefinitionTag = 'someTag' + const tag = 'anotherTag' + + expect(getDidIndyRevocationRegistryId(namespace, did, seqNo, credentialDefinitionTag, tag)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag' + ) + }) + + describe('parseSchemaId', () => { + test('parses legacy schema id', () => { + expect(parseSchemaId('SDqTzbVuCowusqGBNbNDjH:2:schema-name:1.0')).toEqual({ + did: 'SDqTzbVuCowusqGBNbNDjH', + namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + schemaName: 'schema-name', + schemaVersion: '1.0', + }) + }) + + test('parses did:indy schema id', () => { + expect(parseSchemaId('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0')).toEqual( + { + namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + did: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + schemaName: 'schema-name', + schemaVersion: '1.0', + namespace: 'bcovrin:test', + } + ) + }) + }) + + describe('parseCredentialDefinitionId', () => { + test('parses legacy credential definition id', () => { + expect(parseCredentialDefinitionId('TL1EaPFCZ8Si5aUrqScBDt:3:CL:10:TAG')).toEqual({ + did: 'TL1EaPFCZ8Si5aUrqScBDt', + namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + schemaSeqNo: '10', + tag: 'TAG', + }) + }) + + test('parses did:indy credential definition id', () => { + expect( + parseCredentialDefinitionId('did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/10/TAG') + ).toEqual({ + namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + did: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', + namespace: 'pool:localtest', + schemaSeqNo: '10', + tag: 'TAG', + }) + }) + }) + + describe('parseRevocationRegistryId', () => { + test('parses legacy revocation registry id', () => { + expect( + parseRevocationRegistryId('5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1') + ).toEqual({ + did: '5nDyJVP1NrcPAttP3xwMB9', + namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + schemaSeqNo: '56495', + credentialDefinitionTag: 'npdb', + revocationRegistryTag: 'TAG1', + }) + }) + + test('parses did:indy revocation registry id', () => { + expect( + parseRevocationRegistryId('did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_DEF/56495/npdb/TAG1') + ).toEqual({ + namespace: 'sovrin', + namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + did: 'did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9', + schemaSeqNo: '56495', + credentialDefinitionTag: 'npdb', + revocationRegistryTag: 'TAG1', + }) + }) + }) +}) diff --git a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts index d242ca3461..e7e1a2bd49 100644 --- a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts @@ -1,42 +1,156 @@ -export const legacyIndyVdrIssuerIdRegex = /^[a-zA-Z0-9]{21,22}$/ -export const legacyIndyVdrSchemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ -export const legacyIndyVdrCredentialDefinitionIdRegex = - /^[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ -export const legacyIndyVdrRevocationRegistryIdRegex = - /^[a-zA-Z0-9]{21,22}:4:[a-zA-Z0-9]{21,22}:3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)/ +/** + * NOTE: this file is availalbe in both the indy-sdk and indy-vdr packages. If making changes to + * this file, make sure to update both files if applicable. + */ + +import { DID_INDY_REGEX } from '../../utils/did' + +const didIndyAnonCredsBase = + /(?did:indy:(?((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?)):(?([1-9A-HJ-NP-Za-km-z]{21,22})))\/anoncreds\/v0/ + +// did:indy::/anoncreds/v0/SCHEMA// +const didIndySchemaIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/SCHEMA/(?.+)/(?[0-9.]+)$` +) + +// :2:: +const legacyIndySchemaIdRegex = + /^(?(?[a-zA-Z0-9]{21,22})):2:(?.+):(?[0-9.]+)$/ + +// did:indy::/anoncreds/v0/CLAIM_DEF// +const didIndyCredentialDefinitionIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/CLAIM_DEF/(?[1-9][0-9]*)/(?.+)$` +) + +// :3:CL:: +const legacyIndyCredentialDefinitionIdRegex = + /^(?(?[a-zA-Z0-9]{21,22})):3:CL:(?[1-9][0-9]*):(?.+)$/ + +// did:indy::/anoncreds/v0/REV_REG_DEF/// +const didIndyRevocationRegistryIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/REV_REG_DEF/(?[1-9][0-9]*)/(?.+)/(?.+)$` +) + +// :4::3:CL::CL_ACCUM: +const legacyIndyRevocationRegistryIdRegex = + /^(?(?[a-zA-Z0-9]{21,22})):4:[a-zA-Z0-9]{21,22}:3:CL:(?[1-9][0-9]*):(?.+):CL_ACCUM:(?.+)$/ + +// combines both legacy and did:indy anoncreds identifiers and also the issuer id +const indyVdrAnonCredsRegexes = [ + // NOTE: we only include the qualified issuer id here, as we don't support registering objects based on legacy issuer ids. + // you can still resolve using legacy issuer ids, but you need to use the full did:indy identifier when registering. + // As we find a matching anoncreds registry based on the issuerId only when creating an object, this will make sure + // it will throw an no registry found for identifier error. + // issuer id + DID_INDY_REGEX, + + // schema + didIndySchemaIdRegex, + legacyIndySchemaIdRegex, + + // credential definition + didIndyCredentialDefinitionIdRegex, + legacyIndyCredentialDefinitionIdRegex, + + // revocation registry + legacyIndyRevocationRegistryIdRegex, + didIndyRevocationRegistryIdRegex, +] export const indyVdrAnonCredsRegistryIdentifierRegex = new RegExp( - `${legacyIndyVdrIssuerIdRegex.source}|${legacyIndyVdrSchemaIdRegex.source}|${legacyIndyVdrCredentialDefinitionIdRegex.source}|${legacyIndyVdrRevocationRegistryIdRegex.source}` + indyVdrAnonCredsRegexes.map((r) => r.source.replace(/(\?<[a-zA-Z]+>)?/g, '')).join('|') ) +export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, name: string, version: string) { + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/SCHEMA/${name}/${version}` +} + export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { return `${unqualifiedDid}:2:${name}:${version}` } -export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: number, tag: string) { +export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: string | number, tag: string) { return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` } -/** - * Extract did from schema id - */ -export function didFromSchemaId(schemaId: string) { - const [did] = schemaId.split(':') +export function getDidIndyCredentialDefinitionId( + namespace: string, + unqualifiedDid: string, + seqNo: string | number, + tag: string +) { + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/CLAIM_DEF/${seqNo}/${tag}` +} - return did +// TZQuLp43UcYTdtc3HewcDz:4:TZQuLp43UcYTdtc3HewcDz:3:CL:98158:BaustellenzertifikateNU1:CL_ACCUM:1-100 +export function getLegacyRevocationRegistryId( + unqualifiedDid: string, + seqNo: string | number, + credentialDefinitionTag: string, + revocationRegistryTag: string +) { + return `${unqualifiedDid}:4:${unqualifiedDid}:3:CL:${seqNo}:${credentialDefinitionTag}:CL_ACCUM:${revocationRegistryTag}` } -/** - * Extract did from credential definition id - */ -export function didFromCredentialDefinitionId(credentialDefinitionId: string) { - const [did] = credentialDefinitionId.split(':') +export function getDidIndyRevocationRegistryId( + namespace: string, + unqualifiedDid: string, + seqNo: string | number, + credentialDefinitionTag: string, + revocationRegistryTag: string +) { + return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_DEF/${seqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` +} + +interface ParsedSchemaId { + did: string + namespaceIdentifier: string + schemaName: string + schemaVersion: string + namespace?: string +} + +export function parseSchemaId(schemaId: string) { + const match = schemaId.match(didIndySchemaIdRegex) ?? schemaId.match(legacyIndySchemaIdRegex) + + if (!match) throw new Error(`Invalid schema id: ${schemaId}`) - return did + return match.groups as unknown as ParsedSchemaId } -export function didFromRevocationRegistryDefinitionId(revocationRegistryId: string) { - const [did] = revocationRegistryId.split(':') +interface ParsedCredentialDefinitionId { + did: string + namespaceIdentifier: string + schemaSeqNo: string + tag: string + namespace?: string +} + +export function parseCredentialDefinitionId(credentialDefinitionId: string) { + const match = + credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) ?? + credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) + + if (!match) throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) + + return match.groups as unknown as ParsedCredentialDefinitionId +} + +interface ParsedRevocationRegistryId { + did: string + namespaceIdentifier: string + schemaSeqNo: string + credentialDefinitionTag: string + revocationRegistryTag: string + namespace?: string +} + +export function parseRevocationRegistryId(revocationRegistryId: string) { + const match = + revocationRegistryId.match(didIndyRevocationRegistryIdRegex) ?? + revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) + + if (!match) throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) - return did + return match.groups as unknown as ParsedRevocationRegistryId } diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index 50735a67eb..d32e8947ec 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -34,6 +34,7 @@ import { indyDidDocumentFromDid, parseIndyDid, isSelfCertifiedIndyDid, + verificationKeyForIndyDid, } from './didIndyUtil' import { endpointsAttribFromServices } from './didSovUtil' @@ -44,10 +45,10 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { const seed = options.secret?.seed const privateKey = options.secret?.privateKey - const { alias, role, submitterDid, submitterVerkey, services, useEndpointAttrib } = options.options - let verkey = options.options.verkey + const { alias, role, submitterDid, services, useEndpointAttrib } = options.options let did = options.did - let id + let namespaceIdentifier: string + let verificationKey: Key const allowOne = [privateKey, seed, did].filter((e) => e !== undefined) if (allowOne.length > 1) { @@ -62,11 +63,13 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { } try { - const { namespace, id: submitterId } = parseIndyDid(submitterDid) + // Parse submitterDid and extract namespace based on the submitter did + const { namespace: submitterNamespace, namespaceIdentifier: submitterNamespaceIdentifier } = + parseIndyDid(submitterDid) + const submitterSigningKey = await verificationKeyForIndyDid(agentContext, submitterDid) if (did) { - id = parseIndyDid(did).id - if (!verkey) { + if (!options.options.verkey) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -76,26 +79,62 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { }, } } - if (!isSelfCertifiedIndyDid(did, verkey)) { - throw new Error(`Initial verkey ${verkey} does not match did ˇ${did}`) + + const { namespace, namespaceIdentifier: _namespaceIdentifier } = parseIndyDid(did) + namespaceIdentifier = _namespaceIdentifier + verificationKey = Key.fromPublicKeyBase58(options.options.verkey, KeyType.Ed25519) + + if (!isSelfCertifiedIndyDid(did, options.options.verkey)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Initial verkey ${options.options.verkey} does not match did ${did}`, + }, + } + } + + if (submitterNamespace !== namespace) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `The submitter did uses namespace ${submitterNamespace} and the did to register uses namespace ${namespace}. Namespaces must match.`, + }, + } } } else { // Create a new key and calculate did according to the rules for indy did method - const key = await agentContext.wallet.createKey({ privateKey, seed, keyType: KeyType.Ed25519 }) - const buffer = Hasher.hash(key.publicKey, 'sha2-256') + verificationKey = await agentContext.wallet.createKey({ privateKey, seed, keyType: KeyType.Ed25519 }) + const buffer = Hasher.hash(verificationKey.publicKey, 'sha2-256') - id = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) - verkey = key.publicKeyBase58 - did = `did:indy:${namespace}:${id}` + namespaceIdentifier = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + did = `did:indy:${submitterNamespace}:${namespaceIdentifier}` } // Create base did document - const didDocumentBuilder = indyDidDocumentFromDid(did, verkey) + const didDocumentBuilder = indyDidDocumentFromDid(did, verificationKey.publicKeyBase58) let diddocContent // Add services if object was passed if (services) { - services.forEach((item) => didDocumentBuilder.addService(item)) + services.forEach((item) => { + const prependDidIfNotPresent = (id: string) => { + return id.startsWith('#') ? `${did}${id}` : id + } + + // Prepend the did to the service id if it is not already there + item.id = prependDidIfNotPresent(item.id) + + // TODO: should we also prepend the did to routingKeys? + if (item instanceof DidCommV1Service) { + item.recipientKeys = item.recipientKeys.map(prependDidIfNotPresent) + } + + didDocumentBuilder.addService(item) + }) const commTypes = [IndyAgentService.type, DidCommV1Service.type, DidCommV2Service.type] const serviceTypes = new Set(services.map((item) => item.type)) @@ -105,10 +144,11 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { // If there is at least a communication service, add the key agreement key if (commTypes.some((type) => serviceTypes.has(type))) { didDocumentBuilder + .addContext('https://w3id.org/security/suites/x25519-2019/v1') .addVerificationMethod({ controller: did, id: keyAgreementId, - publicKeyBase58: createKeyAgreementKey(verkey), + publicKeyBase58: createKeyAgreementKey(verificationKey.publicKeyBase58), type: 'X25519KeyAgreementKey2019', }) .addKeyAgreement(keyAgreementId) @@ -123,29 +163,36 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { // create diddocContent parameter based on the diff between the base and the resulting DID Document diddocContent = didDocDiff( didDocumentBuilder.build().toJSON(), - indyDidDocumentFromDid(did, verkey).build().toJSON() + indyDidDocumentFromDid(did, verificationKey.publicKeyBase58).build().toJSON() ) } } // Build did document const didDocument = didDocumentBuilder.build() - - const pool = agentContext.dependencyManager.resolve(IndyVdrPoolService).getPoolForNamespace(namespace) - + const pool = agentContext.dependencyManager.resolve(IndyVdrPoolService).getPoolForNamespace(submitterNamespace) // If there are services and we are using legacy indy endpoint attrib, make sure they are suitable before registering the DID if (services && useEndpointAttrib) { const endpoints = endpointsAttribFromServices(services) - await this.registerPublicDid(agentContext, pool, submitterId, submitterVerkey, id, verkey, alias, role) - await this.setEndpointsForDid(agentContext, pool, verkey, id, endpoints) + await this.registerPublicDid( + agentContext, + pool, + submitterNamespaceIdentifier, + submitterSigningKey, + namespaceIdentifier, + verificationKey, + alias, + role + ) + await this.setEndpointsForDid(agentContext, pool, namespaceIdentifier, verificationKey, endpoints) } else { await this.registerPublicDid( agentContext, pool, - submitterId, - submitterVerkey, - id, - verkey, + submitterNamespaceIdentifier, + submitterSigningKey, + namespaceIdentifier, + verificationKey, alias, role, diddocContent @@ -168,7 +215,7 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { return { didDocumentMetadata: {}, didRegistrationMetadata: { - didIndyNamespace: namespace, + didIndyNamespace: submitterNamespace, }, didState: { state: 'finished', @@ -222,41 +269,44 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { private async registerPublicDid( agentContext: AgentContext, pool: IndyVdrPool, - submitterDid: string, - submitterVerkey: string, - targetDid: string, - verkey: string, + unqualifiedSubmitterDid: string, + submitterSigningKey: Key, + unqualifiedDid: string, + signingKey: Key, alias?: string, role?: string, diddocContent?: Record ) { try { - agentContext.config.logger.debug(`Register public did '${targetDid}' on ledger '${pool}'`) + agentContext.config.logger.debug(`Register public did '${unqualifiedDid}' on ledger '${pool}'`) // FIXME: Add diddocContent when supported by indy-vdr if (diddocContent) { throw new IndyVdrError('diddocContent is not yet supported') } - const request = new NymRequest({ submitterDid, dest: targetDid, verkey, alias }) - - const signingKey = Key.fromPublicKeyBase58(submitterVerkey, KeyType.Ed25519) + const request = new NymRequest({ + submitterDid: unqualifiedSubmitterDid, + dest: unqualifiedDid, + verkey: signingKey.publicKeyBase58, + alias, + }) - const response = await pool.submitWriteRequest(agentContext, request, signingKey) + const response = await pool.submitWriteRequest(agentContext, request, submitterSigningKey) - agentContext.config.logger.debug(`Registered public did '${targetDid}' on ledger '${pool.indyNamespace}'`, { + agentContext.config.logger.debug(`Registered public did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, { response, }) - return targetDid + return } catch (error) { agentContext.config.logger.error( - `Error registering public did '${targetDid}' on ledger '${pool.indyNamespace}'`, + `Error registering public did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, { error, - submitterDid, - targetDid, - verkey, + unqualifiedSubmitterDid, + unqualifiedDid, + signingKey, alias, role, pool: pool.indyNamespace, @@ -270,53 +320,55 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { private async setEndpointsForDid( agentContext: AgentContext, pool: IndyVdrPool, - submitterVerkey: string, - did: string, + unqualifiedDid: string, + signingKey: Key, endpoints: IndyEndpointAttrib ): Promise { try { - agentContext.config.logger.debug(`Set endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, endpoints) + agentContext.config.logger.debug( + `Set endpoints for did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, + endpoints + ) const request = new AttribRequest({ - submitterDid: did, - targetDid: did, + submitterDid: unqualifiedDid, + targetDid: unqualifiedDid, raw: JSON.stringify({ endpoint: endpoints }), }) - const signingKey = Key.fromPublicKeyBase58(submitterVerkey, KeyType.Ed25519) - const response = await pool.submitWriteRequest(agentContext, request, signingKey) agentContext.config.logger.debug( - `Successfully set endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, + `Successfully set endpoints for did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, { response, endpoints, } ) } catch (error) { - agentContext.config.logger.error(`Error setting endpoints for did '${did}' on ledger '${pool.indyNamespace}'`, { - error, - did, - endpoints, - }) + agentContext.config.logger.error( + `Error setting endpoints for did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, + { + error, + unqualifiedDid, + endpoints, + } + ) throw new IndyVdrError(error) } } } -export interface IndyVdrDidCreateOptions extends DidCreateOptions { - method: 'indy' - did?: string +interface IndyVdrDidCreateOptionsBase extends DidCreateOptions { didDocument?: never // Not yet supported options: { alias?: string role?: string services?: DidDocumentService[] useEndpointAttrib?: boolean - submitterDid: string - submitterVerkey: string verkey?: string + + submitterDid: string } secret?: { seed?: Buffer @@ -324,6 +376,14 @@ export interface IndyVdrDidCreateOptions extends DidCreateOptions { } } -// TODO: Add Update and Deactivate -export type IndyVdrIndyDidUpdateOptions = never -export type IndyVdrIndyDidDeactivateOptions = never +interface IndyVdrDidCreateOptionsWithDid extends IndyVdrDidCreateOptionsBase { + method?: never + did: string +} + +interface IndyVdrDidCreateOptionsWithoutDid extends IndyVdrDidCreateOptionsBase { + method: 'indy' + did?: never +} + +export type IndyVdrDidCreateOptions = IndyVdrDidCreateOptionsWithDid | IndyVdrDidCreateOptionsWithoutDid diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts index 6f6d40cbcf..124e5da88e 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts @@ -1,4 +1,5 @@ -import type { CommEndpointType, GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' +import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' +import type { IndyVdrPool } from '../pool' import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' @@ -15,10 +16,8 @@ export class IndyVdrIndyDidResolver implements DidResolver { public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} try { - const nym = await this.getPublicDid(agentContext, did) - // Get DID Document from Get NYM response - const didDocument = await this.buildDidDocument(agentContext, nym, did) + const didDocument = await this.buildDidDocument(agentContext, did) return { didDocument, @@ -37,34 +36,37 @@ export class IndyVdrIndyDidResolver implements DidResolver { } } - private async buildDidDocument(agentContext: AgentContext, getNymResponseData: GetNymResponseData, did: string) { + private async buildDidDocument(agentContext: AgentContext, did: string) { + const { namespaceIdentifier, namespace } = parseIndyDid(did) + + const poolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + const pool = poolService.getPoolForNamespace(namespace) + + const nym = await this.getPublicDid(pool, namespaceIdentifier) + // Create base Did Document // For modern did:indy DIDs, we assume that GET_NYM is always a full verkey in base58. // For backwards compatibility, we accept a shortened verkey and convert it using previous convention - const verkey = getFullVerkey(did, getNymResponseData.verkey) + const verkey = getFullVerkey(namespaceIdentifier, nym.verkey) const builder = indyDidDocumentFromDid(did, verkey) // If GET_NYM does not return any diddocContent, fallback to legacy GET_ATTRIB endpoint - if (!getNymResponseData.diddocContent) { + if (!nym.diddocContent) { const keyAgreementId = `${did}#key-agreement-1` - - const endpoints = await this.getEndpointsForDid(agentContext, did) + const endpoints = await this.getEndpointsForDid(agentContext, pool, namespaceIdentifier) if (endpoints) { - // If there is at least a didcomm endpoint, generate and a key agreement key - const commTypes: CommEndpointType[] = ['endpoint', 'did-communication', 'DIDComm'] - if (commTypes.some((type) => endpoints.types?.includes(type))) { - builder - .addVerificationMethod({ - controller: did, - id: keyAgreementId, - publicKeyBase58: createKeyAgreementKey(getNymResponseData.verkey), - type: 'X25519KeyAgreementKey2019', - }) - .addKeyAgreement(keyAgreementId) - } + builder + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(verkey), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) // Process endpoint attrib following the same rules as for did:sov addServicesFromEndpointsAttrib(builder, did, endpoints, keyAgreementId) @@ -72,41 +74,29 @@ export class IndyVdrIndyDidResolver implements DidResolver { return builder.build() } else { // Combine it with didDoc (TODO: Check if diddocContent is returned as a JSON object or a string) - return combineDidDocumentWithJson(builder.build(), getNymResponseData.diddocContent) + return combineDidDocumentWithJson(builder.build(), nym.diddocContent) } } - private async getPublicDid(agentContext: AgentContext, did: string) { - const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - - const { namespace, id } = parseIndyDid(did) - - const pool = indyVdrPoolService.getPoolForNamespace(namespace) - - const request = new GetNymRequest({ dest: id }) + private async getPublicDid(pool: IndyVdrPool, unqualifiedDid: string) { + const request = new GetNymRequest({ dest: unqualifiedDid }) const didResponse = await pool.submitReadRequest(request) if (!didResponse.result.data) { - throw new IndyVdrNotFoundError(`DID ${id} not found in indy namespace ${namespace}`) + throw new IndyVdrNotFoundError(`DID ${unqualifiedDid} not found in indy namespace ${pool.indyNamespace}`) } return JSON.parse(didResponse.result.data) as GetNymResponseData } - private async getEndpointsForDid(agentContext: AgentContext, did: string) { - const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - - const { namespace, id } = parseIndyDid(did) - - const pool = indyVdrPoolService.getPoolForNamespace(namespace) - + private async getEndpointsForDid(agentContext: AgentContext, pool: IndyVdrPool, unqualifiedDid: string) { try { - agentContext.config.logger.debug(`Get endpoints for did '${id}' from ledger '${pool.indyNamespace}'`) + agentContext.config.logger.debug(`Get endpoints for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`) - const request = new GetAttribRequest({ targetDid: id, raw: 'endpoint' }) + const request = new GetAttribRequest({ targetDid: unqualifiedDid, raw: 'endpoint' }) agentContext.config.logger.debug( - `Submitting get endpoint ATTRIB request for did '${id}' to ledger '${pool.indyNamespace}'` + `Submitting get endpoint ATTRIB request for did '${unqualifiedDid}' to ledger '${pool.indyNamespace}'` ) const response = await pool.submitReadRequest(request) @@ -116,7 +106,7 @@ export class IndyVdrIndyDidResolver implements DidResolver { const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib agentContext.config.logger.debug( - `Got endpoints '${JSON.stringify(endpoints)}' for did '${did}' from ledger '${pool.indyNamespace}'`, + `Got endpoints '${JSON.stringify(endpoints)}' for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`, { response, endpoints, @@ -126,7 +116,7 @@ export class IndyVdrIndyDidResolver implements DidResolver { return endpoints } catch (error) { agentContext.config.logger.error( - `Error retrieving endpoints for did '${did}' from ledger '${pool.indyNamespace}'`, + `Error retrieving endpoints for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`, { error, } diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts index 842707aaa1..dd4ecab222 100644 --- a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -1,4 +1,5 @@ import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' +import type { IndyVdrPool } from '../pool' import type { DidResolutionResult, ParsedDid, DidResolver, AgentContext } from '@aries-framework/core' import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' @@ -15,14 +16,19 @@ export class IndyVdrSovDidResolver implements DidResolver { const didDocumentMetadata = {} try { - const nym = await this.getPublicDid(agentContext, parsed.id) - const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) + const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const endpoints = await this.getEndpointsForDid(agentContext, parsed.id) + // FIXME: this actually fetches the did twice (if not cached), once for the pool and once for the nym + // we do not store the diddocContent in the pool cache currently so we need to fetch it again + // The logic is mostly to determine which pool to use for a did + const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, parsed.id) + const nym = await this.getPublicDid(pool, parsed.id) + const endpoints = await this.getEndpointsForDid(agentContext, pool, parsed.id) - if (endpoints) { - const keyAgreementId = `${parsed.did}#key-agreement-1` + const keyAgreementId = `${parsed.did}#key-agreement-1` + const builder = sovDidDocumentFromDid(parsed.did, nym.verkey) + if (endpoints) { addServicesFromEndpointsAttrib(builder, parsed.did, endpoints, keyAgreementId) } @@ -43,26 +49,17 @@ export class IndyVdrSovDidResolver implements DidResolver { } } - private async getPublicDid(agentContext: AgentContext, did: string) { - const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - - const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) - - const request = new GetNymRequest({ dest: did }) - + private async getPublicDid(pool: IndyVdrPool, unqualifiedDid: string) { + const request = new GetNymRequest({ dest: unqualifiedDid }) const didResponse = await pool.submitReadRequest(request) if (!didResponse.result.data) { - throw new IndyVdrNotFoundError(`DID ${did} not found`) + throw new IndyVdrNotFoundError(`DID ${unqualifiedDid} not found`) } return JSON.parse(didResponse.result.data) as GetNymResponseData } - private async getEndpointsForDid(agentContext: AgentContext, did: string) { - const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - - const pool = await indyVdrPoolService.getPoolForDid(agentContext, did) - + private async getEndpointsForDid(agentContext: AgentContext, pool: IndyVdrPool, did: string) { try { agentContext.config.logger.debug(`Get endpoints for did '${did}' from ledger '${pool.indyNamespace}'`) diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts new file mode 100644 index 0000000000..e75cc4d97e --- /dev/null +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts @@ -0,0 +1,706 @@ +import type { DidRecord, RecordSavedEvent } from '@aries-framework/core' + +import { + DidCommV1Service, + DidCommV2Service, + DidDocumentService, + DidDocument, + DidDocumentRole, + DidRepository, + DidsApi, + EventEmitter, + JsonTransformer, + Key, + KeyType, + RepositoryEventTypes, + SigningProviderRegistry, + TypedArrayEncoder, + VerificationMethod, +} from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { agentDependencies, getAgentConfig, getAgentContext, indySdk, mockProperty } from '../../../../core/tests' +import { IndySdkWallet } from '../../../../indy-sdk/src' +import { IndyVdrPool, IndyVdrPoolService } from '../../pool' +import { IndyVdrIndyDidRegistrar } from '../IndyVdrIndyDidRegistrar' + +jest.mock('../../pool/IndyVdrPool') +const IndyVdrPoolMock = IndyVdrPool as jest.Mock +const poolMock = new IndyVdrPoolMock() +mockProperty(poolMock, 'indyNamespace', 'ns1') + +const agentConfig = getAgentConfig('IndyVdrIndyDidRegistrar') + +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + +jest + .spyOn(wallet, 'createKey') + .mockResolvedValue(Key.fromPublicKeyBase58('E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', KeyType.Ed25519)) +const storageService = new InMemoryStorageService() +const eventEmitter = new EventEmitter(agentDependencies, new Subject()) +const didRepository = new DidRepository(storageService, eventEmitter) + +const agentContext = getAgentContext({ + wallet, + registerInstances: [ + [DidRepository, didRepository], + [IndyVdrPoolService, { getPoolForNamespace: jest.fn().mockReturnValue(poolMock) }], + [ + DidsApi, + { + resolve: jest.fn().mockResolvedValue({ + didDocument: new DidDocument({ + id: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + authentication: [ + new VerificationMethod({ + id: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }), + ], + }), + }), + }, + ], + ], + agentConfig, +}) + +const indyVdrIndyDidRegistrar = new IndyVdrIndyDidRegistrar() + +describe('IndyVdrIndyDidRegistrar', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + test('returns an error state if both did and privateKey are provided', async () => { + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + did: 'did:indy:pool1:did-value', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + secret: { + privateKey: TypedArrayEncoder.fromString('key'), + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Only one of 'seed', 'privateKey' and 'did' must be provided`, + }, + }) + }) + + test('returns an error state if the submitter did is not a valid did:indy did', async () => { + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'unknownError: BzCbsNYhMrjHiqZDTUASHg is not a valid did:indy did', + }, + }) + }) + + test('returns an error state if did is provided, but it is not a valid did:indy did', async () => { + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + did: 'BzCbsNYhMrjHiqZDTUASHg', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + verkey: 'verkey', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'unknownError: BzCbsNYhMrjHiqZDTUASHg is not a valid did:indy did', + }, + }) + }) + + test('returns an error state if did is provided, but no verkey', async () => { + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + did: 'BzCbsNYhMrjHiqZDTUASHg', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'If a did is defined, a matching verkey must be provided', + }, + }) + }) + + test('returns an error state if did and verkey are provided, but the did is not self certifying', async () => { + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + did: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + verkey: 'verkey', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Initial verkey verkey does not match did did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + }, + }) + }) + + test('returns an error state if did is provided, but does not match with the namespace from the submitterDid', async () => { + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + did: 'did:indy:pool2:B6xaJg1c2xU3D9ppCtt1CZ', + options: { + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + alias: 'Hello', + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: + 'The submitter did uses namespace pool1 and the did to register uses namespace pool2. Namespaces must match.', + }, + }) + }) + + test('creates a did:indy document without services', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + }, + secret: { + privateKey, + }, + }) + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + poolMock, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'B6xaJg1c2xU3D9ppCtt1CZ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD', + undefined + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + verificationMethod: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + ], + authentication: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey'], + assertionMethod: undefined, + keyAgreement: undefined, + }, + secret: { + privateKey, + }, + }, + }) + }) + + test('creates a did:indy document by passing did', async () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + options: { + verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + }, + secret: {}, + }) + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + poolMock, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'B6xaJg1c2xU3D9ppCtt1CZ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD', + undefined + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + verificationMethod: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + ], + authentication: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey'], + assertionMethod: undefined, + keyAgreement: undefined, + }, + secret: {}, + }, + }) + }) + + test('creates a did:indy document with services using diddocContent', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') + + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + services: [ + new DidDocumentService({ + id: `#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }), + new DidCommV1Service({ + id: `#did-communication`, + priority: 0, + recipientKeys: [`#key-agreement-1`], + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + accept: ['didcomm/aip2;env=rfc19'], + }), + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `#didcomm-1`, + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + }), + ], + }, + secret: { + privateKey, + }, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + poolMock, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'B6xaJg1c2xU3D9ppCtt1CZ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD', + { + '@context': [], + authentication: [], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + keyAgreement: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1'], + service: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#endpoint', + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + accept: ['didcomm/aip2;env=rfc19'], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#did-communication', + priority: 0, + recipientKeys: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1'], + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + }, + { + accept: ['didcomm/v2'], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-1', + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + }, + ], + verificationMethod: [ + { + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + type: 'X25519KeyAgreementKey2019', + }, + ], + } + ) + expect(setEndpointsForDidSpy).not.toHaveBeenCalled() + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + verificationMethod: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + }, + ], + service: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#endpoint', + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#did-communication', + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + priority: 0, + recipientKeys: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1'], + routingKeys: ['key-1'], + accept: ['didcomm/aip2;env=rfc19'], + }, + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-1', + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + routingKeys: ['key-1'], + accept: ['didcomm/v2'], + }, + ], + authentication: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey'], + assertionMethod: undefined, + keyAgreement: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1'], + }, + secret: { + privateKey, + }, + }, + }) + }) + + test('creates a did:indy document with services using attrib', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + const result = await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + useEndpointAttrib: true, + services: [ + new DidDocumentService({ + id: `#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }), + new DidCommV1Service({ + id: `#did-communication`, + priority: 0, + recipientKeys: [`#key-agreement-1`], + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + accept: ['didcomm/aip2;env=rfc19'], + }), + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `#didcomm-1`, + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + }), + ], + }, + secret: { + privateKey, + }, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + poolMock, + // Unqualified submitter did + 'BzCbsNYhMrjHiqZDTUASHg', + // submitter signing key, + expect.any(Key), + // Unqualified created indy did + 'B6xaJg1c2xU3D9ppCtt1CZ', + // Verkey + expect.any(Key), + // Alias + 'Hello', + // Role + 'STEWARD' + ) + expect(setEndpointsForDidSpy).toHaveBeenCalledWith( + agentContext, + poolMock, + 'B6xaJg1c2xU3D9ppCtt1CZ', + expect.any(Key), + { + endpoint: 'https://example.com/endpoint', + routingKeys: ['key-1'], + types: ['endpoint', 'did-communication', 'DIDComm'], + } + ) + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + verificationMethod: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + publicKeyBase58: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', + }, + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1', + type: 'X25519KeyAgreementKey2019', + controller: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + publicKeyBase58: 'Fbv17ZbnUSbafsiUBJbdGeC62M8v8GEscVMMcE59mRPt', + }, + ], + service: [ + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#endpoint', + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#did-communication', + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + priority: 0, + recipientKeys: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1'], + routingKeys: ['key-1'], + accept: ['didcomm/aip2;env=rfc19'], + }, + { + id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-1', + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + routingKeys: ['key-1'], + accept: ['didcomm/v2'], + }, + ], + authentication: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#verkey'], + assertionMethod: undefined, + keyAgreement: ['did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#key-agreement-1'], + }, + secret: { + privateKey, + }, + }, + }) + }) + + test('stores the did document', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - method is private + const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') + setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + + const saveCalled = jest.fn() + eventEmitter.on>(RepositoryEventTypes.RecordSaved, saveCalled) + + await indyVdrIndyDidRegistrar.create(agentContext, { + method: 'indy', + options: { + alias: 'Hello', + submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + role: 'STEWARD', + services: [ + new DidDocumentService({ + id: `#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }), + new DidCommV1Service({ + id: `#did-communication`, + priority: 0, + recipientKeys: [`#key-agreement-1`], + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + accept: ['didcomm/aip2;env=rfc19'], + }), + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `#didcomm-1`, + routingKeys: ['key-1'], + serviceEndpoint: 'https://example.com/endpoint', + }), + ], + }, + secret: { + privateKey, + }, + }) + + expect(saveCalled).toHaveBeenCalledTimes(1) + const [saveEvent] = saveCalled.mock.calls[0] + + expect(saveEvent.payload.record).toMatchObject({ + did: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', + role: DidDocumentRole.Created, + _tags: { + recipientKeyFingerprints: ['z6LSrH6AdsQeZuKKmG6Ehx7abEQZsVg2psR2VU536gigUoAe'], + }, + didDocument: undefined, + }) + }) + + test('returns an error state when calling update', async () => { + const result = await indyVdrIndyDidRegistrar.update() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: updating did:indy not implemented yet`, + }, + }) + }) + + test('returns an error state when calling deactivate', async () => { + const result = await indyVdrIndyDidRegistrar.deactivate() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notImplemented: deactivating did:indy not implemented yet`, + }, + }) + }) +}) diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts index c8001ccd19..0ed14f5856 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts @@ -18,7 +18,7 @@ const agentConfig = getAgentConfig('IndyVdrSovDidResolver') const agentContext = getAgentContext({ agentConfig, - registerInstances: [[IndyVdrPoolService, { getPoolForDid: jest.fn().mockReturnValue(poolMock) }]], + registerInstances: [[IndyVdrPoolService, { getPoolForDid: jest.fn().mockReturnValue({ pool: poolMock }) }]], }) const resolver = new IndyVdrSovDidResolver() diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json index 56014e70be..68874b6fc2 100644 --- a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyR1xKJw17sUoXhejEpugMYJ.json @@ -1,5 +1,5 @@ { - "@context": ["https://w3id.org/did/v1"], + "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/suites/ed25519-2018/v1"], "id": "did:indy:ns1:R1xKJw17sUoXhejEpugMYJ", "verificationMethod": [ { diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json index c131549e18..2a58c356ca 100644 --- a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json @@ -1,5 +1,10 @@ { - "@context": ["https://w3id.org/did/v1", "https://didcomm.org/messaging/contexts/v2"], + "@context": [ + "https://w3id.org/did/v1", + "https://w3id.org/security/suites/ed25519-2018/v1", + "https://w3id.org/security/suites/x25519-2019/v1", + "https://didcomm.org/messaging/contexts/v2" + ], "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo", "verificationMethod": [ { diff --git a/packages/indy-vdr/src/dids/didIndyUtil.ts b/packages/indy-vdr/src/dids/didIndyUtil.ts index 6644703169..e91f5ebd2b 100644 --- a/packages/indy-vdr/src/dids/didIndyUtil.ts +++ b/packages/indy-vdr/src/dids/didIndyUtil.ts @@ -1,8 +1,12 @@ +import type { AgentContext } from '@aries-framework/core' + import { + getKeyFromVerificationMethod, AriesFrameworkError, convertPublicKeyToX25519, DidDocument, DidDocumentBuilder, + DidsApi, Hasher, JsonTransformer, Key, @@ -19,6 +23,7 @@ export function indyDidDocumentFromDid(did: string, verKeyBase58: string) { const publicKeyBase58 = verKeyBase58 const builder = new DidDocumentBuilder(did) + .addContext('https://w3id.org/security/suites/ed25519-2018/v1') .addVerificationMethod({ controller: did, id: verificationMethodId, @@ -37,8 +42,8 @@ export function createKeyAgreementKey(verkey: string) { export function parseIndyDid(did: string) { const match = did.match(DID_INDY_REGEX) if (match) { - const [, namespace, id] = match - return { namespace, id } + const [, namespace, namespaceIdentifier] = match + return { namespace, namespaceIdentifier } } else { throw new AriesFrameworkError(`${did} is not a valid did:indy did`) } @@ -163,3 +168,27 @@ export function indyDidFromNamespaceAndInitialKey(namespace: string, initialKey: return { did, id, verkey } } + +/** + * Fetches the verification key for a given did:indy did and returns the key as a {@link Key} object. + * + * @throws {@link AriesFrameworkError} if the did could not be resolved or the key could not be extracted + */ +export async function verificationKeyForIndyDid(agentContext: AgentContext, did: string) { + // FIXME: we should store the didDocument in the DidRecord so we don't have to fetch our own did + // from the ledger to know which key is associated with the did + const didsApi = agentContext.dependencyManager.resolve(DidsApi) + const didResult = await didsApi.resolve(did) + + if (!didResult.didDocument) { + throw new AriesFrameworkError( + `Could not resolve did ${did}. ${didResult.didResolutionMetadata.error} ${didResult.didResolutionMetadata.message}` + ) + } + + // did:indy dids MUST have a verificationMethod with #verkey + const verificationMethod = didResult.didDocument.dereferenceKey(`${did}#verkey`) + const key = getKeyFromVerificationMethod(verificationMethod) + + return key +} diff --git a/packages/indy-vdr/src/dids/didSovUtil.ts b/packages/indy-vdr/src/dids/didSovUtil.ts index b836eb2eae..0517d00315 100644 --- a/packages/indy-vdr/src/dids/didSovUtil.ts +++ b/packages/indy-vdr/src/dids/didSovUtil.ts @@ -123,14 +123,15 @@ export function endpointsAttribFromServices(services: DidDocumentService[]): Ind const commServiceType = commService.type as CommEndpointType if (types.includes(commServiceType)) { throw new AriesFrameworkError('Only a single communication service per type is supported') - } else { - types.push(commServiceType) } - if (commService instanceof DidCommV1Service || commService instanceof DidCommV2Service) { - if (commService.routingKeys) { - commService.routingKeys.forEach((item) => routingKeys.add(item)) - } + types.push(commServiceType) + + if ( + (commService instanceof DidCommV1Service || commService instanceof DidCommV2Service) && + commService.routingKeys + ) { + commService.routingKeys.forEach((item) => routingKeys.add(item)) } } diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index 1ad1b0f80a..d8e31f72a7 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -36,8 +36,15 @@ export class IndyVdrPoolService { * If the did is a qualified indy did, the pool will be determined based on the namespace. * If it is a legacy unqualified indy did, the pool will be determined based on the algorithm as described in this document: * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit + * + * This method will optionally return a nym response when the did has been resolved to determine the ledger + * either now or in the past. The nymResponse can be used to prevent multiple ledger quries fetching the same + * did */ - public async getPoolForDid(agentContext: AgentContext, did: string): Promise { + public async getPoolForDid( + agentContext: AgentContext, + did: string + ): Promise<{ pool: IndyVdrPool; nymResponse?: CachedDidResponse['nymResponse'] }> { // Check if the did starts with did:indy const match = did.match(DID_INDY_REGEX) @@ -46,7 +53,7 @@ export class IndyVdrPoolService { const pool = this.getPoolForNamespace(namespace) - if (pool) return pool + if (pool) return { pool } throw new IndyVdrError(`Pool for indy namespace '${namespace}' not found`) } else { @@ -54,7 +61,10 @@ export class IndyVdrPoolService { } } - private async getPoolForLegacyDid(agentContext: AgentContext, did: string): Promise { + private async getPoolForLegacyDid( + agentContext: AgentContext, + did: string + ): Promise<{ pool: IndyVdrPool; nymResponse?: CachedDidResponse['nymResponse'] }> { const pools = this.pools if (pools.length === 0) { @@ -71,7 +81,7 @@ export class IndyVdrPoolService { // If we have the nym response with associated pool in the cache, we'll use that if (cachedNymResponse && pool) { this.logger.trace(`Found ledger id '${pool.indyNamespace}' for did '${did}' in cache`) - return pool + return { pool, nymResponse: cachedNymResponse.nymResponse } } const { successful, rejected } = await this.getSettledDidResponsesFromPools(did, pools) @@ -119,7 +129,7 @@ export class IndyVdrPoolService { }, indyNamespace: value.did.indyNamespace, }) - return value.pool + return { pool: value.pool, nymResponse: value.did.nymResponse } } private async getSettledDidResponsesFromPools(did: string, pools: IndyVdrPool[]) { @@ -159,7 +169,7 @@ export class IndyVdrPoolService { private async getDidFromPool(did: string, pool: IndyVdrPool): Promise { try { this.logger.trace(`Get public did '${did}' from ledger '${pool.indyNamespace}'`) - const request = await new GetNymRequest({ dest: did }) + const request = new GetNymRequest({ dest: did }) this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.indyNamespace}'`) const response = await pool.submitReadRequest(request) diff --git a/packages/indy-vdr/tests/__fixtures__/anoncreds.ts b/packages/indy-vdr/tests/__fixtures__/anoncreds.ts new file mode 100644 index 0000000000..fea36d5fcb --- /dev/null +++ b/packages/indy-vdr/tests/__fixtures__/anoncreds.ts @@ -0,0 +1,30 @@ +export const credentialDefinitionValue = { + primary: { + n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', + s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', + r: { + master_secret: + '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', + age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', + }, + rctxt: + '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', + z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', + }, + revocation: { + g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + g_dash: + '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + htilde: + '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h_cap: + '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + }, +} diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index ecaf154ee9..2ea2390329 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -1,12 +1,10 @@ -import type { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' -import type { AgentContext, Key } from '@aries-framework/core' +import type { IndyVdrDidCreateOptions } from '../src/dids/IndyVdrIndyDidRegistrar' +import type { Agent } from '@aries-framework/core' -import { KeyType } from '@aries-framework/core' -import { AttribRequest, NymRequest } from '@hyperledger/indy-vdr-shared' +import { DidCommV1Service, DidCommV2Service, DidDocumentService, KeyType } from '@aries-framework/core' import { genesisTransactions } from '../../core/tests/helpers' import { IndyVdrModuleConfig } from '../src/IndyVdrModuleConfig' -import { indyDidFromPublicKeyBase58 } from '../src/utils/did' export const indyVdrModuleConfig = new IndyVdrModuleConfig({ networks: [ @@ -19,38 +17,46 @@ export const indyVdrModuleConfig = new IndyVdrModuleConfig({ ], }) -export async function createDidOnLedger( - indyVdrPoolService: IndyVdrPoolService, - agentContext: AgentContext, - submitterDid: string, - signerKey: Key -) { - const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') - - const key = await agentContext.wallet.createKey({ keyType: KeyType.Ed25519 }) - const did = indyDidFromPublicKeyBase58(key.publicKeyBase58) - - const nymRequest = new NymRequest({ - dest: did, - submitterDid, - verkey: key.publicKeyBase58, - }) - - await pool.submitWriteRequest(agentContext, nymRequest, signerKey) - - const attribRequest = new AttribRequest({ - submitterDid: did, - targetDid: did, - raw: JSON.stringify({ - endpoint: { - endpoint: 'https://agent.com', - types: ['endpoint', 'did-communication', 'DIDComm'], - routingKeys: ['routingKey1', 'routingKey2'], - }, - }), +export async function createDidOnLedger(agent: Agent, submitterDid: string) { + const key = await agent.wallet.createKey({ keyType: KeyType.Ed25519 }) + + const createResult = await agent.dids.create({ + method: 'indy', + options: { + submitterDid, + alias: 'Alias', + role: 'TRUSTEE', + verkey: key.publicKeyBase58, + useEndpointAttrib: true, + services: [ + new DidDocumentService({ + id: `#endpoint`, + serviceEndpoint: 'http://localhost:3000', + type: 'endpoint', + }), + new DidCommV1Service({ + id: `#did-communication`, + priority: 0, + recipientKeys: [`#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'http://localhost:3000', + accept: ['didcomm/aip2;env=rfc19'], + }), + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `#didcomm-1`, + routingKeys: ['a-routing-key'], + serviceEndpoint: 'http://localhost:3000', + }), + ], + }, }) - await pool.submitWriteRequest(agentContext, attribRequest, key) + if (!createResult.didState.did) { + throw new Error( + `Did was not created. ${createResult.didState.state === 'failed' ? createResult.didState.reason : 'Not finished'}` + ) + } - return { did, key } + return { did: createResult.didState.did, key } } diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index 83f3323c54..f3448d169a 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -9,36 +9,35 @@ import { } from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' -import { IndyVdrSovDidResolver } from '../src' +import { IndyVdrIndyDidResolver, IndyVdrModule, IndyVdrSovDidResolver } from '../src' import { IndyVdrAnonCredsRegistry } from '../src/anoncreds/IndyVdrAnonCredsRegistry' import { IndyVdrPoolService } from '../src/pool' +import { credentialDefinitionValue } from './__fixtures__/anoncreds' import { indyVdrModuleConfig } from './helpers' const agentConfig = getAgentConfig('IndyVdrAnonCredsRegistry') -// TODO: update to module once available -const indyVdrPoolService = new IndyVdrPoolService(agentConfig.logger, indyVdrModuleConfig) -const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') - -// Verkey for the publicDidSeed -const signingKey = Key.fromPublicKeyBase58('FMGcFuU3QwAQLywxvmEnSorQT3NwU9wgDMMTaDFtvswm', KeyType.Ed25519) const indyVdrAnonCredsRegistry = new IndyVdrAnonCredsRegistry() const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, modules: { + indyVdr: new IndyVdrModule({ + networks: indyVdrModuleConfig.networks, + }), indySdk: new IndySdkModule({ indySdk, }), dids: new DidsModule({ - resolvers: [new IndyVdrSovDidResolver()], + resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()], }), }, }) -agent.dependencyManager.registerInstance(IndyVdrPoolService, indyVdrPoolService) +const indyVdrPoolService = agent.dependencyManager.resolve(IndyVdrPoolService) +const pool = indyVdrPoolService.getPoolForNamespace('pool:localtest') describe('IndyVdrAnonCredsRegistry', () => { beforeAll(async () => { @@ -60,13 +59,18 @@ describe('IndyVdrAnonCredsRegistry', () => { test('register and resolve a schema and credential definition', async () => { const dynamicVersion = `1.${Math.random() * 100}` + const legacyIssuerId = 'TL1EaPFCZ8Si5aUrqScBDt' + const signingKey = Key.fromPublicKeyBase58('FMGcFuU3QwAQLywxvmEnSorQT3NwU9wgDMMTaDFtvswm', KeyType.Ed25519) + const didIndyIssuerId = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' + + const legacySchemaId = `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}` + const didIndySchemaId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/SCHEMA/test/${dynamicVersion}` + const schemaResult = await indyVdrAnonCredsRegistry.registerSchema(agent.context, { - options: { - didIndyNamespace: 'pool:localtest', - }, + options: {}, schema: { attrNames: ['age'], - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, name: 'test', version: dynamicVersion, }, @@ -77,31 +81,47 @@ describe('IndyVdrAnonCredsRegistry', () => { state: 'finished', schema: { attrNames: ['age'], - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, name: 'test', version: dynamicVersion, }, - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + schemaId: didIndySchemaId, }, registrationMetadata: {}, schemaMetadata: { indyLedgerSeqNo: expect.any(Number), + }, + }) + + // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + + const legacySchema = await indyVdrAnonCredsRegistry.getSchema(agent.context, legacySchemaId) + expect(legacySchema).toMatchObject({ + schema: { + attrNames: ['age'], + name: 'test', + version: dynamicVersion, + issuerId: legacyIssuerId, + }, + schemaId: legacySchemaId, + resolutionMetadata: {}, + schemaMetadata: { didIndyNamespace: 'pool:localtest', + indyLedgerSeqNo: expect.any(Number), }, }) - const schemaResponse = await indyVdrAnonCredsRegistry.getSchema( - agent.context, - schemaResult.schemaState.schemaId as string - ) - expect(schemaResponse).toMatchObject({ + // Resolve using did indy schema id + const didIndySchema = await indyVdrAnonCredsRegistry.getSchema(agent.context, didIndySchemaId) + expect(didIndySchema).toMatchObject({ schema: { attrNames: ['age'], name: 'test', version: dynamicVersion, - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, }, - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + schemaId: didIndySchemaId, resolutionMetadata: {}, schemaMetadata: { didIndyNamespace: 'pool:localtest', @@ -109,137 +129,72 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }) + const legacyCredentialDefinitionId = `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG` + const didIndyCredentialDefinitionId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG` const credentialDefinitionResult = await indyVdrAnonCredsRegistry.registerCredentialDefinition(agent.context, { credentialDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, tag: 'TAG', - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + schemaId: didIndySchemaId, type: 'CL', - value: { - primary: { - n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', - s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', - r: { - master_secret: - '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', - age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', - }, - rctxt: - '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', - z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', - }, - revocation: { - g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - g_dash: - '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - htilde: - '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h_cap: - '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - }, - }, - }, - options: { - didIndyNamespace: 'pool:localtest', + value: credentialDefinitionValue, }, + options: {}, }) expect(credentialDefinitionResult).toMatchObject({ - credentialDefinitionMetadata: { - didIndyNamespace: 'pool:localtest', - }, + credentialDefinitionMetadata: {}, credentialDefinitionState: { credentialDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, tag: 'TAG', - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + schemaId: didIndySchemaId, type: 'CL', - value: { - primary: { - n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', - s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', - r: { - master_secret: - '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', - age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', - }, - rctxt: - '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', - z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', - }, - revocation: { - g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - g_dash: - '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - htilde: - '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h_cap: - '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - }, - }, + value: credentialDefinitionValue, }, - credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + credentialDefinitionId: didIndyCredentialDefinitionId, state: 'finished', }, registrationMetadata: {}, }) - const credentialDefinitionResponse = await indyVdrAnonCredsRegistry.getCredentialDefinition( + // Wait some time before resolving credential definition object + await new Promise((res) => setTimeout(res, 1000)) + + const legacyCredentialDefinition = await indyVdrAnonCredsRegistry.getCredentialDefinition( agent.context, - credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId as string + legacyCredentialDefinitionId ) - expect(credentialDefinitionResponse).toMatchObject({ - credentialDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + expect(legacyCredentialDefinition).toMatchObject({ + credentialDefinitionId: legacyCredentialDefinitionId, credentialDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', - schemaId: `TL1EaPFCZ8Si5aUrqScBDt:2:test:${dynamicVersion}`, + issuerId: legacyIssuerId, + schemaId: legacySchemaId, tag: 'TAG', type: 'CL', - value: { - primary: { - n: '96517142458750088826087901549537285521906361834839650465292394026155791790248920518228426560592477800345470631128393537910767968076647428853737338120375137978526133371095345886547568849980095910835456337942570110635942227498396677781945046904040000347997661394155645138402989185582727368743644878567330299129483548946710969360956979880962101169330048328620192831242584775824654760726417810662811409929761424969870024291961980782988854217354212087291593903213167261779548063894662259300608395552269380441482047725811646638173390809967510159302372018819245039226007682154490256871635806558216146474297742733244470144481', - s: '20992997088800769394205042281221010730843336204635587269131066142238627416871294692123680065003125450990475247419429111144686875080339959479648984195457400282722471552678361441816569115316390063503704185107464429408708889920969284364549487320740759452356010336698287092961864738455949515401889999320804333605635972368885179914619910494573144273759358510644118555354521660927445864167887629319425342133470781407706668100509422240127902573158722086763638357241708157836231326104213948080124231104027985997092193458353052131052627451830345602820935886233072722689872803371231173593216542422645374438328309647440653637339', - r: { - master_secret: - '96243300745227716230048295249700256382424379142767068560156597061550615821183969840133023439359733351013932957841392861447122785423145599004240865527901625751619237368187131360686977600247815596986496835118582544022443932674638843143227258367859921648385998241629365673854479167826898057354386557912400420925145402535066400276579674049751639901555837852972622061540154688641944145082381483273814616102862399655638465723909813901943343059991047747289931252070264205125933226649905593045675877143065756794349492159868513288280364195700788501708587588090219665708038121636837649207584981238653023213330207384929738192210', - age: '73301750658973501389860306433954162777688414647250690792688553201037736559940890441467927863421690990807820789906540409252803697381653459639864945429958798104818241892796218340966964349674689564019059435289373607451125919476002261041343187491848656595845611576458601110066647002078334660251906541846222115184239401618625285703919125402959929850028352261117167621349930047514115676870868726855651130262227714591240534532398809967792128535084773798290351459391475237061458901325844643172504167457543287673202618731404966555015061917662865397763636445953946274068384614117513804834235388565249331682010365807270858083546', - }, - rctxt: - '37788128721284563440858950515231840450431543928224096081933216180465915572829884228780081835462293611329848268384962871736884632087015070623933628853658097637604059748079512999518737243304794110313829761155878287344472916564970806851294430356498883927870926898737394894892797927804721407643833828162246495645836390303263072281761384240973982733122383052566872688887552226083782030670443318152427129452272570595367287061688769394567289624972332234661767648489253220495098949161964171486245324730862072203259801377135500275012560207100571502032523912388082460843991502336467718632746396226650194750972544436894286230063', - z: '43785356695890052462955676926428400928903479009358861113206349419200366390858322895540291303484939601128045362682307382393826375825484851021601464391509750565285197155653613669680662395620338416776539485377195826876505126073018100680273457526216247879013350460071029101583221000647494610122617904515744711339846577920055655093367012508192004131719432915903924789974568341538556528133188398290594619318653419602058489178526243446782729272985727332736198326183868783570550373552407121582843992983431205917273352230155794805507408743590383242904107596623095433284330566906935063373759426916339149701872288610119965287995', - }, - revocation: { - g: '1 0A84C28144BC8B677839038FFFA824AB5ADE517F8DD4A89F092FAF9A3560C62D 1 00FD708E112EEA5D89AF9D0559795E6DBCF56D3B8CDF79EFF34A72EB741F896F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - g_dash: - '1 201F3E23CC7E9284F3EFCF9500F1E2537C398EAB2E94D2EB801AECC7FBFBDC01 1 08132C7723CF9861D4CC24B56555EF1CBD9AE746C97B3ADFA36C669F2DCE09B6 1 1B2397FB2A1ADE704E2A1E4C242612F4677F9F1BD09E6B14C2E77E25EDA4C62E 1 00CDC2CF5F278D699D52223577AB032C150A3CB4C8E8AB07AB9D592772910E95 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - h: '1 072E0A505004F2F32B4210E72FA18A2ADF17F31479BD2059B7A8C0BA58F2ACB3 1 05C70F039E60317003C41C319753ECACC629791FDB06D6ADC5B06DD94501B973 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h0: '1 03CBE26D18118E9770D4A0B3E8607B3B3A8D3D3CA81FF8D41862430CC583156E 1 004A2A57E0A826AEFF007EDDAF89B02F054050843689167B10127FE9EDEEEDA9 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h1: '1 10C9F9DE537994E4FEF2625AFA78342C8A096238A875F6899DD500230E6022E5 1 0C0A88F53D020557377B4ED9C3826E9B8F918DD03E23B0F8ECD922F8333359D3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h2: '1 017F748AEEC1DDE4E4C3FBAE771C041F0A6FAEAF34FD02AF773AC4B75025147B 1 1298DBD9A4BEE6AD54E060A57BCE932735B7738C30A9ADAEFE2F38E1858A0183 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - htilde: - '1 0C471F0451D6AC352E28B6ECDE8D7233B75530AE59276DF0F4B9A8B0C5C7E5DB 1 24CE4461910AA5D60C09C24EE0FE51E1B1600D8BA6E483E9050EF897CA3E3C8A 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - h_cap: - '1 225B2106DEBD353AABDFC4C7F7E8660D308FB514EA9DAE0533DDEB65CF796159 1 1F6093622F439FC22C64F157F4F35F7C592EC0169C6F0026BC44CD3E375974A7 1 142126FAC3657AD846D394E1F72FD01ECC15E84416713CD133980E324B24F4BC 1 0357995DBDCD4385E59E607761AB30AE8D9DDE005A777EE846EF51AE2816CD33 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - u: '1 00D8DDC2EB6536CA320EE035D099937E59B11678162C1BFEB30C58FCA9F84650 1 1557A5B05A1A30D63322E187D323C9CA431BC5E811E68D4703933D9DDA26D299 1 10E8AB93AA87839B757521742EBA23C3B257C91F61A93D37AEC4C0A011B5F073 1 1DA65E40406A7875DA8CFCE9FD7F283145C166382A937B72819BDC335FE9A734 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - pk: '1 1A7EBBE3E7F8ED50959851364B20997944FA8AE5E3FC0A2BB531BAA17179D320 1 02C55FE6F64A2A4FF49B37C513C39E56ECD565CFAD6CA46DC6D8095179351863 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', - y: '1 1BF97F07270EC21A89E43BCA645D86A755F846B547238F1DA379E088CDD9B40D 1 146BB00F56FFC0DEF6541CEB484C718559B398DB1547B52850E46B23144161F1 1 079A1BEF8DFFA4E6352F701D476664340E7FBE5D3F46B897412BD2B5F10E33D7 1 02FDC508AEF90FB11961AF332BE4037973C76B954FFA48848F7E0588E93FCA8C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', - }, - }, + value: credentialDefinitionValue, + }, + credentialDefinitionMetadata: { + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) + + // resolve using did indy credential definition id + const didIndyCredentialDefinition = await indyVdrAnonCredsRegistry.getCredentialDefinition( + agent.context, + didIndyCredentialDefinitionId + ) + + expect(didIndyCredentialDefinition).toMatchObject({ + credentialDefinitionId: didIndyCredentialDefinitionId, + credentialDefinition: { + issuerId: didIndyIssuerId, + schemaId: didIndySchemaId, + tag: 'TAG', + type: 'CL', + value: credentialDefinitionValue, }, credentialDefinitionMetadata: { didIndyNamespace: 'pool:localtest', @@ -248,12 +203,13 @@ describe('IndyVdrAnonCredsRegistry', () => { }) // We don't support creating a revocation registry using AFJ yet, so we directly use indy-vdr to create the revocation registry - const revocationRegistryDefinitionId = `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` + const legacyRevocationRegistryId = `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResult.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag` + const didIndyRevocationRegistryId = `did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/REV_REG_DEF/${schemaResult.schemaMetadata.indyLedgerSeqNo}/TAG/tag` const revocationRegistryRequest = new RevocationRegistryDefinitionRequest({ submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', revocationRegistryDefinitionV1: { - credDefId: credentialDefinitionResponse.credentialDefinitionId, - id: revocationRegistryDefinitionId, + credDefId: legacyCredentialDefinitionId, + id: legacyRevocationRegistryId, revocDefType: 'CL_ACCUM', tag: 'tag', value: { @@ -277,7 +233,7 @@ describe('IndyVdrAnonCredsRegistry', () => { // Also create a revocation registry entry const revocationEntryRequest = new RevocationRegistryEntryRequest({ - revocationRegistryDefinitionId, + revocationRegistryDefinitionId: legacyRevocationRegistryId, revocationRegistryDefinitionType: 'CL_ACCUM', revocationRegistryEntry: { ver: '1.0', @@ -285,21 +241,50 @@ describe('IndyVdrAnonCredsRegistry', () => { accum: '1', }, }, - submitterDid: 'TL1EaPFCZ8Si5aUrqScBDt', + submitterDid: legacyIssuerId, }) // After this call we can query the revocation registry entries (using timestamp now) - const response = await pool.submitWriteRequest(agent.context, revocationEntryRequest, signingKey) + const entryResponse = await pool.submitWriteRequest(agent.context, revocationEntryRequest, signingKey) - const revocationRegistryDefintion = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( + const legacyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( agent.context, - revocationRegistryDefinitionId + legacyRevocationRegistryId ) + expect(legacyRevocationRegistryDefinition).toMatchObject({ + revocationRegistryDefinitionId: legacyRevocationRegistryId, + revocationRegistryDefinition: { + issuerId: legacyIssuerId, + revocDefType: 'CL_ACCUM', + value: { + maxCredNum: 100, + tailsHash: 'HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + tailsLocation: + '/var/folders/l3/xy8jzyvj4p5_d9g1123rt4bw0000gn/T/HLKresYcDSZYSKogq8wive4zyXNY84669MygftLFBG1i', + publicKeys: { + accumKey: { + z: '1 1812B206EB395D3AEBD4BBF53EBB0FFC3371D8BD6175316AB32C1C5F65452051 1 22A079D49C5351EFDC1410C81A1F6D8B2E3B79CFF20A30690C118FE2050F72CB 1 0FFC28B923A4654E261DB4CB5B9BABEFCB4DB189B20F52412B0CC9CCCBB8A3B2 1 1EE967C43EF1A3F487061D21B07076A26C126AAF7712E7B5CF5A53688DDD5CC0 1 009ED4D65879CA81DA8227D34CEA3B759B4627E1E2FFB273E9645CD4F3B10F19 1 1CF070212E1E213AEB472F56EDFC9D48009796C77B2D8CC16F2836E37B8715C2 1 04954F0B7B468781BAAE3291DD0E6FFA7F1AF66CAA4094D37B24363CC34606FB 1 115367CB755E9DB18781B3825CB1AEE2C334558B2C038E13DF57BB57CE1CF847 1 110D37EC05862EE2757A7DF39E814876FC97376FF8105D2D29619CB575537BDE 1 13C559A9563FCE083B3B39AE7E8FCA4099BEF3A4C8C6672E543D521F9DA88F96 1 137D87CC22ACC1B6B8C20EABE59F6ED456A58FE4CBEEFDFC4FA9B87E3EF32D17 1 00A2A9711737AAF0404F35AE502887AC6172B2B57D236BD4A40B45F659BFC696', + }, + }, + }, + tag: 'tag', + credDefId: legacyCredentialDefinitionId, + }, + revocationRegistryDefinitionMetadata: { + issuanceType: 'ISSUANCE_BY_DEFAULT', + didIndyNamespace: 'pool:localtest', + }, + resolutionMetadata: {}, + }) - expect(revocationRegistryDefintion).toMatchObject({ - revocationRegistryDefinitionId: `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag`, + const didIndyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( + agent.context, + didIndyRevocationRegistryId + ) + expect(didIndyRevocationRegistryDefinition).toMatchObject({ + revocationRegistryDefinitionId: didIndyRevocationRegistryId, revocationRegistryDefinition: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, revocDefType: 'CL_ACCUM', value: { maxCredNum: 100, @@ -313,7 +298,7 @@ describe('IndyVdrAnonCredsRegistry', () => { }, }, tag: 'tag', - credDefId: `TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG`, + credDefId: didIndyCredentialDefinitionId, }, revocationRegistryDefinitionMetadata: { issuanceType: 'ISSUANCE_BY_DEFAULT', @@ -322,26 +307,52 @@ describe('IndyVdrAnonCredsRegistry', () => { resolutionMetadata: {}, }) - const revocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( + const legacyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( + agent.context, + legacyRevocationRegistryId, + entryResponse.result.txnMetadata.txnTime + ) + + expect(legacyRevocationStatusList).toMatchObject({ + resolutionMetadata: {}, + revocationStatusList: { + issuerId: legacyIssuerId, + currentAccumulator: '1', + revRegId: legacyRevocationRegistryId, + revocationList: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + timestamp: entryResponse.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { + didIndyNamespace: 'pool:localtest', + }, + }) + + const didIndyRevocationStatusList = await indyVdrAnonCredsRegistry.getRevocationStatusList( agent.context, - revocationRegistryDefinitionId, - response.result.txnMetadata.txnTime + didIndyRevocationRegistryId, + entryResponse.result.txnMetadata.txnTime ) - expect(revocationStatusList).toMatchObject({ + expect(didIndyRevocationStatusList).toMatchObject({ resolutionMetadata: {}, revocationStatusList: { - issuerId: 'TL1EaPFCZ8Si5aUrqScBDt', + issuerId: didIndyIssuerId, currentAccumulator: '1', - revRegId: `TL1EaPFCZ8Si5aUrqScBDt:4:TL1EaPFCZ8Si5aUrqScBDt:3:CL:${schemaResponse.schemaMetadata.indyLedgerSeqNo}:TAG:CL_ACCUM:tag`, + revRegId: didIndyRevocationRegistryId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], - timestamp: response.result.txnMetadata.txnTime, + timestamp: entryResponse.result.txnMetadata.txnTime, + }, + revocationStatusListMetadata: { + didIndyNamespace: 'pool:localtest', }, - revocationStatusListMetadata: { didIndyNamespace: 'pool:localtest' }, }) }) }) diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index ec6724d576..65eee36cf3 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -1,94 +1,81 @@ +import type { IndyVdrDidCreateOptions } from '../src/dids/IndyVdrIndyDidRegistrar' + import { Key, - InjectionSymbols, - CacheModuleConfig, - InMemoryLruCache, JsonTransformer, KeyType, - SigningProviderRegistry, TypedArrayEncoder, DidCommV1Service, DidCommV2Service, DidDocumentService, + Agent, + DidsModule, } from '@aries-framework/core' import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' -import { Subject } from 'rxjs' -import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' -import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' -import testLogger from '../../core/tests/logger' -import { IndySdkWallet } from '../../indy-sdk/src' +import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' +import { IndySdkModule } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { IndyVdrModule, IndyVdrSovDidResolver } from '../src' import { IndyVdrIndyDidRegistrar } from '../src/dids/IndyVdrIndyDidRegistrar' import { IndyVdrIndyDidResolver } from '../src/dids/IndyVdrIndyDidResolver' import { indyDidFromNamespaceAndInitialKey } from '../src/dids/didIndyUtil' -import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' import { DID_INDY_REGEX } from '../src/utils/did' import { indyVdrModuleConfig } from './helpers' -const logger = testLogger -const wallet = new IndySdkWallet(indySdk, logger, new SigningProviderRegistry([])) - -const agentConfig = getAgentConfig('IndyVdrIndyDidRegistrar E2E', { logger }) - -const cache = new InMemoryLruCache({ limit: 200 }) -const indyVdrIndyDidResolver = new IndyVdrIndyDidResolver() -const indyVdrIndyDidRegistrar = new IndyVdrIndyDidRegistrar() - -let signerKey: Key - -const agentContext = getAgentContext({ - wallet, - agentConfig, - registerInstances: [ - [InjectionSymbols.Stop$, new Subject()], - [InjectionSymbols.AgentDependencies, agentDependencies], - [InjectionSymbols.StorageService, new InMemoryStorageService()], - [IndyVdrPoolService, new IndyVdrPoolService(logger, indyVdrModuleConfig)], - [CacheModuleConfig, new CacheModuleConfig({ cache })], - ], -}) +const agent = new Agent( + getAgentOptions( + 'Indy VDR Indy DID Registrar', + {}, + { + indyVdr: new IndyVdrModule({ + networks: indyVdrModuleConfig.networks, + }), + indySdk: new IndySdkModule({ + indySdk, + }), + dids: new DidsModule({ + registrars: [new IndyVdrIndyDidRegistrar()], + resolvers: [new IndyVdrIndyDidResolver(), new IndyVdrSovDidResolver()], + }), + } + ) +) -const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) +describe('Indy VDR Indy Did Registrar', () => { + let submitterDid: string -describe('Indy VDR registrar E2E', () => { beforeAll(async () => { - await wallet.createAndOpen(agentConfig.walletConfig) - - signerKey = await wallet.createKey({ - privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), - keyType: KeyType.Ed25519, - }) + await agent.initialize() + const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + agent, + TypedArrayEncoder.fromString('000000000000000000000000Trustee9') + ) + submitterDid = `did:indy:pool:localtest:${unqualifiedSubmitterDid}` }) afterAll(async () => { - for (const pool of indyVdrPoolService.pools) { - pool.close() - } - - await wallet.delete() + await agent.shutdown() + await agent.wallet.delete() }) test('can register a did:indy without services', async () => { - const didRegistrationResult = await indyVdrIndyDidRegistrar.create(agentContext, { + const didRegistrationResult = await agent.dids.create({ method: 'indy', options: { - submitterDid: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', - submitterVerkey: signerKey.publicKeyBase58, + submitterDid, }, }) expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ didDocumentMetadata: {}, - didRegistrationMetadata: { - didIndyNamespace: 'pool:localtest', - }, + didRegistrationMetadata: {}, didState: { state: 'finished', did: expect.stringMatching(DID_INDY_REGEX), didDocument: { - '@context': ['https://w3id.org/did/v1'], + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], id: expect.stringMatching(DID_INDY_REGEX), alsoKnownAs: undefined, controller: undefined, @@ -109,14 +96,12 @@ describe('Indy VDR registrar E2E', () => { }) const did = didRegistrationResult.didState.did - if (!did) { - throw Error('did not defined') - } + if (!did) throw Error('did not defined') - const didResolutionResult = await indyVdrIndyDidResolver.resolve(agentContext, did) + const didResolutionResult = await agent.dids.resolve(did) expect(JsonTransformer.toJSON(didResolutionResult)).toMatchObject({ didDocument: { - '@context': ['https://w3id.org/did/v1'], + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], id: did, alsoKnownAs: undefined, controller: undefined, @@ -154,26 +139,22 @@ describe('Indy VDR registrar E2E', () => { 'pool:localtest', Key.fromPublicKey(keyPair.publicKey, KeyType.Ed25519) ) - const didRegistrationResult = await indyVdrIndyDidRegistrar.create(agentContext, { - method: 'indy', + const didRegistrationResult = await agent.dids.create({ did, options: { - submitterDid: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', - submitterVerkey: signerKey.publicKeyBase58, + submitterDid, verkey, }, }) expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ didDocumentMetadata: {}, - didRegistrationMetadata: { - didIndyNamespace: 'pool:localtest', - }, + didRegistrationMetadata: {}, didState: { state: 'finished', did, didDocument: { - '@context': ['https://w3id.org/did/v1'], + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], id: did, alsoKnownAs: undefined, controller: undefined, @@ -193,10 +174,10 @@ describe('Indy VDR registrar E2E', () => { }, }) - const didResult = await indyVdrIndyDidResolver.resolve(agentContext, did) + const didResult = await agent.dids.resolve(did) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: { - '@context': ['https://w3id.org/did/v1'], + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], id: did, alsoKnownAs: undefined, controller: undefined, @@ -229,7 +210,7 @@ describe('Indy VDR registrar E2E', () => { .slice(0, 32) ) - const key = await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) + const key = await agent.wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(key.publicKey)) const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(key.publicKey) @@ -238,12 +219,10 @@ describe('Indy VDR registrar E2E', () => { Key.fromPublicKey(key.publicKey, KeyType.Ed25519) ) - const didRegistrationResult = await indyVdrIndyDidRegistrar.create(agentContext, { - method: 'indy', + const didRegistrationResult = await agent.dids.create({ did, options: { - submitterDid: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', - submitterVerkey: signerKey.publicKeyBase58, + submitterDid, useEndpointAttrib: true, verkey, services: [ @@ -271,7 +250,12 @@ describe('Indy VDR registrar E2E', () => { }) const expectedDidDocument = { - '@context': ['https://w3id.org/did/v1', 'https://didcomm.org/messaging/contexts/v2'], + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], id: did, alsoKnownAs: undefined, controller: undefined, @@ -319,9 +303,7 @@ describe('Indy VDR registrar E2E', () => { expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ didDocumentMetadata: {}, - didRegistrationMetadata: { - didIndyNamespace: 'pool:localtest', - }, + didRegistrationMetadata: {}, didState: { state: 'finished', did, @@ -329,7 +311,7 @@ describe('Indy VDR registrar E2E', () => { }, }) - const didResult = await indyVdrIndyDidResolver.resolve(agentContext, did) + const didResult = await agent.dids.resolve(did) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: expectedDidDocument, didDocumentMetadata: {}, diff --git a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts deleted file mode 100644 index bc0e5f4ea8..0000000000 --- a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts +++ /dev/null @@ -1,181 +0,0 @@ -import type { Key } from '@aries-framework/core' - -import { - TypedArrayEncoder, - CacheModuleConfig, - InMemoryLruCache, - JsonTransformer, - KeyType, - SigningProviderRegistry, -} from '@aries-framework/core' - -import { parseDid } from '../../core/src/modules/dids/domain/parse' -import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' -import testLogger from '../../core/tests/logger' -import { IndySdkWallet } from '../../indy-sdk/src' -import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' -import { IndyVdrSovDidResolver } from '../src/dids' -import { IndyVdrPoolService } from '../src/pool/IndyVdrPoolService' -import { indyDidFromPublicKeyBase58 } from '../src/utils/did' - -import { createDidOnLedger, indyVdrModuleConfig } from './helpers' - -const logger = testLogger -const wallet = new IndySdkWallet(indySdk, logger, new SigningProviderRegistry([])) -const agentConfig = getAgentConfig('IndyVdrResolver E2E', { logger }) - -const cache = new InMemoryLruCache({ limit: 200 }) -const indyVdrSovDidResolver = new IndyVdrSovDidResolver() - -let signerKey: Key - -const agentContext = getAgentContext({ - wallet, - agentConfig, - registerInstances: [ - [IndyVdrPoolService, new IndyVdrPoolService(logger, indyVdrModuleConfig)], - [CacheModuleConfig, new CacheModuleConfig({ cache })], - ], -}) - -const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - -describe('indy-vdr DID Resolver E2E', () => { - beforeAll(async () => { - await wallet.createAndOpen(agentConfig.walletConfig) - - signerKey = await wallet.createKey({ - privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), - keyType: KeyType.Ed25519, - }) - }) - - afterAll(async () => { - for (const pool of indyVdrPoolService.pools) { - pool.close() - } - - await wallet.delete() - }) - - describe('did:sov resolver', () => { - test('can resolve a did sov using the pool', async () => { - const did = 'did:sov:TL1EaPFCZ8Si5aUrqScBDt' - const didResult = await indyVdrSovDidResolver.resolve(agentContext, did, parseDid(did)) - expect(JsonTransformer.toJSON(didResult)).toMatchObject({ - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - ], - id: did, - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - type: 'Ed25519VerificationKey2018', - controller: did, - id: `${did}#key-1`, - publicKeyBase58: expect.any(String), - }, - { - controller: did, - type: 'X25519KeyAgreementKey2019', - id: `${did}#key-agreement-1`, - publicKeyBase58: expect.any(String), - }, - ], - capabilityDelegation: undefined, - capabilityInvocation: undefined, - authentication: [`${did}#key-1`], - assertionMethod: [`${did}#key-1`], - keyAgreement: [`${did}#key-agreement-1`], - service: undefined, - }, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - - test('resolve a did with endpoints', async () => { - // First we need to create a new DID and add ATTRIB endpoint to it - const { did } = await createDidOnLedger( - indyVdrPoolService, - agentContext, - indyDidFromPublicKeyBase58(signerKey.publicKeyBase58), - signerKey - ) - - // DID created. Now resolve it - - const fullyQualifiedDid = `did:sov:${did}` - const didResult = await indyVdrSovDidResolver.resolve( - agentContext, - fullyQualifiedDid, - parseDid(fullyQualifiedDid) - ) - expect(JsonTransformer.toJSON(didResult)).toMatchObject({ - didDocument: { - '@context': [ - 'https://w3id.org/did/v1', - 'https://w3id.org/security/suites/ed25519-2018/v1', - 'https://w3id.org/security/suites/x25519-2019/v1', - 'https://didcomm.org/messaging/contexts/v2', - ], - id: fullyQualifiedDid, - alsoKnownAs: undefined, - controller: undefined, - verificationMethod: [ - { - type: 'Ed25519VerificationKey2018', - controller: fullyQualifiedDid, - id: `${fullyQualifiedDid}#key-1`, - publicKeyBase58: expect.any(String), - }, - { - controller: fullyQualifiedDid, - type: 'X25519KeyAgreementKey2019', - id: `${fullyQualifiedDid}#key-agreement-1`, - publicKeyBase58: expect.any(String), - }, - ], - capabilityDelegation: undefined, - capabilityInvocation: undefined, - authentication: [`${fullyQualifiedDid}#key-1`], - assertionMethod: [`${fullyQualifiedDid}#key-1`], - keyAgreement: [`${fullyQualifiedDid}#key-agreement-1`], - service: [ - { - id: `${fullyQualifiedDid}#endpoint`, - type: 'endpoint', - serviceEndpoint: 'https://agent.com', - }, - { - id: `${fullyQualifiedDid}#did-communication`, - type: 'did-communication', - priority: 0, - recipientKeys: [`${fullyQualifiedDid}#key-agreement-1`], - routingKeys: ['routingKey1', 'routingKey2'], - accept: ['didcomm/aip2;env=rfc19'], - serviceEndpoint: 'https://agent.com', - }, - { - id: `${fullyQualifiedDid}#didcomm-1`, - type: 'DIDComm', - serviceEndpoint: 'https://agent.com', - accept: ['didcomm/v2'], - routingKeys: ['routingKey1', 'routingKey2'], - }, - ], - }, - didDocumentMetadata: {}, - didResolutionMetadata: { - contentType: 'application/did+ld+json', - }, - }) - }) - }) -}) diff --git a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts new file mode 100644 index 0000000000..209b05e698 --- /dev/null +++ b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts @@ -0,0 +1,143 @@ +import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' + +import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' +import { IndySdkModule } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { IndyVdrModule } from '../src' +import { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from '../src/dids' + +import { createDidOnLedger, indyVdrModuleConfig } from './helpers' + +const agent = new Agent( + getAgentOptions( + 'Indy VDR Indy DID resolver', + {}, + { + indyVdr: new IndyVdrModule({ + networks: indyVdrModuleConfig.networks, + }), + indySdk: new IndySdkModule({ + indySdk, + }), + dids: new DidsModule({ + registrars: [new IndyVdrIndyDidRegistrar()], + resolvers: [new IndyVdrIndyDidResolver(), new IndyVdrSovDidResolver()], + }), + } + ) +) + +describe('indy-vdr DID Resolver E2E', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + test('resolve a did:indy did', async () => { + const did = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' + const didResult = await agent.dids.resolve(did) + + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + assertionMethod: undefined, + keyAgreement: undefined, + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('resolve a did with endpoints', async () => { + const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + agent, + TypedArrayEncoder.fromString('000000000000000000000000Trustee9') + ) + + // First we need to create a new DID and add ATTRIB endpoint to it + const { did } = await createDidOnLedger(agent, `did:indy:pool:localtest:${unqualifiedSubmitterDid}`) + + // DID created. Now resolve it + const didResult = await agent.dids.resolve(did) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: expect.any(String), + }, + { + controller: did, + type: 'X25519KeyAgreementKey2019', + id: `${did}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + assertionMethod: undefined, + keyAgreement: [`${did}#key-agreement-1`], + service: [ + { + id: `${did}#endpoint`, + serviceEndpoint: 'http://localhost:3000', + type: 'endpoint', + }, + { + id: `${did}#did-communication`, + accept: ['didcomm/aip2;env=rfc19'], + priority: 0, + recipientKeys: [`${did}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'http://localhost:3000', + type: 'did-communication', + }, + { + id: `${did}#didcomm-1`, + accept: ['didcomm/v2'], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'http://localhost:3000', + type: 'DIDComm', + }, + ], + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) +}) diff --git a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts new file mode 100644 index 0000000000..2ff501c641 --- /dev/null +++ b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts @@ -0,0 +1,157 @@ +import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' + +import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' +import { IndySdkModule } from '../../indy-sdk/src' +import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' +import { IndyVdrModule } from '../src' +import { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from '../src/dids' +import { parseIndyDid } from '../src/dids/didIndyUtil' + +import { createDidOnLedger, indyVdrModuleConfig } from './helpers' + +const agent = new Agent( + getAgentOptions( + 'Indy VDR Sov DID resolver', + {}, + { + indyVdr: new IndyVdrModule({ + networks: indyVdrModuleConfig.networks, + }), + indySdk: new IndySdkModule({ + indySdk, + }), + dids: new DidsModule({ + registrars: [new IndyVdrIndyDidRegistrar()], + resolvers: [new IndyVdrIndyDidResolver(), new IndyVdrSovDidResolver()], + }), + } + ) +) + +describe('Indy VDR Sov DID Resolver', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + test('resolve a did:sov did', async () => { + const did = 'did:sov:TL1EaPFCZ8Si5aUrqScBDt' + const didResult = await agent.dids.resolve(did) + + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + ], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#key-1`, + publicKeyBase58: expect.any(String), + }, + { + controller: did, + type: 'X25519KeyAgreementKey2019', + id: `${did}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#key-1`], + assertionMethod: [`${did}#key-1`], + keyAgreement: [`${did}#key-agreement-1`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('resolve a did with endpoints', async () => { + const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( + agent, + TypedArrayEncoder.fromString('000000000000000000000000Trustee9') + ) + + // First we need to create a new DID and add ATTRIB endpoint to it + const { did } = await createDidOnLedger(agent, `did:indy:pool:localtest:${unqualifiedSubmitterDid}`) + const { namespaceIdentifier } = parseIndyDid(did) + const sovDid = `did:sov:${namespaceIdentifier}` + + // DID created. Now resolve it + const didResult = await agent.dids.resolve(sovDid) + + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: sovDid, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: sovDid, + id: `${sovDid}#key-1`, + publicKeyBase58: expect.any(String), + }, + { + controller: sovDid, + type: 'X25519KeyAgreementKey2019', + id: `${sovDid}#key-agreement-1`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${sovDid}#key-1`], + assertionMethod: [`${sovDid}#key-1`], + keyAgreement: [`${sovDid}#key-agreement-1`], + service: [ + { + id: `${sovDid}#endpoint`, + serviceEndpoint: 'http://localhost:3000', + type: 'endpoint', + }, + { + id: `${sovDid}#did-communication`, + accept: ['didcomm/aip2;env=rfc19'], + priority: 0, + recipientKeys: [`${sovDid}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'http://localhost:3000', + type: 'did-communication', + }, + { + id: `${sovDid}#didcomm-1`, + accept: ['didcomm/v2'], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'http://localhost:3000', + type: 'DIDComm', + }, + ], + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) +}) diff --git a/yarn.lock b/yarn.lock index a96c0c26f9..ae76e3b622 100644 --- a/yarn.lock +++ b/yarn.lock @@ -896,22 +896,22 @@ dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.6": - version "0.1.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.6.tgz#28946107feb6c641839de843cc7ddca9eef01f8d" - integrity sha512-jtFRkfjiveKIfeyTx9qDAUXLtpFaiZlUGN2ts2zX8QvY6XGZEKc6LvhMPzLW5kLW34u6ldEqjG4mjyAawUKRsA== +"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.10": + version "0.1.0-dev.10" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.10.tgz#f5fc988b3c11766475f7f084f814af8a4eca0caf" + integrity sha512-wETVax9HRidhRTRiRqwLTj/looNCvjZ3vdqMoqmPbmet5sM9eRkU9v4ipz4paqz4LiZipTx2fisLyYskKW9g6A== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.6" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.10" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" ref-array-di "^1.2.2" ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.6", "@hyperledger/indy-vdr-shared@^0.1.0-dev.6": - version "0.1.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.6.tgz#345b6ff60d29d74615ee882e225af674315a0bda" - integrity sha512-MIUdm3zIwKfFZmZSwbAPiZHEZE0HhsIPjeEWiu//Z1m9GDDSNhEyxsHuVN17pE0pcxwbuCQrIaK3v4Tc6x0jJw== +"@hyperledger/indy-vdr-shared@0.1.0-dev.10", "@hyperledger/indy-vdr-shared@^0.1.0-dev.10": + version "0.1.0-dev.10" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.10.tgz#2a6d09a3a6ae3458ae9209ab7b6f09435e88b9c9" + integrity sha512-7fr9/Iliu0u94qE8u1g/RoLJOwMH0kFdclttwBHfTAKTQyfoXUPe1yIZ8ITRgLwnmFYPQpRa54lU+uv15c37fg== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" From c72ba149bad3a4596f5818b28516f6286b9088bf Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 8 Mar 2023 17:40:03 +0100 Subject: [PATCH 081/139] fix(askar): custom error handling (#1372) Signed-off-by: Timo Glastra --- packages/askar/package.json | 4 ++-- .../askar/src/storage/AskarStorageService.ts | 18 +----------------- .../__tests__/AskarStorageService.test.ts | 2 +- packages/askar/src/wallet/AskarWallet.ts | 12 ------------ .../src/wallet/__tests__/AskarWallet.test.ts | 2 +- .../askar/tests/askar-postgres.e2e.test.ts | 3 ++- yarn.lock | 18 +++++++++--------- 7 files changed, 16 insertions(+), 43 deletions(-) diff --git a/packages/askar/package.json b/packages/askar/package.json index 9e4024caea..dceb761c97 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.3", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.4", "bn.js": "^5.2.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", @@ -34,7 +34,7 @@ }, "devDependencies": { "@types/bn.js": "^5.1.0", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.3", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.4", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index d901f6e767..3c4dcda0ec 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -115,17 +115,7 @@ export class AskarStorageService implements StorageService } return recordToInstance(record, recordClass) } catch (error) { - if ( - isAskarError(error) && - (error.code === AskarErrorCode.NotFound || - // FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario - error.message === 'Received null pointer. The native library could not find the value.') - ) { - throw new RecordNotFoundError(`record with id ${id} not found.`, { - recordType: recordClass.type, - cause: error, - }) - } + if (error instanceof RecordNotFoundError) throw error throw new WalletError(`Error getting record`, { cause: error }) } } @@ -169,12 +159,6 @@ export class AskarStorageService implements StorageService } return instances } catch (error) { - if ( - isAskarError(error) && // FIXME: this is current output from askar wrapper but does not describe specifically a 0 length scenario - error.message === 'Received null pointer. The native library could not find the value.' - ) { - return instances - } throw new WalletError(`Error executing query`, { cause: error }) } } diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index 70ba8bec1f..956d0b124b 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -73,7 +73,7 @@ describeRunInNodeVersion([18], 'AskarStorageService', () => { forUpdate: false, }) - expect(JSON.parse(retrieveRecord.getTags(0))).toEqual({ + expect(JSON.parse(retrieveRecord?.getTags(0) ?? '{}')).toEqual({ someBoolean: '1', someOtherBoolean: '0', someStringValue: 'string', diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 35831c43a6..06985912ef 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -17,7 +17,6 @@ import { isValidSeed, isValidPrivateKey, JsonTransformer, - RecordNotFoundError, WalletInvalidKeyError, WalletDuplicateError, JsonEncoder, @@ -715,17 +714,6 @@ export class AskarWallet implements Wallet { throw new WalletError(`No content found for record with public key: ${publicKeyBase58}`) } } catch (error) { - if ( - isAskarError(error) && - (error.code === AskarErrorCode.NotFound || - // FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario - error.message === 'Received null pointer. The native library could not find the value.') - ) { - throw new RecordNotFoundError(`KeyPairRecord not found for public key: ${publicKeyBase58}.`, { - recordType: 'KeyPairRecord', - cause: error, - }) - } throw new WalletError('Error retrieving KeyPair record', { cause: error }) } } diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 18b9fec9d0..ffbb648999 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -212,7 +212,7 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { }) }) -describe('AskarWallet management', () => { +describeRunInNodeVersion([18], 'AskarWallet management', () => { let askarWallet: AskarWallet afterEach(async () => { diff --git a/packages/askar/tests/askar-postgres.e2e.test.ts b/packages/askar/tests/askar-postgres.e2e.test.ts index dfbc6db600..c25f6d16c3 100644 --- a/packages/askar/tests/askar-postgres.e2e.test.ts +++ b/packages/askar/tests/askar-postgres.e2e.test.ts @@ -6,6 +6,7 @@ import type { ConnectionRecord } from '@aries-framework/core' import { Agent, HandshakeProtocol } from '@aries-framework/core' import { Subject } from 'rxjs' +import { describeRunInNodeVersion } from '../../../tests/runInVersion' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { waitForBasicMessage } from '../../core/tests/helpers' @@ -31,7 +32,7 @@ const bobPostgresAgentOptions = getPostgresAgentOptions('AgentsBob', storageConf }) // FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved -describe.skip('Askar Postgres agents', () => { +describeRunInNodeVersion([18], 'Askar Postgres agents', () => { let aliceAgent: Agent let bobAgent: Agent let aliceConnection: ConnectionRecord diff --git a/yarn.lock b/yarn.lock index ae76e3b622..1e95de9d5f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -876,12 +876,12 @@ resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.6.tgz#2e6afb4641cc25daef4074a32990ec078d2fd94e" integrity sha512-4YZ2kzhOOrGRL//n/Qe/A+yyGsbnHqojQW6vGEZQpZ9bf4ir+QaZLRHijgXFmffMA+SRONfdlnxhLJ6/b6ZaMg== -"@hyperledger/aries-askar-nodejs@^0.1.0-dev.3": - version "0.1.0-dev.3" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.3.tgz#19ecff41f81525efea8212a3ad6b8c3db11950c4" - integrity sha512-9hnCNWxIRkLP793P4DuZAJRWfxf1v6NdQyEgoMdNletcP7KAf/YfBqySTYGqA6TIiMu/abNrmq+WsHkK0yyZ+g== +"@hyperledger/aries-askar-nodejs@^0.1.0-dev.4": + version "0.1.0-dev.4" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.4.tgz#016f89886732366eff9cba54eb6fcbf02cc5a212" + integrity sha512-Wh1SoxakBpQvgbFrLq+NIJ0l02N8SjBRDZBs/c55gIeenXzDJY/ZgfM6faLdDv4XeE6agXd4yl35f4s7+3zK+Q== dependencies: - "@hyperledger/aries-askar-shared" "0.1.0-dev.3" + "@hyperledger/aries-askar-shared" "0.1.0-dev.4" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" node-cache "^5.1.2" @@ -889,10 +889,10 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.1.0-dev.3", "@hyperledger/aries-askar-shared@^0.1.0-dev.3": - version "0.1.0-dev.3" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.3.tgz#2056db8c0671ec4b1e926e1491fdca9357ede633" - integrity sha512-LIRyCg2PK6wN483Bdzq4eJmQ2LNCCRq2g7GF4yv+H+V04ky7hdeoJbSKN8lYr/OQn1tS6ALx9p2ArvAt7pTfVw== +"@hyperledger/aries-askar-shared@0.1.0-dev.4", "@hyperledger/aries-askar-shared@^0.1.0-dev.4": + version "0.1.0-dev.4" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.4.tgz#439fbaa3911be56c134cbacfddd1553eb0b06dde" + integrity sha512-z+bWVbFD3S7IuYlG2XTxCjyaJWmS/wiHtYRxgWXjF6o4iR2vW+/y0NhTpiX2gO4Gx62zxFfh50b0oQTOM6/XqQ== dependencies: fast-text-encoding "^1.0.3" From 19cefa54596a4e4848bdbe89306a884a5ce2e991 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 11 Mar 2023 07:20:49 -0300 Subject: [PATCH 082/139] feat(askar): import/export wallet support for SQLite (#1377) Signed-off-by: Ariel Gentile --- packages/askar/src/wallet/AskarWallet.ts | 376 ++++++++++++------ .../src/wallet/__tests__/AskarWallet.test.ts | 8 +- packages/askar/tests/askar-sqlite.e2e.test.ts | 187 +++++++++ packages/askar/tests/helpers.ts | 22 +- packages/core/src/storage/FileSystem.ts | 1 + packages/node/src/NodeFileSystem.ts | 6 +- .../react-native/src/ReactNativeFileSystem.ts | 4 + 7 files changed, 471 insertions(+), 133 deletions(-) create mode 100644 packages/askar/tests/askar-sqlite.e2e.test.ts diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 06985912ef..4084d5a415 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -9,8 +9,9 @@ import type { WalletConfigRekey, KeyPair, KeyDerivationMethod, + WalletExportImportConfig, } from '@aries-framework/core' -import type { Session } from '@hyperledger/aries-askar-shared' +import type { KeyEntryObject, Session } from '@hyperledger/aries-askar-shared' import { WalletKeyExistsError, @@ -131,6 +132,14 @@ export class AskarWallet implements Wallet { this.logger.debug(`Creating wallet '${walletConfig.id}`) const askarWalletConfig = await this.getAskarWalletConfig(walletConfig) + + // Check if database exists + const { path: filePath } = uriFromWalletConfig(walletConfig, this.fileSystem.dataPath) + if (filePath && (await this.fileSystem.exists(filePath))) { + throw new WalletDuplicateError(`Wallet '${walletConfig.id}' already exists.`, { + walletType: 'AskarWallet', + }) + } try { this._store = await Store.provision({ recreate: false, @@ -223,7 +232,9 @@ export class AskarWallet implements Wallet { if (rekey) { await this._store.rekey({ passKey: rekey, - keyMethod: keyDerivationMethodToStoreKeyMethod(rekeyDerivation) ?? StoreKeyMethod.Raw, + keyMethod: + keyDerivationMethodToStoreKeyMethod(rekeyDerivation) ?? + (`${StoreKeyMethod.Kdf}:argon2i:int` as StoreKeyMethod), }) } this._session = await this._store.openSession() @@ -246,10 +257,7 @@ export class AskarWallet implements Wallet { cause: error, }) } - throw new WalletError( - `Error opening wallet ${walletConfig.id}. ERROR CODE ${error.code} MESSAGE ${error.message}`, - { cause: error } - ) + throw new WalletError(`Error opening wallet ${walletConfig.id}: ${error.message}`, { cause: error }) } this.logger.debug(`Wallet '${walletConfig.id}' opened with handle '${this._store.handle.handle}'`) @@ -267,7 +275,6 @@ export class AskarWallet implements Wallet { } this.logger.info(`Deleting wallet '${this.walletConfig.id}'`) - if (this._store) { await this.close() } @@ -286,14 +293,89 @@ export class AskarWallet implements Wallet { } } - public async export() { - // TODO - throw new WalletError('AskarWallet Export not yet implemented') + public async export(exportConfig: WalletExportImportConfig) { + if (!this.walletConfig) { + throw new WalletError( + 'Can not export wallet that does not have wallet config set. Make sure to open it before exporting' + ) + } + + const { path: destinationPath, key: exportKey } = exportConfig + + const { path: sourcePath } = uriFromWalletConfig(this.walletConfig, this.fileSystem.dataPath) + if (!sourcePath) { + throw new WalletError('Export is only supported for SQLite backend') + } + + try { + // This method ensures that destination directory is created + const exportedWalletConfig = await this.getAskarWalletConfig({ + ...this.walletConfig, + storage: { type: 'sqlite', path: destinationPath }, + }) + + // Close this wallet before copying + await this.close() + + // Copy wallet to the destination path + await this.fileSystem.copyFile(sourcePath, destinationPath) + + // Open exported wallet and rotate its key to the one requested + const exportedWalletStore = await Store.open({ + uri: exportedWalletConfig.uri, + keyMethod: exportedWalletConfig.keyMethod, + passKey: exportedWalletConfig.passKey, + }) + await exportedWalletStore.rekey({ keyMethod: exportedWalletConfig.keyMethod, passKey: exportKey }) + + await exportedWalletStore.close() + + await this._open(this.walletConfig) + } catch (error) { + const errorMessage = `Error exporting wallet '${this.walletConfig.id}': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } } - public async import() { - // TODO - throw new WalletError('AskarWallet Import not yet implemented') + public async import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig) { + const { path: sourcePath, key: importKey } = importConfig + const { path: destinationPath } = uriFromWalletConfig(walletConfig, this.fileSystem.dataPath) + + if (!destinationPath) { + throw new WalletError('Import is only supported for SQLite backend') + } + + try { + // This method ensures that destination directory is created + const importWalletConfig = await this.getAskarWalletConfig(walletConfig) + + // Copy wallet to the destination path + await this.fileSystem.copyFile(sourcePath, destinationPath) + + // Open imported wallet and rotate its key to the one requested + const importedWalletStore = await Store.open({ + uri: importWalletConfig.uri, + keyMethod: importWalletConfig.keyMethod, + passKey: importKey, + }) + + await importedWalletStore.rekey({ keyMethod: importWalletConfig.keyMethod, passKey: importWalletConfig.passKey }) + + await importedWalletStore.close() + } catch (error) { + const errorMessage = `Error importing wallet '${walletConfig.id}': ${error.message}` + this.logger.error(errorMessage, { + error, + errorMessage: error.message, + }) + + throw new WalletError(errorMessage, { cause: error }) + } } /** @@ -343,17 +425,21 @@ export class AskarWallet implements Wallet { const algorithm = keyAlgFromString(keyType) // Create key - const key = privateKey - ? AskarKey.fromSecretBytes({ secretKey: privateKey, algorithm }) - : seed - ? AskarKey.fromSeed({ seed, algorithm }) - : AskarKey.generate(algorithm) - - // Store key + let key: AskarKey | undefined try { - await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(key.publicBytes) }) - return Key.fromPublicKey(key.publicBytes, keyType) + const key = privateKey + ? AskarKey.fromSecretBytes({ secretKey: privateKey, algorithm }) + : seed + ? AskarKey.fromSeed({ seed, algorithm }) + : AskarKey.generate(algorithm) + + const keyPublicBytes = key.publicBytes + // Store key + await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(keyPublicBytes) }) + key.handle.free() + return Key.fromPublicKey(keyPublicBytes, keyType) } catch (error) { + key?.handle.free() // Handle case where key already exists if (isAskarError(error, AskarErrorCode.Duplicate)) { throw new WalletKeyExistsError('Key already exists') @@ -393,12 +479,13 @@ export class AskarWallet implements Wallet { * @returns A signature for the data */ public async sign({ data, key }: WalletSignOptions): Promise { + let keyEntry: KeyEntryObject | null | undefined try { if (keyTypeSupportedByAskar(key.keyType)) { if (!TypedArrayEncoder.isTypedArray(data)) { throw new WalletError(`Currently not supporting signing of multiple messages`) } - const keyEntry = await this.session.fetchKey({ name: key.publicKeyBase58 }) + keyEntry = await this.session.fetchKey({ name: key.publicKeyBase58 }) if (!keyEntry) { throw new WalletError('Key entry not found') @@ -406,6 +493,8 @@ export class AskarWallet implements Wallet { const signed = keyEntry.key.signMessage({ message: data as Buffer }) + keyEntry.key.handle.free() + return Buffer.from(signed) } else { // Check if there is a signing key provider for the specified key type. @@ -424,6 +513,7 @@ export class AskarWallet implements Wallet { throw new WalletError(`Unsupported keyType: ${key.keyType}`) } } catch (error) { + keyEntry?.key.handle.free() if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) } @@ -444,6 +534,7 @@ export class AskarWallet implements Wallet { * @throws {WalletError} When an unsupported keytype is used */ public async verify({ data, key, signature }: WalletVerifyOptions): Promise { + let askarKey: AskarKey | undefined try { if (keyTypeSupportedByAskar(key.keyType)) { if (!TypedArrayEncoder.isTypedArray(data)) { @@ -454,7 +545,9 @@ export class AskarWallet implements Wallet { algorithm: keyAlgFromString(key.keyType), publicKey: key.publicKey, }) - return askarKey.verifySignature({ message: data as Buffer, signature }) + const verified = askarKey.verifySignature({ message: data as Buffer, signature }) + askarKey.handle.free() + return verified } else { // Check if there is a signing key provider for the specified key type. if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { @@ -471,6 +564,7 @@ export class AskarWallet implements Wallet { throw new WalletError(`Unsupported keyType: ${key.keyType}`) } } catch (error) { + askarKey?.handle.free() if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) } @@ -493,79 +587,92 @@ export class AskarWallet implements Wallet { recipientKeys: string[], senderVerkey?: string // in base58 ): Promise { - const cek = AskarKey.generate(KeyAlgs.Chacha20C20P) + let cek: AskarKey | undefined + let senderKey: KeyEntryObject | null | undefined + let senderExchangeKey: AskarKey | undefined - const senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined + try { + cek = AskarKey.generate(KeyAlgs.Chacha20C20P) + senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined + senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined - const senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined + const recipients: JweRecipient[] = [] - const recipients: JweRecipient[] = [] + for (const recipientKey of recipientKeys) { + let targetExchangeKey: AskarKey | undefined + try { + targetExchangeKey = AskarKey.fromPublicBytes({ + publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, + algorithm: KeyAlgs.Ed25519, + }).convertkey({ algorithm: KeyAlgs.X25519 }) - for (const recipientKey of recipientKeys) { - const targetExchangeKey = AskarKey.fromPublicBytes({ - publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, - algorithm: KeyAlgs.Ed25519, - }).convertkey({ algorithm: KeyAlgs.X25519 }) + if (senderVerkey && senderExchangeKey) { + const encryptedSender = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: Buffer.from(senderVerkey), + }) + const nonce = CryptoBox.randomNonce() + const encryptedCek = CryptoBox.cryptoBox({ + recipientKey: targetExchangeKey, + senderKey: senderExchangeKey, + message: cek.secretBytes, + nonce, + }) - if (senderVerkey && senderExchangeKey) { - const encryptedSender = CryptoBox.seal({ - recipientKey: targetExchangeKey, - message: Buffer.from(senderVerkey), - }) - const nonce = CryptoBox.randomNonce() - const encryptedCek = CryptoBox.cryptoBox({ - recipientKey: targetExchangeKey, - senderKey: senderExchangeKey, - message: cek.secretBytes, - nonce, - }) + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + sender: TypedArrayEncoder.toBase64URL(encryptedSender), + iv: TypedArrayEncoder.toBase64URL(nonce), + }, + }) + ) + } else { + const encryptedCek = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: cek.secretBytes, + }) + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + }, + }) + ) + } + } finally { + targetExchangeKey?.handle.free() + } + } - recipients.push( - new JweRecipient({ - encryptedKey: encryptedCek, - header: { - kid: recipientKey, - sender: TypedArrayEncoder.toBase64URL(encryptedSender), - iv: TypedArrayEncoder.toBase64URL(nonce), - }, - }) - ) - } else { - const encryptedCek = CryptoBox.seal({ - recipientKey: targetExchangeKey, - message: cek.secretBytes, - }) - recipients.push( - new JweRecipient({ - encryptedKey: encryptedCek, - header: { - kid: recipientKey, - }, - }) - ) + const protectedJson = { + enc: 'xchacha20poly1305_ietf', + typ: 'JWM/1.0', + alg: senderVerkey ? 'Authcrypt' : 'Anoncrypt', + recipients: recipients.map((item) => JsonTransformer.toJSON(item)), } - } - const protectedJson = { - enc: 'xchacha20poly1305_ietf', - typ: 'JWM/1.0', - alg: senderVerkey ? 'Authcrypt' : 'Anoncrypt', - recipients: recipients.map((item) => JsonTransformer.toJSON(item)), + const { ciphertext, tag, nonce } = cek.aeadEncrypt({ + message: Buffer.from(JSON.stringify(payload)), + aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), + }).parts + + const envelope = new JweEnvelope({ + ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), + iv: TypedArrayEncoder.toBase64URL(nonce), + protected: JsonEncoder.toBase64URL(protectedJson), + tag: TypedArrayEncoder.toBase64URL(tag), + }).toJson() + + return envelope as EncryptedMessage + } finally { + cek?.handle.free() + senderKey?.key.handle.free() + senderExchangeKey?.handle.free() } - - const { ciphertext, tag, nonce } = cek.aeadEncrypt({ - message: Buffer.from(JSON.stringify(payload)), - aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), - }).parts - - const envelope = new JweEnvelope({ - ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), - iv: TypedArrayEncoder.toBase64URL(nonce), - protected: JsonEncoder.toBase64URL(protectedJson), - tag: TypedArrayEncoder.toBase64URL(tag), - }).toJson() - - return envelope as EncryptedMessage } /** @@ -607,41 +714,43 @@ export class AskarWallet implements Wallet { let payloadKey, senderKey, recipientKey for (const recipient of recipients) { - let recipientKeyEntry + let recipientKeyEntry: KeyEntryObject | null | undefined + let sender_x: AskarKey | undefined + let recip_x: AskarKey | undefined + try { recipientKeyEntry = await this.session.fetchKey({ name: recipient.kid }) - } catch (error) { - // TODO: Currently Askar wrapper throws error when key is not found - // In this case we don't need to throw any error because we should - // try with other recipient keys - continue - } - if (recipientKeyEntry) { - const recip_x = recipientKeyEntry.key.convertkey({ algorithm: KeyAlgs.X25519 }) - recipientKey = recipient.kid - - if (recipient.sender && recipient.iv) { - senderKey = TypedArrayEncoder.toUtf8String( - CryptoBox.sealOpen({ + if (recipientKeyEntry) { + const recip_x = recipientKeyEntry.key.convertkey({ algorithm: KeyAlgs.X25519 }) + recipientKey = recipient.kid + + if (recipient.sender && recipient.iv) { + senderKey = TypedArrayEncoder.toUtf8String( + CryptoBox.sealOpen({ + recipientKey: recip_x, + ciphertext: recipient.sender, + }) + ) + const sender_x = AskarKey.fromPublicBytes({ + algorithm: KeyAlgs.Ed25519, + publicKey: TypedArrayEncoder.fromBase58(senderKey), + }).convertkey({ algorithm: KeyAlgs.X25519 }) + + payloadKey = CryptoBox.open({ recipientKey: recip_x, - ciphertext: recipient.sender, + senderKey: sender_x, + message: recipient.encrypted_key, + nonce: recipient.iv, }) - ) - const sender_x = AskarKey.fromPublicBytes({ - algorithm: KeyAlgs.Ed25519, - publicKey: TypedArrayEncoder.fromBase58(senderKey), - }).convertkey({ algorithm: KeyAlgs.X25519 }) - - payloadKey = CryptoBox.open({ - recipientKey: recip_x, - senderKey: sender_x, - message: recipient.encrypted_key, - nonce: recipient.iv, - }) - } else { - payloadKey = CryptoBox.sealOpen({ ciphertext: recipient.encrypted_key, recipientKey: recip_x }) + } else { + payloadKey = CryptoBox.sealOpen({ ciphertext: recipient.encrypted_key, recipientKey: recip_x }) + } + break } - break + } finally { + recipientKeyEntry?.key.handle.free() + sender_x?.handle.free() + recip_x?.handle.free() } } if (!payloadKey) { @@ -652,17 +761,22 @@ export class AskarWallet implements Wallet { throw new WalletError('Sender public key not provided for Authcrypt') } - const cek = AskarKey.fromSecretBytes({ algorithm: KeyAlgs.Chacha20C20P, secretKey: payloadKey }) - const message = cek.aeadDecrypt({ - ciphertext: TypedArrayEncoder.fromBase64(messagePackage.ciphertext as any), - nonce: TypedArrayEncoder.fromBase64(messagePackage.iv as any), - tag: TypedArrayEncoder.fromBase64(messagePackage.tag as any), - aad: TypedArrayEncoder.fromString(messagePackage.protected), - }) - return { - plaintextMessage: JsonEncoder.fromBuffer(message), - senderKey, - recipientKey, + let cek: AskarKey | undefined + try { + cek = AskarKey.fromSecretBytes({ algorithm: KeyAlgs.Chacha20C20P, secretKey: payloadKey }) + const message = cek.aeadDecrypt({ + ciphertext: TypedArrayEncoder.fromBase64(messagePackage.ciphertext as any), + nonce: TypedArrayEncoder.fromBase64(messagePackage.iv as any), + tag: TypedArrayEncoder.fromBase64(messagePackage.tag as any), + aad: TypedArrayEncoder.fromString(messagePackage.protected), + }) + return { + plaintextMessage: JsonEncoder.fromBuffer(message), + senderKey, + recipientKey, + } + } finally { + cek?.handle.free() } } @@ -699,7 +813,9 @@ export class AskarWallet implements Wallet { uri, profile: walletConfig.id, // FIXME: Default derivation method should be set somewhere in either agent config or some constants - keyMethod: keyDerivationMethodToStoreKeyMethod(walletConfig.keyDerivationMethod) ?? StoreKeyMethod.None, + keyMethod: + keyDerivationMethodToStoreKeyMethod(walletConfig.keyDerivationMethod) ?? + (`${StoreKeyMethod.Kdf}:argon2i:int` as StoreKeyMethod), passKey: walletConfig.key, } } diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index ffbb648999..efeccd9fcd 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -267,7 +267,13 @@ describeRunInNodeVersion([18], 'AskarWallet management', () => { await askarWallet.close() const newKey = Store.generateRawKey() - await askarWallet.rotateKey({ ...walletConfig, id: 'AskarWallet Key Rotation', key: initialKey, rekey: newKey }) + await askarWallet.rotateKey({ + ...walletConfig, + id: 'AskarWallet Key Rotation', + key: initialKey, + rekey: newKey, + rekeyDerivationMethod: KeyDerivationMethod.Raw, + }) await askarWallet.close() diff --git a/packages/askar/tests/askar-sqlite.e2e.test.ts b/packages/askar/tests/askar-sqlite.e2e.test.ts new file mode 100644 index 0000000000..3de47f3183 --- /dev/null +++ b/packages/askar/tests/askar-sqlite.e2e.test.ts @@ -0,0 +1,187 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { + Agent, + BasicMessageRecord, + BasicMessageRepository, + BasicMessageRole, + KeyDerivationMethod, + TypedArrayEncoder, + utils, + WalletDuplicateError, + WalletInvalidKeyError, + WalletNotFoundError, +} from '@aries-framework/core' +import { Store } from '@hyperledger/aries-askar-shared' +import { tmpdir } from 'os' +import path from 'path' + +import { describeRunInNodeVersion } from '../../../tests/runInVersion' + +import { getSqliteAgentOptions } from './helpers' + +const aliceAgentOptions = getSqliteAgentOptions('AgentsAlice') +const bobAgentOptions = getSqliteAgentOptions('AgentsBob') + +// FIXME: Re-include in tests when Askar NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'Askar SQLite agents', () => { + let aliceAgent: Agent + let bobAgent: Agent + + beforeEach(async () => { + aliceAgent = new Agent(aliceAgentOptions) + bobAgent = new Agent(bobAgentOptions) + }) + + afterEach(async () => { + await aliceAgent.shutdown() + await bobAgent.shutdown() + + if (aliceAgent.wallet.isProvisioned) { + await aliceAgent.wallet.delete() + } + if (bobAgent.wallet.isProvisioned) { + await bobAgent.wallet.delete() + } + }) + + test('open, create and open wallet with different wallet key that it is in agent config', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey-0', + } + + try { + await aliceAgent.wallet.open(walletConfig) + } catch (error) { + if (error instanceof WalletNotFoundError) { + await aliceAgent.wallet.create(walletConfig) + await aliceAgent.wallet.open(walletConfig) + } + } + + await aliceAgent.initialize() + + expect(aliceAgent.isInitialized).toBe(true) + }) + + test('when opening non-existing wallet throw WalletNotFoundError', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey-1', + } + + await expect(aliceAgent.wallet.open(walletConfig)).rejects.toThrowError(WalletNotFoundError) + }) + + test('when create wallet and shutdown, wallet is closed', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey-2', + } + + await aliceAgent.wallet.create(walletConfig) + + await aliceAgent.shutdown() + + await expect(aliceAgent.wallet.open(walletConfig)).resolves.toBeUndefined() + }) + + test('create wallet with custom key derivation method', async () => { + const walletConfig = { + id: 'mywallet', + key: Store.generateRawKey(TypedArrayEncoder.fromString('mysecretwalletkey')), + keyDerivationMethod: KeyDerivationMethod.Raw, + } + + await aliceAgent.wallet.createAndOpen(walletConfig) + + expect(aliceAgent.wallet.isInitialized).toBe(true) + }) + + test('when exporting and importing a wallet, content is copied', async () => { + await bobAgent.initialize() + const bobBasicMessageRepository = bobAgent.dependencyManager.resolve(BasicMessageRepository) + + const basicMessageRecord = new BasicMessageRecord({ + id: 'some-id', + connectionId: 'connId', + content: 'hello', + role: BasicMessageRole.Receiver, + sentTime: 'sentIt', + }) + + // Save in wallet + await bobBasicMessageRepository.save(bobAgent.context, basicMessageRecord) + + if (!bobAgent.config.walletConfig) { + throw new Error('No wallet config on bobAgent') + } + + const backupKey = 'someBackupKey' + const backupWalletName = `backup-${utils.uuid()}` + const backupPath = path.join(tmpdir(), backupWalletName) + + // Create backup and delete wallet + await bobAgent.wallet.export({ path: backupPath, key: backupKey }) + await bobAgent.wallet.delete() + + // Initialize the wallet again and assert record does not exist + // This should create a new wallet + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await bobAgent.wallet.initialize(bobAgentOptions.config.walletConfig!) + expect(await bobBasicMessageRepository.findById(bobAgent.context, basicMessageRecord.id)).toBeNull() + await bobAgent.wallet.delete() + + // Import backup with different wallet id and initialize + await bobAgent.wallet.import({ id: backupWalletName, key: backupWalletName }, { path: backupPath, key: backupKey }) + await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) + + // Expect same basic message record to exist in new wallet + expect(await bobBasicMessageRepository.getById(bobAgent.context, basicMessageRecord.id)).toMatchObject( + basicMessageRecord + ) + }) + + test('changing wallet key', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey', + } + + await aliceAgent.wallet.createAndOpen(walletConfig) + await aliceAgent.initialize() + + //Close agent + const walletConfigRekey = { + id: 'mywallet', + key: 'mysecretwalletkey', + rekey: '123', + } + + await aliceAgent.shutdown() + await aliceAgent.wallet.rotateKey(walletConfigRekey) + await aliceAgent.initialize() + + expect(aliceAgent.isInitialized).toBe(true) + }) + + test('when creating already existing wallet throw WalletDuplicateError', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey-2', + } + + await aliceAgent.wallet.create(walletConfig) + await expect(aliceAgent.wallet.create(walletConfig)).rejects.toThrowError(WalletDuplicateError) + }) + + test('when opening wallet with invalid key throw WalletInvalidKeyError', async () => { + const walletConfig = { + id: 'mywallet', + key: 'mysecretwalletkey-3', + } + + await aliceAgent.wallet.create(walletConfig) + await expect(aliceAgent.wallet.open({ ...walletConfig, key: 'abcd' })).rejects.toThrowError(WalletInvalidKeyError) + }) +}) diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index 8be4b2a833..9321aca39d 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -20,7 +20,7 @@ export function getPostgresAgentOptions( extraConfig: Partial = {} ) { const config: InitConfig = { - label: `Agent: ${name}`, + label: `Agent: ${name} Postgres`, walletConfig: { id: `Wallet${name}`, key: `Key${name}`, @@ -37,3 +37,23 @@ export function getPostgresAgentOptions( modules: { askar: new AskarModule() }, } as const } + +export function getSqliteAgentOptions(name: string, extraConfig: Partial = {}) { + const config: InitConfig = { + label: `Agent: ${name} SQLite`, + walletConfig: { + id: `Wallet${name}`, + key: `Key${name}`, + storage: { type: 'sqlite' }, + }, + autoAcceptConnections: true, + autoUpdateStorageOnStartup: false, + logger: new TestLogger(LogLevel.off, name), + ...extraConfig, + } + return { + config, + dependencies: agentDependencies, + modules: { askar: new AskarModule() }, + } as const +} diff --git a/packages/core/src/storage/FileSystem.ts b/packages/core/src/storage/FileSystem.ts index a6eeb08e48..9a5710835e 100644 --- a/packages/core/src/storage/FileSystem.ts +++ b/packages/core/src/storage/FileSystem.ts @@ -11,6 +11,7 @@ export interface FileSystem { exists(path: string): Promise createDirectory(path: string): Promise + copyFile(sourcePath: string, destinationPath: string): Promise write(path: string, data: string): Promise read(path: string): Promise delete(path: string): Promise diff --git a/packages/node/src/NodeFileSystem.ts b/packages/node/src/NodeFileSystem.ts index 75ebb01b73..33af5391d6 100644 --- a/packages/node/src/NodeFileSystem.ts +++ b/packages/node/src/NodeFileSystem.ts @@ -8,7 +8,7 @@ import https from 'https' import { tmpdir, homedir } from 'os' import { dirname } from 'path' -const { access, readFile, writeFile, mkdir, rm, unlink } = promises +const { access, readFile, writeFile, mkdir, rm, unlink, copyFile } = promises export class NodeFileSystem implements FileSystem { public readonly dataPath @@ -44,6 +44,10 @@ export class NodeFileSystem implements FileSystem { await mkdir(dirname(path), { recursive: true }) } + public async copyFile(sourcePath: string, destinationPath: string): Promise { + await copyFile(sourcePath, destinationPath) + } + public async write(path: string, data: string): Promise { // Make sure parent directories exist await mkdir(dirname(path), { recursive: true }) diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index 48588fad88..a14ba4bd40 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -43,6 +43,10 @@ export class ReactNativeFileSystem implements FileSystem { await RNFS.mkdir(getDirFromFilePath(path)) } + public async copyFile(sourcePath: string, destinationPath: string): Promise { + await RNFS.copyFile(sourcePath, destinationPath) + } + public async write(path: string, data: string): Promise { // Make sure parent directories exist await RNFS.mkdir(getDirFromFilePath(path)) From 582c711728db12b7d38a0be2e9fa78dbf31b34c6 Mon Sep 17 00:00:00 2001 From: Mo <10432473+morrieinmaas@users.noreply.github.com> Date: Sat, 11 Mar 2023 12:08:02 +0100 Subject: [PATCH 083/139] feat: outbound message send via session (#1335) Co-authored-by: Jim Ezesinachi Co-authored-by: Timo Glastra Signed-off-by: Moriarty --- .../action-menu/tests/action-menu.e2e.test.ts | 4 +- .../v1-connectionless-proofs.e2e.test.ts | 114 ++- .../v1/handlers/V1PresentationHandler.ts | 1 + .../handlers/V1RequestPresentationHandler.ts | 1 + packages/core/src/agent/Agent.ts | 8 + packages/core/src/agent/Dispatcher.ts | 7 +- packages/core/src/agent/MessageReceiver.ts | 14 +- packages/core/src/agent/MessageSender.ts | 64 +- .../agent/models/OutboundMessageContext.ts | 4 + .../oob/__tests__/connect-to-self.e2e.test.ts | 3 +- .../v2-indy-connectionless-proofs.e2e.test.ts | 101 +++ ...=> v2-indy-proofs-auto-accept.e2e.test.ts} | 0 .../v2/handlers/V2PresentationHandler.ts | 1 + .../handlers/V2RequestPresentationHandler.ts | 1 + packages/core/tests/oob.test.ts | 5 +- .../tests/openid4vc-client.e2e.test.ts | 4 +- .../tests/question-answer.e2e.test.ts | 4 +- .../e2e-askar-indy-sdk-wallet-subject.test.ts | 4 +- tests/e2e-subject.test.ts | 5 +- tests/e2e-ws-pickup-v2.test.ts | 3 +- yarn.lock | 720 +++++++++--------- 21 files changed, 665 insertions(+), 403 deletions(-) rename packages/core/src/modules/proofs/protocol/v2/__tests__/{v2-indy-proofs-auto-accept.2e.test.ts => v2-indy-proofs-auto-accept.e2e.test.ts} (100%) diff --git a/packages/action-menu/tests/action-menu.e2e.test.ts b/packages/action-menu/tests/action-menu.e2e.test.ts index 8ba99acdbc..a32b13df49 100644 --- a/packages/action-menu/tests/action-menu.e2e.test.ts +++ b/packages/action-menu/tests/action-menu.e2e.test.ts @@ -5,6 +5,8 @@ import { Agent } from '@aries-framework/core' import { getAgentOptions, makeConnection, testLogger, setupSubjectTransports, indySdk } from '../../core/tests' import { IndySdkModule } from '../../indy-sdk/src' +import { waitForActionMenuRecord } from './helpers' + import { ActionMenu, ActionMenuModule, @@ -13,8 +15,6 @@ import { ActionMenuState, } from '@aries-framework/action-menu' -import { waitForActionMenuRecord } from './helpers' - const modules = { actionMenu: new ActionMenuModule(), indySdk: new IndySdkModule({ diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index 046f454c7c..ea2049d00d 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -237,10 +237,106 @@ describe('V1 Proofs - Connectionless - Indy', () => { await waitForProofExchangeRecordSubject(aliceReplay, { state: ProofState.Done, + threadId: message.threadId, }) await waitForProofExchangeRecordSubject(faberReplay, { state: ProofState.Done, + threadId: message.threadId, + }) + }) + + test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and without an outbound transport', async () => { + const { + holderAgent: aliceAgent, + issuerAgent: faberAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerReplay: faberReplay, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber v1 connection-less Proofs - Always', + holderName: 'Alice v1 connection-less Proofs - Always', + autoAcceptProofs: AutoAcceptProof.Always, + attributeNames: ['name', 'age'], + }) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + holderAgent: aliceAgent, + issuerReplay: faberReplay, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'John', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) + + agents = [aliceAgent, faberAgent] + + const { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v1', + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + autoAcceptProof: AutoAcceptProof.ContentApproved, + }) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofExchangeRecord.id, + message, + domain: 'https://a-domain.com', + }) + + for (const transport of faberAgent.outboundTransports) { + await faberAgent.unregisterOutboundTransport(transport) + } + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + threadId: requestMessage.threadId, + }) + + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + threadId: requestMessage.threadId, }) }) @@ -364,14 +460,6 @@ describe('V1 Proofs - Connectionless - Indy', () => { }, }) - const aliceProofExchangeRecordPromise = waitForProofExchangeRecordSubject(aliceReplay, { - state: ProofState.Done, - }) - - const faberProofExchangeRecordPromise = waitForProofExchangeRecordSubject(faberReplay, { - state: ProofState.Done, - }) - // eslint-disable-next-line prefer-const let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ protocolVersion: 'v1', @@ -427,9 +515,15 @@ describe('V1 Proofs - Connectionless - Indy', () => { await aliceAgent.receiveMessage(requestMessage.toJSON()) - await aliceProofExchangeRecordPromise + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + threadId: requestMessage.threadId, + }) - await faberProofExchangeRecordPromise + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + threadId: requestMessage.threadId, + }) await aliceAgent.mediationRecipient.stopMessagePickup() await faberAgent.mediationRecipient.stopMessagePickup() diff --git a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts index 20732e3ecf..41bcd9b4ae 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1PresentationHandler.ts @@ -68,6 +68,7 @@ export class V1PresentationHandler implements MessageHandler { serviceParams: { service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, }, }) } diff --git a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts index f0309ddee3..a697f0da90 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/handlers/V1RequestPresentationHandler.ts @@ -77,6 +77,7 @@ export class V1RequestPresentationHandler implements MessageHandler { serviceParams: { service: recipientService.resolvedDidCommService, senderKey: message.service.resolvedDidCommService.recipientKeys[0], + returnRoute: true, }, }) } diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 88ff3597fb..57649e1740 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -136,6 +136,10 @@ export class Agent extends BaseAge this.messageReceiver.registerInboundTransport(inboundTransport) } + public async unregisterInboundTransport(inboundTransport: InboundTransport) { + await this.messageReceiver.unregisterInboundTransport(inboundTransport) + } + public get inboundTransports() { return this.messageReceiver.inboundTransports } @@ -144,6 +148,10 @@ export class Agent extends BaseAge this.messageSender.registerOutboundTransport(outboundTransport) } + public async unregisterOutboundTransport(outboundTransport: OutboundTransport) { + await this.messageSender.unregisterOutboundTransport(outboundTransport) + } + public get outboundTransports() { return this.messageSender.outboundTransports } diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 709ba04ea0..5301745040 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -68,6 +68,7 @@ class Dispatcher { outboundMessage = new OutboundMessageContext(problemReportMessage, { agentContext, connection: messageContext.connection, + inboundMessageContext: messageContext, }) } else { this.logger.error(`Error handling message with type ${message.type}`, { @@ -83,10 +84,14 @@ class Dispatcher { } if (outboundMessage) { + // set the inbound message context, if not already defined + if (!outboundMessage.inboundMessageContext) { + outboundMessage.inboundMessageContext = messageContext + } + if (outboundMessage.isOutboundServiceMessage()) { await this.messageSender.sendMessageToService(outboundMessage) } else { - outboundMessage.sessionId = messageContext.sessionId await this.messageSender.sendMessage(outboundMessage) } } diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 6dfd073c3f..2bfabd9342 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -34,7 +34,7 @@ export class MessageReceiver { private connectionService: ConnectionService private messageHandlerRegistry: MessageHandlerRegistry private agentContextProvider: AgentContextProvider - public readonly inboundTransports: InboundTransport[] = [] + private _inboundTransports: InboundTransport[] = [] public constructor( envelopeService: EnvelopeService, @@ -54,10 +54,20 @@ export class MessageReceiver { this.messageHandlerRegistry = messageHandlerRegistry this.agentContextProvider = agentContextProvider this.logger = logger + this._inboundTransports = [] + } + + public get inboundTransports() { + return this._inboundTransports } public registerInboundTransport(inboundTransport: InboundTransport) { - this.inboundTransports.push(inboundTransport) + this._inboundTransports.push(inboundTransport) + } + + public async unregisterInboundTransport(inboundTransport: InboundTransport) { + this._inboundTransports = this._inboundTransports.filter((transport) => transport !== inboundTransport) + await inboundTransport.stop() } /** diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index a481521e8c..f6eeb9b758 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -43,7 +43,7 @@ export class MessageSender { private didResolverService: DidResolverService private didCommDocumentService: DidCommDocumentService private eventEmitter: EventEmitter - public readonly outboundTransports: OutboundTransport[] = [] + private _outboundTransports: OutboundTransport[] = [] public constructor( envelopeService: EnvelopeService, @@ -61,11 +61,20 @@ export class MessageSender { this.didResolverService = didResolverService this.didCommDocumentService = didCommDocumentService this.eventEmitter = eventEmitter - this.outboundTransports = [] + this._outboundTransports = [] + } + + public get outboundTransports() { + return this._outboundTransports } public registerOutboundTransport(outboundTransport: OutboundTransport) { - this.outboundTransports.push(outboundTransport) + this._outboundTransports.push(outboundTransport) + } + + public async unregisterOutboundTransport(outboundTransport: OutboundTransport) { + this._outboundTransports = this.outboundTransports.filter((transport) => transport !== outboundTransport) + await outboundTransport.stop() } public async packMessage( @@ -185,7 +194,7 @@ export class MessageSender { transportPriority?: TransportPriorityOptions } ) { - const { agentContext, connection, outOfBand, sessionId, message } = outboundMessageContext + const { agentContext, connection, outOfBand, message } = outboundMessageContext const errors: Error[] = [] if (!connection) { @@ -201,17 +210,9 @@ export class MessageSender { connectionId: connection.id, }) - let session: TransportSession | undefined - - if (sessionId) { - session = this.transportService.findSessionById(sessionId) - } - if (!session) { - // Try to send to already open session - session = this.transportService.findSessionByConnectionId(connection.id) - } + const session = this.findSessionForOutboundContext(outboundMessageContext) - if (session?.inboundMessage?.hasReturnRouting(message.threadId)) { + if (session) { this.logger.debug(`Found session with return routing for message '${message.id}' (connection '${connection.id}'`) try { await this.sendMessageToSession(agentContext, session, message) @@ -343,6 +344,20 @@ export class MessageSender { } public async sendMessageToService(outboundMessageContext: OutboundMessageContext) { + const session = this.findSessionForOutboundContext(outboundMessageContext) + + if (session) { + this.logger.debug(`Found session with return routing for message '${outboundMessageContext.message.id}'`) + try { + await this.sendMessageToSession(outboundMessageContext.agentContext, session, outboundMessageContext.message) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.SentToSession) + return + } catch (error) { + this.logger.debug(`Sending an outbound message via session failed with error: ${error.message}.`, error) + } + } + + // If there is no session try sending to service instead try { await this.sendToService(outboundMessageContext) this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.SentToTransport) @@ -411,7 +426,7 @@ export class MessageSender { for (const transport of this.outboundTransports) { const protocolScheme = getProtocolScheme(service.serviceEndpoint) if (!protocolScheme) { - this.logger.warn('Service does not have valid protocolScheme.') + this.logger.warn('Service does not have a protocol scheme.') } else if (transport.supportedSchemes.includes(protocolScheme)) { await transport.sendMessage(outboundPackage) return @@ -422,6 +437,25 @@ export class MessageSender { }) } + private findSessionForOutboundContext(outboundContext: OutboundMessageContext) { + let session: TransportSession | undefined = undefined + + // Use session id from outbound context if present, or use the session from the inbound message context + const sessionId = outboundContext.sessionId ?? outboundContext.inboundMessageContext?.sessionId + + // Try to find session by id + if (sessionId) { + session = this.transportService.findSessionById(sessionId) + } + + // Try to find session by connection id + if (!session && outboundContext.connection?.id) { + session = this.transportService.findSessionByConnectionId(outboundContext.connection.id) + } + + return session && session.inboundMessage?.hasAnyReturnRoute() ? session : null + } + private async retrieveServicesByConnection( agentContext: AgentContext, connection: ConnectionRecord, diff --git a/packages/core/src/agent/models/OutboundMessageContext.ts b/packages/core/src/agent/models/OutboundMessageContext.ts index 465b339eb2..de0eca1705 100644 --- a/packages/core/src/agent/models/OutboundMessageContext.ts +++ b/packages/core/src/agent/models/OutboundMessageContext.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import type { InboundMessageContext } from './InboundMessageContext' import type { Key } from '../../crypto' import type { ConnectionRecord } from '../../modules/connections' import type { ResolvedDidCommService } from '../../modules/didcomm' @@ -17,6 +18,7 @@ export interface ServiceMessageParams { export interface OutboundMessageContextParams { agentContext: AgentContext + inboundMessageContext?: InboundMessageContext associatedRecord?: BaseRecord connection?: ConnectionRecord serviceParams?: ServiceMessageParams @@ -31,6 +33,7 @@ export class OutboundMessageContext { public outOfBand?: OutOfBandRecord public associatedRecord?: BaseRecord public sessionId?: string + public inboundMessageContext?: InboundMessageContext public readonly agentContext: AgentContext public constructor(message: T, context: OutboundMessageContextParams) { @@ -40,6 +43,7 @@ export class OutboundMessageContext { this.outOfBand = context.outOfBand this.serviceParams = context.serviceParams this.associatedRecord = context.associatedRecord + this.inboundMessageContext = context.inboundMessageContext this.agentContext = context.agentContext } diff --git a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts index 5807e13d70..59ea798c98 100644 --- a/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/connect-to-self.e2e.test.ts @@ -8,11 +8,10 @@ import { SubjectOutboundTransport } from '../../../../../../tests/transport/Subj import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentOptions } from '../../../../tests/helpers' import { HandshakeProtocol, DidExchangeState } from '../../connections' +import { OutOfBandState } from '../domain/OutOfBandState' import { Agent } from '@aries-framework/core' -import { OutOfBandState } from '../domain/OutOfBandState' - const faberAgentOptions = getAgentOptions( 'Faber Agent OOB Connect to Self', { diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index d272ee4fc5..7b6e8cad49 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -231,14 +231,17 @@ describe('V2 Connectionless Proofs - Indy', () => { message, domain: 'https://a-domain.com', }) + await aliceAgent.receiveMessage(requestMessage.toJSON()) await waitForProofExchangeRecordSubject(aliceReplay, { state: ProofState.Done, + threadId: requestMessage.threadId, }) await waitForProofExchangeRecordSubject(faberReplay, { state: ProofState.Done, + threadId: requestMessage.threadId, }) }) @@ -417,10 +420,108 @@ describe('V2 Connectionless Proofs - Indy', () => { await waitForProofExchangeRecordSubject(aliceReplay, { state: ProofState.Done, + threadId: requestMessage.threadId, + }) + + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Done, + threadId: requestMessage.threadId, + }) + }) + + test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and without an outbound transport', async () => { + testLogger.test('Faber sends presentation request to Alice') + + const { + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber connection-less Proofs v2 - Auto Accept', + holderName: 'Alice connection-less Proofs v2 - Auto Accept', + autoAcceptProofs: AutoAcceptProof.Always, + attributeNames: ['name', 'age'], + }) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) + + agents = [aliceAgent, faberAgent] + + // eslint-disable-next-line prefer-const + let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v2', + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: { + age: { + name: 'age', + p_type: '>=', + p_value: 50, + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + }, + }, + autoAcceptProof: AutoAcceptProof.ContentApproved, + }) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofExchangeRecord.id, + message, + domain: 'rxjs:faber', + }) + + for (const transport of faberAgent.outboundTransports) { + await faberAgent.unregisterOutboundTransport(transport) + } + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Done, + threadId: requestMessage.threadId, }) await waitForProofExchangeRecordSubject(faberReplay, { state: ProofState.Done, + threadId: requestMessage.threadId, }) }) }) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.e2e.test.ts similarity index 100% rename from packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.2e.test.ts rename to packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs-auto-accept.e2e.test.ts diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts index e3c68c1d84..73216d17c1 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts @@ -58,6 +58,7 @@ export class V2PresentationHandler implements MessageHandler { serviceParams: { service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, }, }) } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts index e43a60df7e..0eaad0f2d0 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2RequestPresentationHandler.ts @@ -71,6 +71,7 @@ export class V2RequestPresentationHandler implements MessageHandler { serviceParams: { service: recipientService.resolvedDidCommService, senderKey: message.service.resolvedDidCommService.recipientKeys[0], + returnRoute: true, }, }) } diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index e5f2b34550..0ee9e59a51 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -10,9 +10,6 @@ import { SubjectInboundTransport } from '../../../tests/transport/SubjectInbound import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' import { getLegacyAnonCredsModules, prepareForAnonCredsIssuance } from '../../anoncreds/tests/legacyAnonCredsSetup' import { Agent } from '../src/agent/Agent' - -import { AgentEventTypes, AriesFrameworkError, AutoAcceptCredential, CredentialState } from '@aries-framework/core' - import { Key } from '../src/crypto' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' import { OutOfBandDidCommService } from '../src/modules/oob/domain/OutOfBandDidCommService' @@ -26,6 +23,8 @@ import { JsonEncoder } from '../src/utils' import { TestMessage } from './TestMessage' import { getAgentOptions, waitForCredentialRecord } from './helpers' +import { AgentEventTypes, AriesFrameworkError, AutoAcceptCredential, CredentialState } from '@aries-framework/core' + const faberAgentOptions = getAgentOptions( 'Faber Agent OOB', { diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index 2afc542a49..2c20b6acce 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -8,10 +8,10 @@ import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/docume import { getAgentOptions, indySdk } from '../../core/tests' import { IndySdkModule } from '../../indy-sdk/src' -import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' - import { acquireAccessTokenResponse, credentialRequestResponse, getMetadataResponse } from './fixtures' +import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' + const modules = { openId4VcClient: new OpenId4VcClientModule(), w3cVc: new W3cVcModule({ diff --git a/packages/question-answer/tests/question-answer.e2e.test.ts b/packages/question-answer/tests/question-answer.e2e.test.ts index b15d71efe6..af3373de88 100644 --- a/packages/question-answer/tests/question-answer.e2e.test.ts +++ b/packages/question-answer/tests/question-answer.e2e.test.ts @@ -5,10 +5,10 @@ import { Agent } from '@aries-framework/core' import { indySdk, setupSubjectTransports, testLogger, getAgentOptions, makeConnection } from '../../core/tests' import { IndySdkModule } from '../../indy-sdk/src' -import { QuestionAnswerModule, QuestionAnswerRole, QuestionAnswerState } from '@aries-framework/question-answer' - import { waitForQuestionAnswerRecord } from './helpers' +import { QuestionAnswerModule, QuestionAnswerRole, QuestionAnswerState } from '@aries-framework/question-answer' + const modules = { questionAnswer: new QuestionAnswerModule(), indySdk: new IndySdkModule({ diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts index 5f1197a294..f7ce1e929f 100644 --- a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -9,13 +9,13 @@ import { } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { getAgentOptions } from '../packages/core/tests/helpers' -import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' - import { e2eTest } from './e2e-test' import { describeRunInNodeVersion } from './runInVersion' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' +import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' + const recipientAgentOptions = getAgentOptions( 'E2E Askar Subject Recipient', { diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 6ec26ef417..148b13f9c4 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -7,12 +7,11 @@ import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAno import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' - -import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' - import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' +import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' + const recipientAgentOptions = getAgentOptions( 'E2E Subject Recipient', { diff --git a/tests/e2e-ws-pickup-v2.test.ts b/tests/e2e-ws-pickup-v2.test.ts index 16ace3cd90..50fe8e0598 100644 --- a/tests/e2e-ws-pickup-v2.test.ts +++ b/tests/e2e-ws-pickup-v2.test.ts @@ -3,10 +3,9 @@ import type { AnonCredsTestsAgent } from '../packages/anoncreds/tests/legacyAnon import { getLegacyAnonCredsModules } from '../packages/anoncreds/tests/legacyAnonCredsSetup' import { getAgentOptions } from '../packages/core/tests/helpers' -import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' - import { e2eTest } from './e2e-test' +import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' const recipientOptions = getAgentOptions( diff --git a/yarn.lock b/yarn.lock index 1e95de9d5f..702840d99a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": +"@ampproject/remapping@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== @@ -30,38 +30,39 @@ "@babel/highlight" "^7.18.6" "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5": - version "7.20.10" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.10.tgz#9d92fa81b87542fff50e848ed585b4212c1d34ec" - integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298" + integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" - integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.0.tgz#1341aefdcc14ccc7553fcc688dd8986a2daffc13" + integrity sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA== dependencies: - "@ampproject/remapping" "^2.1.0" + "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" + "@babel/generator" "^7.21.0" "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helpers" "^7.20.7" - "@babel/parser" "^7.20.7" + "@babel/helper-module-transforms" "^7.21.0" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.0" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.12" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.20.7", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a" - integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw== +"@babel/generator@^7.21.0", "@babel/generator@^7.21.1", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": + version "7.21.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.1.tgz#951cc626057bc0af2c35cd23e9c64d384dea83dd" + integrity sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA== dependencies: - "@babel/types" "^7.20.7" + "@babel/types" "^7.21.0" "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" "@babel/helper-annotate-as-pure@^7.18.6": @@ -90,27 +91,27 @@ lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.7": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz#4349b928e79be05ed2d1643b20b99bb87c503819" - integrity sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9" + integrity sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-member-expression-to-functions" "^7.20.7" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.21.0" "@babel/helper-optimise-call-expression" "^7.18.6" "@babel/helper-replace-supers" "^7.20.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-create-regexp-features-plugin@^7.18.6": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz#5ea79b59962a09ec2acf20a963a01ab4d076ccca" - integrity sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz#53ff78472e5ce10a52664272a239787107603ebb" + integrity sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.2.1" + regexpu-core "^5.3.1" "@babel/helper-define-polyfill-provider@^0.3.3": version "0.3.3" @@ -136,13 +137,13 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" @@ -151,12 +152,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz#a6f26e919582275a93c3aa6594756d71b0bb7f05" - integrity sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw== +"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" + integrity sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q== dependencies: - "@babel/types" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/helper-module-imports@^7.18.6": version "7.18.6" @@ -165,10 +166,10 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.20.11": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" - integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== +"@babel/helper-module-transforms@^7.21.0", "@babel/helper-module-transforms@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" @@ -176,8 +177,8 @@ "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-validator-identifier" "^7.19.1" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.10" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -234,19 +235,19 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== +"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== -"@babel/helpers@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.7.tgz#04502ff0feecc9f20ecfaad120a18f011a8e6dce" - integrity sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA== +"@babel/helpers@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" + integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== dependencies: "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" @@ -257,10 +258,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b" - integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.0", "@babel/parser@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.2.tgz#dacafadfc6d7654c3051a66d6fe55b6cb2f2a0b3" + integrity sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ== "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": version "7.18.6" @@ -306,9 +307,9 @@ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz#49f2b372519ab31728cc14115bb0998b15bfda55" - integrity sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" @@ -448,21 +449,21 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.11.tgz#9f5a3424bd112a3f32fe0cf9364fbb155cff262a" - integrity sha512-tA4N427a7fjf1P0/2I4ScsHGc5jcHPbb30xMbaTke2gxDuWpUfXDuX1FEymJwKk4tuGUvGcejAR6HdZVqmmPyw== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" + integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-classes@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073" - integrity sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" + integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-compilation-targets" "^7.20.7" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" + "@babel/helper-function-name" "^7.21.0" "@babel/helper-optimise-call-expression" "^7.18.6" "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-replace-supers" "^7.20.7" @@ -493,19 +494,19 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.18.6": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz#e9e8606633287488216028719638cbbb2f2dde8f" - integrity sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz#6aeca0adcb81dc627c8986e770bfaa4d9812aff5" + integrity sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-flow" "^7.18.6" "@babel/plugin-transform-for-of@^7.0.0": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" - integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz#964108c9988de1a60b4be2354a7d7e245f36e86e" + integrity sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-function-name@^7.0.0": version "7.18.9" @@ -531,11 +532,11 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz#8cb23010869bf7669fd4b3098598b6b2be6dc607" - integrity sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw== + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" + integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== dependencies: - "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-module-transforms" "^7.21.2" "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-simple-access" "^7.20.2" @@ -576,11 +577,11 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-jsx-self@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz#3849401bab7ae8ffa1e3e5687c94a753fc75bda7" - integrity sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.21.0.tgz#ec98d4a9baafc5a1eb398da4cf94afbb40254a54" + integrity sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-react-jsx-source@^7.0.0": version "7.19.6" @@ -590,15 +591,15 @@ "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.7.tgz#025d85a1935fd7e19dfdcb1b1d4df34d4da484f7" - integrity sha512-Tfq7qqD+tRj3EoDhY00nn2uP2hsRxgYGi5mLQ5TimKav0a9Lrpd4deE+fcLXU8zFYRjlKPHZhpCvfEA6qnBxqQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz#656b42c2fdea0a6d8762075d58ef9d4e3c4ab8a2" + integrity sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-module-imports" "^7.18.6" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/plugin-transform-regenerator@^7.0.0": version "7.20.5" @@ -609,12 +610,12 @@ regenerator-transform "^0.15.1" "@babel/plugin-transform-runtime@^7.0.0": - version "7.19.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz#9d2a9dbf4e12644d6f46e5e75bfbf02b5d6e9194" - integrity sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz#2a884f29556d0a68cd3d152dcc9e6c71dfb6eee8" + integrity sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg== dependencies: "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" babel-plugin-polyfill-corejs2 "^0.3.3" babel-plugin-polyfill-corejs3 "^0.6.0" babel-plugin-polyfill-regenerator "^0.4.1" @@ -649,12 +650,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-typescript@^7.18.6", "@babel/plugin-transform-typescript@^7.5.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.7.tgz#673f49499cd810ae32a1ea5f3f8fab370987e055" - integrity sha512-m3wVKEvf6SoszD8pu4NZz3PvfKRCMgk6D6d0Qi9hNnlM5M6CFS92EgF4EiHVLKbU0r/r7ty1hg7NPZwE7WRbYw== +"@babel/plugin-transform-typescript@^7.21.0", "@babel/plugin-transform-typescript@^7.5.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz#f0956a153679e3b377ae5b7f0143427151e4c848" + integrity sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.20.7" + "@babel/helper-create-class-features-plugin" "^7.21.0" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-typescript" "^7.20.0" @@ -676,18 +677,18 @@ "@babel/plugin-transform-flow-strip-types" "^7.18.6" "@babel/preset-typescript@^7.1.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399" - integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz#bcbbca513e8213691fe5d4b23d9251e01f00ebff" + integrity sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-validator-option" "^7.18.6" - "@babel/plugin-transform-typescript" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-transform-typescript" "^7.21.0" "@babel/register@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.18.9.tgz#1888b24bc28d5cc41c412feb015e9ff6b96e439c" - integrity sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.21.0.tgz#c97bf56c2472e063774f31d344c592ebdcefa132" + integrity sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw== dependencies: clone-deep "^4.0.1" find-cache-dir "^2.0.0" @@ -695,14 +696,19 @@ pirates "^4.0.5" source-map-support "^0.5.16" +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + "@babel/runtime@^7.8.4": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd" - integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": +"@babel/template@^7.0.0", "@babel/template@^7.20.7", "@babel/template@^7.3.3": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== @@ -711,26 +717,26 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.7", "@babel/traverse@^7.7.2": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.12.tgz#7f0f787b3a67ca4475adef1f56cb94f6abd4a4b5" - integrity sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.7.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.2.tgz#ac7e1f27658750892e815e60ae90f382a46d8e75" + integrity sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" + "@babel/generator" "^7.21.1" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" + "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" + "@babel/parser" "^7.21.2" + "@babel/types" "^7.21.2" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.2.tgz#92246f6e00f91755893c2876ad653db70c8310d1" + integrity sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -859,11 +865,11 @@ integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== "@hyperledger/anoncreds-nodejs@^0.1.0-dev.6": - version "0.1.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.6.tgz#db90e9de4a05e1446132048c07f54503afee4272" - integrity sha512-0BKTEVf1ovkGZGEsMK1jj545jIX48nYQwjKakMmKnSFrb62mtS7VkZ+5kQWp8fGzcTjr3h6Lo/G7TaMIDgQ6Ww== + version "0.1.0-dev.8" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.8.tgz#e9574ce0f7fdc557decc11f4ea4a252333f4a63b" + integrity sha512-KLjTFcGNjby3Pa1CkMXQUZqFJjOAv2peRJ7TzXVcqqXxVHbLIY+B3H8kHNYpRaAPAaKBfHybmzEuWrfFRB4q4w== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.6" + "@hyperledger/anoncreds-shared" "0.1.0-dev.8" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -871,10 +877,10 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.6", "@hyperledger/anoncreds-shared@^0.1.0-dev.6": - version "0.1.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.6.tgz#2e6afb4641cc25daef4074a32990ec078d2fd94e" - integrity sha512-4YZ2kzhOOrGRL//n/Qe/A+yyGsbnHqojQW6vGEZQpZ9bf4ir+QaZLRHijgXFmffMA+SRONfdlnxhLJ6/b6ZaMg== +"@hyperledger/anoncreds-shared@0.1.0-dev.8", "@hyperledger/anoncreds-shared@^0.1.0-dev.6": + version "0.1.0-dev.8" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.8.tgz#991ee8079435f6865f6b1c39b87ff6dfaf67ad3e" + integrity sha512-yJBgFPcRX2JSYkOctdubL9YYji9rYo+A6qDBrk3ClSNYdH5TFfl02da6ZbNp+iyOyhmbKV3GO1MTD8/fGEyxkw== "@hyperledger/aries-askar-nodejs@^0.1.0-dev.4": version "0.1.0-dev.4" @@ -896,22 +902,22 @@ dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.10": - version "0.1.0-dev.10" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.10.tgz#f5fc988b3c11766475f7f084f814af8a4eca0caf" - integrity sha512-wETVax9HRidhRTRiRqwLTj/looNCvjZ3vdqMoqmPbmet5sM9eRkU9v4ipz4paqz4LiZipTx2fisLyYskKW9g6A== +"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.6": + version "0.1.0-dev.9" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.9.tgz#11552e7bcfb3eb9ebeceb9021be5aa775ab5a881" + integrity sha512-5tw1b7LbCInFn6KDL8TYIicHpndDwMOxMZkAjmOIUopK7CzHV7DM56UgCF3SUajxkPln1vreBqEuGAMiiTl70Q== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.10" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.9" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" ref-array-di "^1.2.2" ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.10", "@hyperledger/indy-vdr-shared@^0.1.0-dev.10": - version "0.1.0-dev.10" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.10.tgz#2a6d09a3a6ae3458ae9209ab7b6f09435e88b9c9" - integrity sha512-7fr9/Iliu0u94qE8u1g/RoLJOwMH0kFdclttwBHfTAKTQyfoXUPe1yIZ8ITRgLwnmFYPQpRa54lU+uv15c37fg== +"@hyperledger/indy-vdr-shared@0.1.0-dev.9", "@hyperledger/indy-vdr-shared@^0.1.0-dev.6": + version "0.1.0-dev.9" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.9.tgz#b257c1ca75690812598e4606386c618e652d3f8c" + integrity sha512-n7W0YYVRad2AFD13RtZJ+xduUyz2Th6erfVW+7w21K04whyhsoPj9VTda2PtFGzPPNbZzrzRyEwdYNzsvy2GXQ== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -1164,7 +1170,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== @@ -2286,7 +2292,7 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@sideway/formula@^3.0.0": +"@sideway/formula@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== @@ -2326,9 +2332,9 @@ uint8arrays "^3.1.1" "@sphereon/ssi-types@^0.8.1-next.123": - version "0.8.1-unstable.179" - resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.8.1-unstable.179.tgz#9583ea0e1011d03876a9108eb863dce83502ada3" - integrity sha512-Se8n7sh3UEO+LGfUcO946TaQaGJf7ozY5tRo9V3Ssax0Rg5MMSOdlf+YE0tgZ7X84WZOrFTdzUVxpN2tpoYRlQ== + version "0.8.1-unstable.242" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.8.1-unstable.242.tgz#cf958a3f6ae0cae5e2b5175616edd158170a65ac" + integrity sha512-/Dy7WuT78gWOChhe4YTbDhr9xly1rxajPnGyPaYmeKOleY8c3az2zU2pRcAiL5nqFN0DkVnT0ncYeeENr9NzOw== dependencies: jwt-decode "^3.1.2" @@ -2423,12 +2429,12 @@ integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.1.20" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.20.tgz#e168cdd612c92a2d335029ed62ac94c95b362359" - integrity sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ== + version "7.20.0" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" + integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" "@types/babel__generator" "*" "@types/babel__template" "*" "@types/babel__traverse" "*" @@ -2502,22 +2508,22 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== -"@types/express-serve-static-core@^4.17.31": - version "4.17.32" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz#93dda387f5516af616d8d3f05f2c4c79d81e1b82" - integrity sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA== +"@types/express-serve-static-core@^4.17.33": + version "4.17.33" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz#de35d30a9d637dc1450ad18dd583d75d5733d543" + integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" "@types/express@^4.17.13", "@types/express@^4.17.15": - version "4.17.15" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.15.tgz#9290e983ec8b054b65a5abccb610411953d417ff" - integrity sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ== + version "4.17.17" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" + integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== dependencies: "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.31" + "@types/express-serve-static-core" "^4.17.33" "@types/qs" "*" "@types/serve-static" "*" @@ -2550,11 +2556,12 @@ buffer "^6.0.0" "@types/inquirer@^8.1.3": - version "8.2.5" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.5.tgz#c508423bcc11126db278170ab07347783ac2300c" - integrity sha512-QXlzybid60YtAwfgG3cpykptRYUx2KomzNutMlWsQC64J/WG/gQSl+P4w7A21sGN0VIxRVava4rgnT7FQmFCdg== + version "8.2.6" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.6.tgz#abd41a5fb689c7f1acb12933d787d4262a02a0ab" + integrity sha512-3uT88kxg8lNzY8ay2ZjP44DKcRaTGztqeIvN2zHvhzIBH/uAPaL75aBtdNRKbA7xXoMbBt5kX0M00VKAnfOYlA== dependencies: "@types/through" "*" + rxjs "^7.2.0" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" @@ -2622,9 +2629,9 @@ form-data "^3.0.0" "@types/node@*", "@types/node@^16.11.7": - version "16.18.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.11.tgz#cbb15c12ca7c16c85a72b6bdc4d4b01151bb3cae" - integrity sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA== + version "16.18.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.13.tgz#c572f8837094c6e3b73918a68674c784f6877fc0" + integrity sha512-l0/3XZ153UTlNOnZK8xSNoJlQda9/WnYgiTdcKKPJSZjdjI9MU+A9oMXOesAWLSnqAaaJhj3qfQsU07Dr8OUwg== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2662,25 +2669,25 @@ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/react-native@^0.64.10": - version "0.64.29" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.29.tgz#521544f12f01192e38cdaa376817eceb0c4104db" - integrity sha512-nCa4rcAlilTWL7wEUwTnxo6HjxQvFjVeDPK9taglDvId06pw/eOUu2NozfpwY91o8K7UdZn8VUoDRaGt2i8LBA== + version "0.64.31" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.31.tgz#589fa25676818f47d43e2fe16deca8c926c67b4d" + integrity sha512-U54mD5zsVade6nJidMEr8Zo7JGJytH+mH3Be3EeYrfgNMW1bHYr6U1HODFUC0wpprMdlQYvFXOSrBaWR2eefOA== dependencies: "@types/react" "^17" "@types/react@^17": - version "17.0.52" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.52.tgz#10d8b907b5c563ac014a541f289ae8eaa9bf2e9b" - integrity sha512-vwk8QqVODi0VaZZpDXQCmEmiOuyjEFPY7Ttaw5vjM112LOq37yz1CDJGrRJwA1fYEq4Iitd5rnjd1yWAc/bT+A== + version "17.0.53" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.53.tgz#10d4d5999b8af3d6bc6a9369d7eb953da82442ab" + integrity sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" csstype "^3.0.2" "@types/ref-napi@*", "@types/ref-napi@^3.0.4": - version "3.0.6" - resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.6.tgz#56f95b10e7698dced16e05b2bd10b6a46cf90f20" - integrity sha512-yLbSiZkLQB9Bv6m46+c4Gdv5Xmw34ehdUagQCfc88FvqHLamaGpYInHbFQ3+sawFonAQ0GDysQIEdZmSOmMh3A== + version "3.0.7" + resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.7.tgz#20adc93a7a2f9f992dfb17409fd748e6f4bf403d" + integrity sha512-CzPwr36VkezSpaJGdQX/UrczMSDsDgsWQQFEfQkS799Ft7n/s183a53lsql7RwVq+Ik4yLEgI84pRnLC0XXRlA== dependencies: "@types/node" "*" @@ -2702,9 +2709,9 @@ integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== "@types/serve-static@*": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" - integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== + version "1.15.1" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.1.tgz#86b1753f0be4f9a1bee68d459fcda5be4ea52b5d" + integrity sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ== dependencies: "@types/mime" "*" "@types/node" "*" @@ -2727,9 +2734,9 @@ integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== "@types/validator@^13.1.3", "@types/validator@^13.7.10": - version "13.7.10" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.10.tgz#f9763dc0933f8324920afa9c0790308eedf55ca7" - integrity sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ== + version "13.7.12" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.12.tgz#a285379b432cc8d103b69d223cbb159a253cf2f7" + integrity sha512-YVtyAPqpefU+Mm/qqnOANW6IkqKpCSrarcyV269C8MA8Ux0dbkEuQwM/4CjL47kVEM2LgBef/ETfkH+c6+moFA== "@types/varint@^6.0.0": version "6.0.1" @@ -2765,14 +2772,15 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.1.tgz#deee67e399f2cb6b4608c935777110e509d8018c" - integrity sha512-9nY5K1Rp2ppmpb9s9S2aBiF3xo5uExCehMDmYmmFqqyxgenbHJ3qbarcLt4ITgaD6r/2ypdlcFRdcuVPnks+fQ== + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz#24b8b4a952f3c615fe070e3c461dd852b5056734" + integrity sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw== dependencies: - "@typescript-eslint/scope-manager" "5.48.1" - "@typescript-eslint/type-utils" "5.48.1" - "@typescript-eslint/utils" "5.48.1" + "@typescript-eslint/scope-manager" "5.53.0" + "@typescript-eslint/type-utils" "5.53.0" + "@typescript-eslint/utils" "5.53.0" debug "^4.3.4" + grapheme-splitter "^1.0.4" ignore "^5.2.0" natural-compare-lite "^1.4.0" regexpp "^3.2.0" @@ -2780,71 +2788,71 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.48.1.tgz#d0125792dab7e232035434ab8ef0658154db2f10" - integrity sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA== + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.53.0.tgz#a1f2b9ae73b83181098747e96683f1b249ecab52" + integrity sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ== dependencies: - "@typescript-eslint/scope-manager" "5.48.1" - "@typescript-eslint/types" "5.48.1" - "@typescript-eslint/typescript-estree" "5.48.1" + "@typescript-eslint/scope-manager" "5.53.0" + "@typescript-eslint/types" "5.53.0" + "@typescript-eslint/typescript-estree" "5.53.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz#39c71e4de639f5fe08b988005beaaf6d79f9d64d" - integrity sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ== +"@typescript-eslint/scope-manager@5.53.0": + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz#42b54f280e33c82939275a42649701024f3fafef" + integrity sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w== dependencies: - "@typescript-eslint/types" "5.48.1" - "@typescript-eslint/visitor-keys" "5.48.1" + "@typescript-eslint/types" "5.53.0" + "@typescript-eslint/visitor-keys" "5.53.0" -"@typescript-eslint/type-utils@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz#5d94ac0c269a81a91ad77c03407cea2caf481412" - integrity sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ== +"@typescript-eslint/type-utils@5.53.0": + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz#41665449935ba9b4e6a1ba6e2a3f4b2c31d6cf97" + integrity sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw== dependencies: - "@typescript-eslint/typescript-estree" "5.48.1" - "@typescript-eslint/utils" "5.48.1" + "@typescript-eslint/typescript-estree" "5.53.0" + "@typescript-eslint/utils" "5.53.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.48.1.tgz#efd1913a9aaf67caf8a6e6779fd53e14e8587e14" - integrity sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg== +"@typescript-eslint/types@5.53.0": + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.53.0.tgz#f79eca62b97e518ee124086a21a24f3be267026f" + integrity sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A== -"@typescript-eslint/typescript-estree@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz#9efa8ee2aa471c6ab62e649f6e64d8d121bc2056" - integrity sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA== +"@typescript-eslint/typescript-estree@5.53.0": + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz#bc651dc28cf18ab248ecd18a4c886c744aebd690" + integrity sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w== dependencies: - "@typescript-eslint/types" "5.48.1" - "@typescript-eslint/visitor-keys" "5.48.1" + "@typescript-eslint/types" "5.53.0" + "@typescript-eslint/visitor-keys" "5.53.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.48.1.tgz#20f2f4e88e9e2a0961cbebcb47a1f0f7da7ba7f9" - integrity sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA== +"@typescript-eslint/utils@5.53.0": + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.53.0.tgz#e55eaad9d6fffa120575ffaa530c7e802f13bce8" + integrity sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g== dependencies: "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.48.1" - "@typescript-eslint/types" "5.48.1" - "@typescript-eslint/typescript-estree" "5.48.1" + "@typescript-eslint/scope-manager" "5.53.0" + "@typescript-eslint/types" "5.53.0" + "@typescript-eslint/typescript-estree" "5.53.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz#79fd4fb9996023ef86849bf6f904f33eb6c8fccb" - integrity sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA== +"@typescript-eslint/visitor-keys@5.53.0": + version "5.53.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz#8a5126623937cdd909c30d8fa72f79fa56cc1a9f" + integrity sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w== dependencies: - "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/types" "5.53.0" eslint-visitor-keys "^3.3.0" "@unimodules/core@*": @@ -2929,9 +2937,9 @@ acorn@^7.1.1, acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4, acorn@^8.4.1: - version "8.8.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" - integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== add-stream@^1.0.0: version "1.0.0" @@ -3215,7 +3223,7 @@ array.prototype.flat@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.3.0: +array.prototype.flatmap@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== @@ -3640,15 +3648,15 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.21.3, browserslist@^4.21.4: - version "4.21.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" - integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== +browserslist@^4.21.3, browserslist@^4.21.5: + version "4.21.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== dependencies: - caniuse-lite "^1.0.30001400" - electron-to-chromium "^1.4.251" - node-releases "^2.0.6" - update-browserslist-db "^1.0.9" + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" bs-logger@0.x: version "0.2.6" @@ -3799,10 +3807,10 @@ camelcase@^6.0.0, camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001400: - version "1.0.30001445" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz#cf2d4eb93f2bcdf0310de9dd6d18be271bc0b447" - integrity sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg== +caniuse-lite@^1.0.30001449: + version "1.0.30001458" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz#871e35866b4654a7d25eccca86864f411825540c" + integrity sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w== canonicalize@^1.0.1: version "1.0.8" @@ -3872,9 +3880,9 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.1.tgz#708a6cdae38915d597afdf3b145f2f8e1ff55f3f" - integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w== + version "3.8.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== cjs-module-lexer@^1.0.0: version "1.2.2" @@ -4210,9 +4218,9 @@ content-disposition@0.5.4: safe-buffer "5.2.1" content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== conventional-changelog-angular@^5.0.12: version "5.0.13" @@ -4326,11 +4334,11 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.25.1: - version "3.27.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.27.1.tgz#b5695eb25c602d72b1d30cbfba3cb7e5e4cf0a67" - integrity sha512-Dg91JFeCDA17FKnneN7oCMz4BkQ4TcffkgHP4OWwp9yx3pi7ubqMDXXSacfNak1PQqjc95skyt+YBLHQJnkJwA== + version "3.29.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.0.tgz#1b8d9eb4191ab112022e7f6364b99b65ea52f528" + integrity sha512-ScMn3uZNAFhK2DGoEfErguoiAHhV2Ju+oJo/jK08p7B3f3UhocUrCCkTvnZaiS+edl5nlIoiBXKcwMc6elv4KQ== dependencies: - browserslist "^4.21.4" + browserslist "^4.21.5" core-util-is@1.0.2: version "1.0.2" @@ -4544,9 +4552,9 @@ deepmerge@^3.2.0: integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA== deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + version "4.3.0" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" + integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== defaults@^1.0.3: version "1.0.4" @@ -4556,9 +4564,9 @@ defaults@^1.0.3: clone "^1.0.2" define-properties@^1.1.3, define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== dependencies: has-property-descriptors "^1.0.0" object-keys "^1.1.1" @@ -4750,10 +4758,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.251: - version "1.4.284" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" - integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== +electron-to-chromium@^1.4.284: + version "1.4.311" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.311.tgz#953bc9a4767f5ce8ec125f9a1ad8e00e8f67e479" + integrity sha512-RoDlZufvrtr2Nx3Yx5MB8jX3aHIxm8nRWPJm3yVvyHmyKaRvn90RjzB6hNnt0AkhS3IInJdyRfQb4mWhPvUjVw== emittery@^0.8.1: version "0.8.1" @@ -4993,13 +5001,13 @@ eslint-module-utils@^2.7.4: debug "^3.2.7" eslint-plugin-import@^2.23.4: - version "2.27.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.4.tgz#319c2f6f6580e1678d674a258ee5e981c10cc25b" - integrity sha512-Z1jVt1EGKia1X9CnBCkpAOhWy8FgQ7OmJ/IblEkT82yrFU/xJaxwujaTzLWqigewwynRQ9mmHfX9MtAfhxm0sA== + version "2.27.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" + integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== dependencies: array-includes "^3.1.6" array.prototype.flat "^1.3.1" - array.prototype.flatmap "^1.3.0" + array.prototype.flatmap "^1.3.1" debug "^3.2.7" doctrine "^2.1.0" eslint-import-resolver-node "^0.3.7" @@ -5125,9 +5133,9 @@ esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + version "1.4.2" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.2.tgz#c6d3fee05dd665808e2ad870631f221f5617b1d1" + integrity sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng== dependencies: estraverse "^5.1.0" @@ -5246,9 +5254,9 @@ expo-modules-autolinking@^0.0.3: fs-extra "^9.1.0" expo-random@*: - version "13.0.0" - resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-13.0.0.tgz#fc9c1496ac9f7555563d86de0db25966739c028f" - integrity sha512-aGb0vtUmFFuW0TF1rdOgsz89zEVD/RXUPUnnZy5+i3jJeQ2PerJ4uo72/EuWqHpCBNto8/qT+aCzFinmQDeTAA== + version "13.1.1" + resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-13.1.1.tgz#15e781911d5db4fbcee75e26ac109bc2523fe00c" + integrity sha512-+KkhGp7xW45GvMRzlcSOzvDwzTgyXo6C84GaG4GI43rOdECBQ2lGUJ12st39OtfZm1lORNskpi66DjnuJ73g9w== dependencies: base64-js "^1.3.0" @@ -5553,9 +5561,9 @@ flatted@^3.1.0: integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.197.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.197.0.tgz#9a581ef7c0b1c3377b195cec0bbad794b88be67b" - integrity sha512-yhwkJPxH1JBg0aJunk/jVRy5p3UhVZBGkzL1hq/GK+GaBh6bKr2YKkv6gDuiufaw+i3pKWQgOLtD++1cvrgXLA== + version "0.200.1" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.200.1.tgz#99a94b35b7d1815716e3db56bb797440ed340716" + integrity sha512-N6gxgo0iQx0G2m3aJjg3RLxNLUG3EBYgBN/xDDPGQXSjvqNkTdEd2t1myE36Xi7GndZQWngDP7jf0GvxdL6pRg== flow-parser@^0.121.0: version "0.121.0" @@ -5744,10 +5752,10 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" - integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" + integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== dependencies: function-bind "^1.1.1" has "^1.0.3" @@ -5898,9 +5906,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.19.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" - integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== + version "13.20.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== dependencies: type-fest "^0.20.2" @@ -5947,6 +5955,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + handlebars@^4.7.6, handlebars@^4.7.7: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" @@ -6299,11 +6312,11 @@ inquirer@^7.3.3: through "^2.3.6" internal-slot@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" - integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== dependencies: - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" has "^1.0.3" side-channel "^1.0.4" @@ -7223,14 +7236,14 @@ jetifier@^1.6.2: integrity sha512-3Zi16h6L5tXDRQJTb221cnRoVG9/9OvreLdLU2/ZjRv/GILL+2Cemt0IKvkowwkDpvouAU1DQPOJ7qaiHeIdrw== joi@^17.2.1: - version "17.7.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3" - integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg== + version "17.8.3" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.8.3.tgz#d772fe27a87a5cda21aace5cf11eee8671ca7e6f" + integrity sha512-q5Fn6Tj/jR8PfrLrx4fpGH4v9qM6o+vDUfD4/3vxxyg34OmKcNqYZ1qn2mpLza96S8tL0p0rIw2gOZX+/cTg9w== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" "@sideway/address" "^4.1.3" - "@sideway/formula" "^3.0.0" + "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: @@ -7371,7 +7384,7 @@ json5@2.x, json5@^2.2.2: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -json5@^1.0.1: +json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== @@ -7542,9 +7555,9 @@ libnpmpublish@^4.0.0: ssri "^8.0.1" libphonenumber-js@^1.10.14, libphonenumber-js@^1.9.7: - version "1.10.18" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.18.tgz#657c419071c8a02c638c0e80d9ee1232f152f280" - integrity sha512-NS4ZEgNhwbcPz1gfSXCGFnQm0xEiyTSPRthIuWytDzOiEG9xnZ2FbLyfJC4tI2BMAAXpoWbNxHYH75pa3Dq9og== + version "1.10.21" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.21.tgz#860312cbec1389e36e28389161025c7817a3ae32" + integrity sha512-/udZhx49av2r2gZR/+xXSrwcR8smX/sDNrVpOFrvW+CA26TfYTVZfwb3MIDvmwAYMLs7pXuJjZX0VxxGpqPhsA== lines-and-columns@^1.1.6: version "1.2.4" @@ -8190,9 +8203,9 @@ minimist-options@4.1.0: kind-of "^6.0.3" minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== minipass-collect@^1.0.2: version "1.0.2" @@ -8257,11 +8270,9 @@ minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: yallist "^4.0.0" minipass@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.0.0.tgz#7cebb0f9fa7d56f0c5b17853cbe28838a8dbbd3b" - integrity sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw== - dependencies: - yallist "^4.0.0" + version "4.2.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.4.tgz#7d0d97434b6a19f59c5c3221698b48bbf3b2cd06" + integrity sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ== minizlib@^1.3.3: version "1.3.3" @@ -8504,9 +8515,9 @@ node-fetch@3.0.0-beta.9: fetch-blob "^2.1.1" node-fetch@^2.0, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: - version "2.6.8" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.8.tgz#a68d30b162bc1d8fd71a367e81b997e1f4d4937e" - integrity sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg== + version "2.6.9" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" + integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== dependencies: whatwg-url "^5.0.0" @@ -8585,10 +8596,10 @@ node-pre-gyp@0.17.0: semver "^5.7.1" tar "^4.4.13" -node-releases@^2.0.6: - version "2.0.8" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" - integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A== +node-releases@^2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" + integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== node-stream-zip@^1.9.1: version "1.15.0" @@ -9311,9 +9322,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.8.3" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.3.tgz#ab697b1d3dd46fb4626fbe2f543afe0cc98d8632" - integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== + version "2.8.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" + integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" @@ -9430,9 +9441,9 @@ pump@^3.0.0: once "^1.3.1" punycode@^2.1.0, punycode@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.2.0.tgz#2092cc57cd2582c38e4e7e8bb869dc8d3148bc74" - integrity sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw== + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== pvtsutils@^1.3.2: version "1.3.2" @@ -9524,9 +9535,9 @@ rc@^1.2.8: strip-json-comments "~2.0.1" react-devtools-core@^4.6.0: - version "4.27.1" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.1.tgz#167aa174383c65786cbb7e965a5b39c702f0a2d3" - integrity sha512-qXhcxxDWiFmFAOq48jts9YQYe1+wVoUXzJTlY4jbaATzyio6dd6CUGu3dXBhREeVgpZ+y4kg6vFJzIOZh6vY2w== + version "4.27.2" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.2.tgz#d20fc57e258c656eedabafc2c851d38b33583148" + integrity sha512-8SzmIkpO87alD7Xr6gWIEa1jHkMjawOZ+6egjazlnjB4UUcbnzGDf/vBJ4BzGuWWEM+pzrxuzsPpcMqlQkYK2g== dependencies: shell-quote "^1.6.1" ws "^7" @@ -9719,18 +9730,18 @@ read@1, read@~1.0.1: mute-stream "~0.0.4" readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + version "3.6.1" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.1.tgz#f9f9b5f536920253b3d26e7660e7da4ccff9bb62" + integrity sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" util-deprecate "^1.0.1" readable-stream@^2.0.6, readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -9856,23 +9867,18 @@ regexpp@^3.1.0, regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regexpu-core@^5.2.1: - version "5.2.2" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.2.tgz#3e4e5d12103b64748711c3aad69934d7718e75fc" - integrity sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw== +regexpu-core@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.1.tgz#66900860f88def39a5cb79ebd9490e84f17bcdfb" + integrity sha512-nCOzW2V/X15XpLsK2rlgdwrysrBq+AauCn+omItIz4R1pIcmeot5zvjdmOBRLzEH/CkC6IxMJVmxDe3QcMuNVQ== dependencies: + "@babel/regjsgen" "^0.8.0" regenerate "^1.4.2" regenerate-unicode-properties "^10.1.0" - regjsgen "^0.7.1" regjsparser "^0.9.1" unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.1.0" -regjsgen@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.7.1.tgz#ee5ef30e18d3f09b7c369b76e7c2373ed25546f6" - integrity sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA== - regjsparser@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" @@ -10033,9 +10039,9 @@ rimraf@^3.0.0, rimraf@^3.0.2: glob "^7.1.3" rimraf@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.0.7.tgz#f438c7d6a2d5e5cca1d81e3904a48ac7b053a542" - integrity sha512-CUEDDrZvc0swDgVdXGiv3FcYYQMpJxjvSGt85Amj6yU+MCVWurrLCeLiJDdJPHCzNJnwuebBEdcO//eP11Xa7w== + version "4.1.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.1.2.tgz#20dfbc98083bdfaa28b01183162885ef213dbf7c" + integrity sha512-BlIbgFryTbw3Dz6hyoWFhKk+unCcHMSkZGrTFVAx2WmttdBSonsdtRlwiuTbDqTKr+UlXIUqJVS4QT5tUzGENQ== rimraf@~2.2.6: version "2.2.8" @@ -10266,9 +10272,9 @@ shell-quote@1.6.1: jsonify "~0.0.0" shell-quote@^1.6.1: - version "1.7.4" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.4.tgz#33fe15dee71ab2a81fcbd3a52106c5cfb9fb75d8" - integrity sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw== + version "1.8.0" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.0.tgz#20d078d0eaf71d54f43bd2ba14a1b5b9bfa5c8ba" + integrity sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ== shelljs@^0.8.4: version "0.8.5" @@ -11021,12 +11027,12 @@ ts-typed-json@^0.3.2: integrity sha512-Tdu3BWzaer7R5RvBIJcg9r8HrTZgpJmsX+1meXMJzYypbkj8NK2oJN0yvm4Dp/Iv6tzFa/L5jKRmEVTga6K3nA== tsconfig-paths@^3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== dependencies: "@types/json5" "^0.0.29" - json5 "^1.0.1" + json5 "^1.0.2" minimist "^1.2.6" strip-bom "^3.0.0" @@ -11045,9 +11051,9 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== tslog@^3.2.0: version "3.3.4" @@ -11176,9 +11182,9 @@ typedarray@^0.0.6: integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== typescript@~4.9.4: - version "4.9.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" - integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== typical@^4.0.0: version "4.0.0" @@ -11320,7 +11326,7 @@ upath@^2.0.1: resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== -update-browserslist-db@^1.0.9: +update-browserslist-db@^1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== @@ -11432,9 +11438,9 @@ validate-npm-package-name@^3.0.0: builtins "^1.0.3" validator@^13.5.2, validator@^13.7.0: - version "13.7.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" - integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== + version "13.9.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" + integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA== varint@^6.0.0: version "6.0.0" @@ -11497,9 +11503,9 @@ web-did-resolver@^2.0.8: did-resolver "^4.0.0" webcrypto-core@^1.7.4: - version "1.7.5" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.5.tgz#c02104c953ca7107557f9c165d194c6316587ca4" - integrity sha512-gaExY2/3EHQlRNNNVSrbG2Cg94Rutl7fAaKILS1w8ZDhGxdFOaw6EbCfHIxPy9vt/xwp5o0VQAx9aySPF6hU1A== + version "1.7.6" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.6.tgz#e32c4a12a13de4251f8f9ef336a6cba7cdec9b55" + integrity sha512-TBPiewB4Buw+HI3EQW+Bexm19/W4cP/qZG/02QJCXN+iN+T5sl074vZ3rJcle/ZtDBQSgjkbsQO/1eFcxnSBUA== dependencies: "@peculiar/asn1-schema" "^2.1.6" "@peculiar/json-schema" "^1.1.12" From 122cdde6982174a8e9cf70ef26a1393cb3912066 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 11 Mar 2023 13:31:09 +0100 Subject: [PATCH 084/139] fix: return HTTP 415 if unsupported content type (#1313) Signed-off-by: Timo Glastra --- .../src/transport/HttpInboundTransport.ts | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/node/src/transport/HttpInboundTransport.ts b/packages/node/src/transport/HttpInboundTransport.ts index 2bc1161954..891ad4145e 100644 --- a/packages/node/src/transport/HttpInboundTransport.ts +++ b/packages/node/src/transport/HttpInboundTransport.ts @@ -5,6 +5,8 @@ import type { Server } from 'http' import { DidCommMimeType, AriesFrameworkError, TransportService, utils, MessageReceiver } from '@aries-framework/core' import express, { text } from 'express' +const supportedContentTypes: string[] = [DidCommMimeType.V0, DidCommMimeType.V1] + export class HttpInboundTransport implements InboundTransport { public readonly app: Express private port: number @@ -22,12 +24,19 @@ export class HttpInboundTransport implements InboundTransport { this.app = app ?? express() this.path = path ?? '/' - this.app.use( - text({ - type: [DidCommMimeType.V0, DidCommMimeType.V1], - limit: '5mb', - }) - ) + this.app.use((req, res, next) => { + const contentType = req.headers['content-type'] + + if (!contentType || !supportedContentTypes.includes(contentType)) { + return res + .status(415) + .send('Unsupported content-type. Supported content-types are: ' + supportedContentTypes.join(', ')) + } + + return next() + }) + + this.app.use(text({ type: supportedContentTypes, limit: '5mb' })) } public async start(agent: Agent) { From dd6c02005135fb0260f589658643d68089233bab Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 11 Mar 2023 11:57:37 -0300 Subject: [PATCH 085/139] feat(anoncreds-rs): use new API methods for json conversion (#1373) Signed-off-by: Ariel Gentile --- packages/anoncreds-rs/package.json | 4 +- .../src/services/AnonCredsRsHolderService.ts | 170 ++++++++++-------- .../src/services/AnonCredsRsIssuerService.ts | 74 ++++---- .../services/AnonCredsRsVerifierService.ts | 34 ++-- .../AnonCredsRsHolderService.test.ts | 17 +- .../src/services/__tests__/helpers.ts | 103 +++++++---- yarn.lock | 36 ++-- 7 files changed, 242 insertions(+), 196 deletions(-) diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index 49622b9fb7..1539c8dd9d 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.6", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.9", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.6", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.9", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index f0516665ee..88d167d722 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -15,9 +15,16 @@ import type { AnonCredsCredential, AnonCredsRequestedAttributeMatch, AnonCredsRequestedPredicateMatch, + AnonCredsCredentialRequest, + AnonCredsCredentialRequestMetadata, } from '@aries-framework/anoncreds' import type { AgentContext, Query, SimpleQuery } from '@aries-framework/core' -import type { CredentialEntry, CredentialProve } from '@hyperledger/anoncreds-shared' +import type { + CredentialEntry, + CredentialProve, + CredentialRequestMetadata, + JsonObject, +} from '@hyperledger/anoncreds-shared' import { AnonCredsCredentialRecord, @@ -26,18 +33,14 @@ import { } from '@aries-framework/anoncreds' import { utils, injectable } from '@aries-framework/core' import { - CredentialRequestMetadata, + anoncreds, Credential, - CredentialDefinition, - CredentialOffer, CredentialRequest, CredentialRevocationState, MasterSecret, Presentation, - PresentationRequest, RevocationRegistryDefinition, RevocationStatusList, - Schema, } from '@hyperledger/anoncreds-shared' import { AnonCredsRsError } from '../errors/AnonCredsRsError' @@ -48,31 +51,35 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { agentContext: AgentContext, options?: CreateLinkSecretOptions ): Promise { + let masterSecret: MasterSecret | undefined try { + masterSecret = MasterSecret.create() + + // FIXME: This is a very specific format of anoncreds-rs. I think it should be simply a string + const linkSecretJson = masterSecret.toJson() as { value: { ms: string } } + return { linkSecretId: options?.linkSecretId ?? utils.uuid(), - linkSecretValue: JSON.parse(MasterSecret.create().toJson()).value.ms, + linkSecretValue: linkSecretJson.value.ms, } - } catch (error) { - agentContext.config.logger.error(`Error creating Link Secret`, { - error, - }) - throw new AnonCredsRsError('Error creating Link Secret', { cause: error }) + } finally { + masterSecret?.handle.clear() } } public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise { const { credentialDefinitions, proofRequest, selectedCredentials, schemas } = options + let presentation: Presentation | undefined try { - const rsCredentialDefinitions: Record = {} + const rsCredentialDefinitions: Record = {} for (const credDefId in credentialDefinitions) { - rsCredentialDefinitions[credDefId] = CredentialDefinition.load(JSON.stringify(credentialDefinitions[credDefId])) + rsCredentialDefinitions[credDefId] = credentialDefinitions[credDefId] as unknown as JsonObject } - const rsSchemas: Record = {} + const rsSchemas: Record = {} for (const schemaId in schemas) { - rsSchemas[schemaId] = Schema.load(JSON.stringify(schemas[schemaId])) + rsSchemas[schemaId] = schemas[schemaId] as unknown as JsonObject } const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) @@ -89,30 +96,30 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { retrievedCredentials.set(attribute.credentialId, credentialRecord) } - const credential = Credential.load(JSON.stringify(credentialRecord.credential)) - - const revocationRegistryDefinitionId = credential.revocationRegistryId - const revocationRegistryIndex = credential.revocationRegistryIndex + const revocationRegistryDefinitionId = credentialRecord.credential.rev_reg_id + const revocationRegistryIndex = credentialRecord.credentialRevocationId // TODO: Check if credential has a revocation registry id (check response from anoncreds-rs API, as it is // sending back a mandatory string in Credential.revocationRegistryId) const timestamp = attribute.timestamp - let revocationState - if (timestamp) { - if (revocationRegistryIndex) { + let revocationState: CredentialRevocationState | undefined + let revocationRegistryDefinition: RevocationRegistryDefinition | undefined + try { + if (timestamp && revocationRegistryIndex && revocationRegistryDefinitionId) { if (!options.revocationRegistries[revocationRegistryDefinitionId]) { throw new AnonCredsRsError(`Revocation Registry ${revocationRegistryDefinitionId} not found`) } const { definition, tailsFilePath } = options.revocationRegistries[revocationRegistryDefinitionId] - const revocationRegistryDefinition = RevocationRegistryDefinition.load(JSON.stringify(definition)) + revocationRegistryDefinition = RevocationRegistryDefinition.fromJson(definition as unknown as JsonObject) revocationState = CredentialRevocationState.create({ - revocationRegistryIndex, + revocationRegistryIndex: Number(revocationRegistryIndex), revocationRegistryDefinition, tailsPath: tailsFilePath, revocationStatusList: RevocationStatusList.create({ + issuerId: definition.issuerId, issuanceByDefault: true, revocationRegistryDefinition, revocationRegistryDefinitionId, @@ -120,14 +127,17 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { }), }) } - } - return { - linkSecretId: credentialRecord.linkSecretId, - credentialEntry: { - credential, - revocationState, - timestamp, - }, + return { + linkSecretId: credentialRecord.linkSecretId, + credentialEntry: { + credential: credentialRecord.credential as unknown as JsonObject, + revocationState: revocationState?.toJson(), + timestamp, + }, + } + } finally { + revocationState?.handle.clear() + revocationRegistryDefinition?.handle.clear() } } @@ -163,24 +173,19 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { throw new AnonCredsRsError('Link Secret value not stored') } - const presentation = Presentation.create({ + presentation = Presentation.create({ credentialDefinitions: rsCredentialDefinitions, schemas: rsSchemas, - presentationRequest: PresentationRequest.load(JSON.stringify(proofRequest)), + presentationRequest: proofRequest as unknown as JsonObject, credentials: credentials.map((entry) => entry.credentialEntry), credentialsProve, selfAttest: selectedCredentials.selfAttestedAttributes, - masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), + masterSecret: { value: { ms: linkSecretRecord.value } }, }) - return JSON.parse(presentation.toJson()) - } catch (error) { - agentContext.config.logger.error(`Error creating AnonCreds Proof`, { - error, - proofRequest, - selectedCredentials, - }) - throw new AnonCredsRsError(`Error creating proof: ${error}`, { cause: error }) + return presentation.toJson() as unknown as AnonCredsProof + } finally { + presentation?.handle.clear() } } @@ -189,6 +194,9 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { options: CreateCredentialRequestOptions ): Promise { const { credentialDefinition, credentialOffer } = options + let createReturnObj: + | { credentialRequest: CredentialRequest; credentialRequestMetadata: CredentialRequestMetadata } + | undefined try { const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) @@ -204,19 +212,22 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { ) } - const { credentialRequest, credentialRequestMetadata } = CredentialRequest.create({ - credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), - credentialOffer: CredentialOffer.load(JSON.stringify(credentialOffer)), - masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), + createReturnObj = CredentialRequest.create({ + entropy: anoncreds.generateNonce(), // FIXME: find a better source of entropy + credentialDefinition: credentialDefinition as unknown as JsonObject, + credentialOffer: credentialOffer as unknown as JsonObject, + masterSecret: { value: { ms: linkSecretRecord.value } }, masterSecretId: linkSecretRecord.linkSecretId, }) return { - credentialRequest: JSON.parse(credentialRequest.toJson()), - credentialRequestMetadata: JSON.parse(credentialRequestMetadata.toJson()), + credentialRequest: createReturnObj.credentialRequest.toJson() as unknown as AnonCredsCredentialRequest, + credentialRequestMetadata: + createReturnObj.credentialRequestMetadata.toJson() as unknown as AnonCredsCredentialRequestMetadata, } - } catch (error) { - throw new AnonCredsRsError(`Error creating credential request: ${error}`, { cause: error }) + } finally { + createReturnObj?.credentialRequest.handle.clear() + createReturnObj?.credentialRequestMetadata.handle.clear() } } @@ -227,35 +238,42 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { .resolve(AnonCredsLinkSecretRepository) .getByLinkSecretId(agentContext, credentialRequestMetadata.master_secret_name) - const revocationRegistryDefinition = revocationRegistry?.definition - ? RevocationRegistryDefinition.load(JSON.stringify(revocationRegistry.definition)) - : undefined + const revocationRegistryDefinition = revocationRegistry?.definition as unknown as JsonObject const credentialId = options.credentialId ?? utils.uuid() - const processedCredential = Credential.load(JSON.stringify(credential)).process({ - credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), - credentialRequestMetadata: CredentialRequestMetadata.load(JSON.stringify(credentialRequestMetadata)), - masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })), - revocationRegistryDefinition, - }) - - const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) - await credentialRepository.save( - agentContext, - new AnonCredsCredentialRecord({ - credential: JSON.parse(processedCredential.toJson()) as AnonCredsCredential, - credentialId, - linkSecretId: linkSecretRecord.linkSecretId, - issuerId: options.credentialDefinition.issuerId, - schemaName: schema.name, - schemaIssuerId: schema.issuerId, - schemaVersion: schema.version, - credentialRevocationId: processedCredential.revocationRegistryIndex?.toString(), + let credentialObj: Credential | undefined + let processedCredential: Credential | undefined + try { + credentialObj = Credential.fromJson(credential as unknown as JsonObject) + processedCredential = credentialObj.process({ + credentialDefinition: credentialDefinition as unknown as JsonObject, + credentialRequestMetadata: credentialRequestMetadata as unknown as JsonObject, + masterSecret: { value: { ms: linkSecretRecord.value } }, + revocationRegistryDefinition, }) - ) - return credentialId + const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) + + await credentialRepository.save( + agentContext, + new AnonCredsCredentialRecord({ + credential: processedCredential.toJson() as unknown as AnonCredsCredential, + credentialId, + linkSecretId: linkSecretRecord.linkSecretId, + issuerId: options.credentialDefinition.issuerId, + schemaName: schema.name, + schemaIssuerId: schema.issuerId, + schemaVersion: schema.version, + credentialRevocationId: processedCredential.revocationRegistryIndex?.toString(), + }) + ) + + return credentialId + } finally { + credentialObj?.handle.clear() + processedCredential?.handle.clear() + } } public async getCredential( diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts index 17b3c91d91..91c439d4b1 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts @@ -9,8 +9,10 @@ import type { AnonCredsSchema, AnonCredsCredentialDefinition, CreateCredentialDefinitionReturn, + AnonCredsCredential, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' +import type { CredentialDefinitionPrivate, JsonObject, KeyCorrectnessProof } from '@hyperledger/anoncreds-shared' import { AnonCredsKeyCorrectnessProofRepository, @@ -18,15 +20,7 @@ import { AnonCredsCredentialDefinitionRepository, } from '@aries-framework/anoncreds' import { injectable, AriesFrameworkError } from '@aries-framework/core' -import { - Credential, - CredentialDefinition, - CredentialDefinitionPrivate, - CredentialRequest, - CredentialOffer, - KeyCorrectnessProof, - Schema, -} from '@hyperledger/anoncreds-shared' +import { Credential, CredentialDefinition, CredentialOffer, Schema } from '@hyperledger/anoncreds-shared' import { AnonCredsRsError } from '../errors/AnonCredsRsError' @@ -35,6 +29,7 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise { const { issuerId, name, version, attrNames: attributeNames } = options + let schema: Schema | undefined try { const schema = Schema.create({ issuerId, @@ -43,9 +38,9 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { attributeNames, }) - return JSON.parse(schema.toJson()) as AnonCredsSchema - } catch (error) { - throw new AnonCredsRsError('Error creating schema', { cause: error }) + return schema.toJson() as unknown as AnonCredsSchema + } finally { + schema?.handle.clear() } } @@ -55,9 +50,16 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { ): Promise { const { tag, supportRevocation, schema, issuerId, schemaId } = options + let createReturnObj: + | { + credentialDefinition: CredentialDefinition + credentialDefinitionPrivate: CredentialDefinitionPrivate + keyCorrectnessProof: KeyCorrectnessProof + } + | undefined try { - const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = CredentialDefinition.create({ - schema: Schema.load(JSON.stringify(schema)), + createReturnObj = CredentialDefinition.create({ + schema: schema as unknown as JsonObject, issuerId, schemaId, tag, @@ -66,12 +68,14 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { }) return { - credentialDefinition: JSON.parse(credentialDefinition.toJson()) as AnonCredsCredentialDefinition, - credentialDefinitionPrivate: JSON.parse(credentialDefinitionPrivate.toJson()), - keyCorrectnessProof: JSON.parse(keyCorrectnessProof.toJson()), + credentialDefinition: createReturnObj.credentialDefinition.toJson() as unknown as AnonCredsCredentialDefinition, + credentialDefinitionPrivate: createReturnObj.credentialDefinitionPrivate.toJson(), + keyCorrectnessProof: createReturnObj.keyCorrectnessProof.toJson(), } - } catch (error) { - throw new AnonCredsRsError('Error creating credential definition', { cause: error }) + } finally { + createReturnObj?.credentialDefinition.handle.clear() + createReturnObj?.credentialDefinitionPrivate.handle.clear() + createReturnObj?.keyCorrectnessProof.handle.clear() } } @@ -81,6 +85,7 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { ): Promise { const { credentialDefinitionId } = options + let credentialOffer: CredentialOffer | undefined try { const credentialDefinitionRecord = await agentContext.dependencyManager .resolve(AnonCredsCredentialDefinitionRepository) @@ -94,15 +99,15 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { throw new AnonCredsRsError(`Credential Definition ${credentialDefinitionId} not found`) } - const credentialOffer = CredentialOffer.create({ + credentialOffer = CredentialOffer.create({ credentialDefinitionId, - keyCorrectnessProof: KeyCorrectnessProof.load(JSON.stringify(keyCorrectnessProofRecord?.value)), + keyCorrectnessProof: keyCorrectnessProofRecord?.value, schemaId: credentialDefinitionRecord.credentialDefinition.schemaId, }) - return JSON.parse(credentialOffer.toJson()) as AnonCredsCredentialOffer - } catch (error) { - throw new AnonCredsRsError(`Error creating credential offer: ${error}`, { cause: error }) + return credentialOffer.toJson() as unknown as AnonCredsCredentialOffer + } finally { + credentialOffer?.handle.clear() } } @@ -112,6 +117,7 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { ): Promise { const { tailsFilePath, credentialOffer, credentialRequest, credentialValues, revocationRegistryId } = options + let credential: Credential | undefined try { if (revocationRegistryId || tailsFilePath) { throw new AriesFrameworkError('Revocation not supported yet') @@ -133,26 +139,22 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { .resolve(AnonCredsCredentialDefinitionPrivateRepository) .getByCredentialDefinitionId(agentContext, options.credentialRequest.cred_def_id) - const credential = Credential.create({ - credentialDefinition: CredentialDefinition.load( - JSON.stringify(credentialDefinitionRecord.credentialDefinition) - ), - credentialOffer: CredentialOffer.load(JSON.stringify(credentialOffer)), - credentialRequest: CredentialRequest.load(JSON.stringify(credentialRequest)), + credential = Credential.create({ + credentialDefinition: credentialDefinitionRecord.credentialDefinition as unknown as JsonObject, + credentialOffer: credentialOffer as unknown as JsonObject, + credentialRequest: credentialRequest as unknown as JsonObject, revocationRegistryId, attributeEncodedValues, attributeRawValues, - credentialDefinitionPrivate: CredentialDefinitionPrivate.load( - JSON.stringify(credentialDefinitionPrivateRecord.value) - ), + credentialDefinitionPrivate: credentialDefinitionPrivateRecord.value, }) return { - credential: JSON.parse(credential.toJson()), + credential: credential.toJson() as unknown as AnonCredsCredential, credentialRevocationId: credential.revocationRegistryIndex?.toString(), } - } catch (error) { - throw new AnonCredsRsError(`Error creating credential: ${error}`, { cause: error }) + } finally { + credential?.handle.clear() } } } diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts index be4952d632..81edd11c56 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts @@ -1,34 +1,27 @@ import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' +import type { JsonObject } from '@hyperledger/anoncreds-shared' import { injectable } from '@aries-framework/core' -import { - CredentialDefinition, - Presentation, - PresentationRequest, - RevocationRegistryDefinition, - RevocationStatusList, - Schema, -} from '@hyperledger/anoncreds-shared' - -import { AnonCredsRsError } from '../errors/AnonCredsRsError' +import { Presentation, RevocationRegistryDefinition, RevocationStatusList } from '@hyperledger/anoncreds-shared' @injectable() export class AnonCredsRsVerifierService implements AnonCredsVerifierService { public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise { const { credentialDefinitions, proof, proofRequest, revocationRegistries, schemas } = options + let presentation: Presentation | undefined try { - const presentation = Presentation.load(JSON.stringify(proof)) + presentation = Presentation.fromJson(proof as unknown as JsonObject) - const rsCredentialDefinitions: Record = {} + const rsCredentialDefinitions: Record = {} for (const credDefId in credentialDefinitions) { - rsCredentialDefinitions[credDefId] = CredentialDefinition.load(JSON.stringify(credentialDefinitions[credDefId])) + rsCredentialDefinitions[credDefId] = credentialDefinitions[credDefId] as unknown as JsonObject } - const rsSchemas: Record = {} + const rsSchemas: Record = {} for (const schemaId in schemas) { - rsSchemas[schemaId] = Schema.load(JSON.stringify(schemas[schemaId])) + rsSchemas[schemaId] = schemas[schemaId] as unknown as JsonObject } const revocationRegistryDefinitions: Record = {} @@ -37,13 +30,14 @@ export class AnonCredsRsVerifierService implements AnonCredsVerifierService { for (const revocationRegistryDefinitionId in revocationRegistries) { const { definition, revocationStatusLists } = options.revocationRegistries[revocationRegistryDefinitionId] - revocationRegistryDefinitions[revocationRegistryDefinitionId] = RevocationRegistryDefinition.load( - JSON.stringify(definition) + revocationRegistryDefinitions[revocationRegistryDefinitionId] = RevocationRegistryDefinition.fromJson( + definition as unknown as JsonObject ) for (const timestamp in revocationStatusLists) { lists.push( RevocationStatusList.create({ + issuerId: definition.issuerId, issuanceByDefault: true, revocationRegistryDefinition: revocationRegistryDefinitions[revocationRegistryDefinitionId], revocationRegistryDefinitionId, @@ -54,14 +48,14 @@ export class AnonCredsRsVerifierService implements AnonCredsVerifierService { } return presentation.verify({ - presentationRequest: PresentationRequest.load(JSON.stringify(proofRequest)), + presentationRequest: proofRequest as unknown as JsonObject, credentialDefinitions: rsCredentialDefinitions, schemas: rsSchemas, revocationRegistryDefinitions, revocationStatusLists: lists, }) - } catch (error) { - throw new AnonCredsRsError('Error verifying proof', { cause: error }) + } finally { + presentation?.handle.clear() } } } diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index 23565f8e2b..3240dd5ff9 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -5,7 +5,10 @@ import type { AnonCredsCredential, AnonCredsSchema, AnonCredsSelectedCredentials, + AnonCredsRevocationRegistryDefinition, + AnonCredsCredentialRequestMetadata, } from '@aries-framework/anoncreds' +import type { JsonObject } from '@hyperledger/anoncreds-nodejs' import { AnonCredsHolderServiceSymbol, @@ -56,7 +59,7 @@ const agentContext = getAgentContext({ describe('AnonCredsRsHolderService', () => { const getByCredentialIdMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'getByCredentialId') - afterEach(() => { + beforeEach(() => { getByCredentialIdMock.mockClear() }) @@ -160,7 +163,7 @@ describe('AnonCredsRsHolderService', () => { height: '179', age: '19', }, - credentialDefinition: personCredentialDefinition, + credentialDefinition: personCredentialDefinition as unknown as JsonObject, schemaId: 'personschema:uri', credentialDefinitionId: 'personcreddef:uri', credentialDefinitionPrivate: personCredentialDefinitionPrivate, @@ -180,7 +183,7 @@ describe('AnonCredsRsHolderService', () => { attributes: { phoneNumber: 'linkSecretId56', }, - credentialDefinition: phoneCredentialDefinition, + credentialDefinition: phoneCredentialDefinition as unknown as JsonObject, schemaId: 'phoneschema:uri', credentialDefinitionId: 'phonecreddef:uri', credentialDefinitionPrivate: phoneCredentialDefinitionPrivate, @@ -454,7 +457,7 @@ describe('AnonCredsRsHolderService', () => { height: '179', age: '19', }, - credentialDefinition, + credentialDefinition: credentialDefinition as unknown as JsonObject, schemaId: 'personschema:uri', credentialDefinitionId: 'personcreddef:uri', credentialDefinitionPrivate, @@ -474,11 +477,13 @@ describe('AnonCredsRsHolderService', () => { credentialDefinition, schema, credentialDefinitionId: 'personcreddefid:uri', - credentialRequestMetadata: JSON.parse(credentialRequestMetadata.toJson()), + credentialRequestMetadata: credentialRequestMetadata.toJson() as unknown as AnonCredsCredentialRequestMetadata, credentialId: 'personCredId', revocationRegistry: { id: 'personrevregid:uri', - definition: JSON.parse(new RevocationRegistryDefinition(revocationRegistryDefinition.handle).toJson()), + definition: new RevocationRegistryDefinition( + revocationRegistryDefinition.handle + ).toJson() as unknown as AnonCredsRevocationRegistryDefinition, }, }) diff --git a/packages/anoncreds-rs/src/services/__tests__/helpers.ts b/packages/anoncreds-rs/src/services/__tests__/helpers.ts index 07d5b09f49..b71cfdcf6d 100644 --- a/packages/anoncreds-rs/src/services/__tests__/helpers.ts +++ b/packages/anoncreds-rs/src/services/__tests__/helpers.ts @@ -1,13 +1,22 @@ -import type { AnonCredsCredentialInfo } from '@aries-framework/anoncreds' +import type { + AnonCredsCredential, + AnonCredsCredentialDefinition, + AnonCredsCredentialInfo, + AnonCredsCredentialOffer, +} from '@aries-framework/anoncreds' +import type { JsonObject } from '@hyperledger/anoncreds-nodejs' import { anoncreds, + Credential, CredentialDefinition, - CredentialDefinitionPrivate, CredentialOffer, CredentialRequest, - KeyCorrectnessProof, + CredentialRevocationConfig, MasterSecret, + RevocationRegistryDefinition, + RevocationRegistryDefinitionPrivate, + RevocationStatusList, Schema, } from '@hyperledger/anoncreds-shared' @@ -34,24 +43,33 @@ export function createCredentialDefinition(options: { attributeNames: string[]; tag: 'TAG', }) - return { - credentialDefinition: JSON.parse(credentialDefinition.toJson()), - credentialDefinitionPrivate: JSON.parse(credentialDefinitionPrivate.toJson()), - keyCorrectnessProof: JSON.parse(keyCorrectnessProof.toJson()), - schema: JSON.parse(schema.toJson()), + const returnObj = { + credentialDefinition: credentialDefinition.toJson() as unknown as AnonCredsCredentialDefinition, + credentialDefinitionPrivate: credentialDefinitionPrivate.toJson() as unknown as JsonObject, + keyCorrectnessProof: keyCorrectnessProof.toJson() as unknown as JsonObject, + schema: schema.toJson() as unknown as Schema, } + + credentialDefinition.handle.clear() + credentialDefinitionPrivate.handle.clear() + keyCorrectnessProof.handle.clear() + schema.handle.clear() + + return returnObj } /** * Creates a valid credential offer and returns itsf */ -export function createCredentialOffer(kcp: Record) { +export function createCredentialOffer(keyCorrectnessProof: Record) { const credentialOffer = CredentialOffer.create({ credentialDefinitionId: 'creddef:uri', - keyCorrectnessProof: KeyCorrectnessProof.load(JSON.stringify(kcp)), + keyCorrectnessProof, schemaId: 'schema:uri', }) - return JSON.parse(credentialOffer.toJson()) + const credentialOfferJson = credentialOffer.toJson() as unknown as AnonCredsCredentialOffer + credentialOffer.handle.clear() + return credentialOfferJson } /** @@ -59,13 +77,16 @@ export function createCredentialOffer(kcp: Record) { * @returns Creates a valid link secret value for anoncreds-rs */ export function createLinkSecret() { - return JSON.parse(MasterSecret.create().toJson()).value.ms as string + const masterSecret = MasterSecret.create() + const ms = (masterSecret.toJson() as { value: { ms: string } }).value.ms as string + masterSecret.handle.clear() + return ms } export function createCredentialForHolder(options: { - credentialDefinition: Record - credentialDefinitionPrivate: Record - keyCorrectnessProof: Record + credentialDefinition: JsonObject + credentialDefinitionPrivate: JsonObject + keyCorrectnessProof: JsonObject schemaId: string credentialDefinitionId: string attributes: Record @@ -89,19 +110,18 @@ export function createCredentialForHolder(options: { const credentialOffer = CredentialOffer.create({ credentialDefinitionId, - keyCorrectnessProof: KeyCorrectnessProof.load(JSON.stringify(keyCorrectnessProof)), + keyCorrectnessProof, schemaId, }) const { credentialRequest, credentialRequestMetadata } = CredentialRequest.create({ - credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)), + entropy: 'some-entropy', + credentialDefinition, credentialOffer, - masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecret } })), + masterSecret: { value: { ms: linkSecret } }, masterSecretId: linkSecretId, }) - // FIXME: Revocation config should not be mandatory but current anoncreds-rs is requiring it - const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate, tailsPath } = createRevocationRegistryDefinition({ credentialDefinitionId, @@ -109,30 +129,29 @@ export function createCredentialForHolder(options: { }) const timeCreateRevStatusList = 12 - const revocationStatusList = anoncreds.createRevocationStatusList({ + const revocationStatusList = RevocationStatusList.create({ + issuerId: credentialDefinition.issuerId as string, timestamp: timeCreateRevStatusList, issuanceByDefault: true, - revocationRegistryDefinition, - revocationRegistryDefinitionId: revocationRegistryDefinitionId, + revocationRegistryDefinition: new RevocationRegistryDefinition(revocationRegistryDefinition.handle), + revocationRegistryDefinitionId: 'mock:uri', }) - // TODO: Use Credential.create (needs to update the paramters in anoncreds-rs) - const credentialObj = anoncreds.createCredential({ - credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)).handle, - credentialDefinitionPrivate: CredentialDefinitionPrivate.load(JSON.stringify(credentialDefinitionPrivate)).handle, - credentialOffer: credentialOffer.handle, - credentialRequest: credentialRequest.handle, + const credentialObj = Credential.create({ + credentialDefinition, + credentialDefinitionPrivate, + credentialOffer, + credentialRequest, attributeRawValues: attributes, revocationRegistryId: revocationRegistryDefinitionId, revocationStatusList, - revocationConfiguration: { + revocationConfiguration: new CredentialRevocationConfig({ + registryDefinition: new RevocationRegistryDefinition(revocationRegistryDefinition.handle), + registryDefinitionPrivate: new RevocationRegistryDefinitionPrivate(revocationRegistryDefinitionPrivate.handle), registryIndex: 9, - revocationRegistryDefinition, - revocationRegistryDefinitionPrivate, tailsPath, - }, + }), }) - const credential = anoncreds.getJson({ objectHandle: credentialObj }) const credentialInfo: AnonCredsCredentialInfo = { attributes, @@ -140,13 +159,21 @@ export function createCredentialForHolder(options: { credentialId, schemaId, } - return { - credential: JSON.parse(credential), + const returnObj = { + credential: credentialObj.toJson() as unknown as AnonCredsCredential, credentialInfo, revocationRegistryDefinition, tailsPath, credentialRequestMetadata, } + + credentialObj.handle.clear() + credentialOffer.handle.clear() + credentialRequest.handle.clear() + revocationRegistryDefinitionPrivate.clear() + revocationStatusList.handle.clear() + + return returnObj } export function createRevocationRegistryDefinition(options: { @@ -157,8 +184,8 @@ export function createRevocationRegistryDefinition(options: { const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate } = anoncreds.createRevocationRegistryDefinition({ credentialDefinitionId, - credentialDefinition: CredentialDefinition.load(JSON.stringify(credentialDefinition)).handle, - issuerId: 'mock:uri', + credentialDefinition: CredentialDefinition.fromJson(credentialDefinition).handle, + issuerId: credentialDefinition.issuerId as string, tag: 'some_tag', revocationRegistryType: 'CL_ACCUM', maximumCredentialNumber: 10, diff --git a/yarn.lock b/yarn.lock index 702840d99a..17b793081f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -864,12 +864,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.6": - version "0.1.0-dev.8" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.8.tgz#e9574ce0f7fdc557decc11f4ea4a252333f4a63b" - integrity sha512-KLjTFcGNjby3Pa1CkMXQUZqFJjOAv2peRJ7TzXVcqqXxVHbLIY+B3H8kHNYpRaAPAaKBfHybmzEuWrfFRB4q4w== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.9": + version "0.1.0-dev.9" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.9.tgz#5f09d5f147ce204abd9b3cea486c23e556cc8fdc" + integrity sha512-Ht5Gt1DfnFiRIY+AlTpe8Hcdryb+jKl79hGQ3kszdct/1JFZ70A81ssfwX0VKYTdCHt5jK5cJzVaOjOVmBKZiw== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.8" + "@hyperledger/anoncreds-shared" "0.1.0-dev.9" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -877,10 +877,10 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.8", "@hyperledger/anoncreds-shared@^0.1.0-dev.6": - version "0.1.0-dev.8" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.8.tgz#991ee8079435f6865f6b1c39b87ff6dfaf67ad3e" - integrity sha512-yJBgFPcRX2JSYkOctdubL9YYji9rYo+A6qDBrk3ClSNYdH5TFfl02da6ZbNp+iyOyhmbKV3GO1MTD8/fGEyxkw== +"@hyperledger/anoncreds-shared@0.1.0-dev.9", "@hyperledger/anoncreds-shared@^0.1.0-dev.9": + version "0.1.0-dev.9" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.9.tgz#7f6a033997e2641432a51ff2b609d603b3f9ab50" + integrity sha512-xbWEB9Z9PwkxC2awx74xt1OULMxihbK2v7818Dtrrmun75gNBXF8Jdorn9+t+TEd62QLrJpVUJ1ZCKhXPx81zw== "@hyperledger/aries-askar-nodejs@^0.1.0-dev.4": version "0.1.0-dev.4" @@ -902,22 +902,22 @@ dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.6": - version "0.1.0-dev.9" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.9.tgz#11552e7bcfb3eb9ebeceb9021be5aa775ab5a881" - integrity sha512-5tw1b7LbCInFn6KDL8TYIicHpndDwMOxMZkAjmOIUopK7CzHV7DM56UgCF3SUajxkPln1vreBqEuGAMiiTl70Q== +"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.10": + version "0.1.0-dev.10" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.10.tgz#f5fc988b3c11766475f7f084f814af8a4eca0caf" + integrity sha512-wETVax9HRidhRTRiRqwLTj/looNCvjZ3vdqMoqmPbmet5sM9eRkU9v4ipz4paqz4LiZipTx2fisLyYskKW9g6A== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.9" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.10" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" ref-array-di "^1.2.2" ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.9", "@hyperledger/indy-vdr-shared@^0.1.0-dev.6": - version "0.1.0-dev.9" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.9.tgz#b257c1ca75690812598e4606386c618e652d3f8c" - integrity sha512-n7W0YYVRad2AFD13RtZJ+xduUyz2Th6erfVW+7w21K04whyhsoPj9VTda2PtFGzPPNbZzrzRyEwdYNzsvy2GXQ== +"@hyperledger/indy-vdr-shared@0.1.0-dev.10", "@hyperledger/indy-vdr-shared@^0.1.0-dev.10": + version "0.1.0-dev.10" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.10.tgz#2a6d09a3a6ae3458ae9209ab7b6f09435e88b9c9" + integrity sha512-7fr9/Iliu0u94qE8u1g/RoLJOwMH0kFdclttwBHfTAKTQyfoXUPe1yIZ8ITRgLwnmFYPQpRa54lU+uv15c37fg== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" From a4204ef2db769de53d12f0d881d2c4422545c390 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 11 Mar 2023 16:31:09 +0100 Subject: [PATCH 086/139] fix: remove named capture groups (#1378) named capture groups are only supported in more recent versions of hermes Signed-off-by: Timo Glastra --- .../utils/__tests__/identifiers.test.ts | 2 +- .../src/anoncreds/utils/identifiers.ts | 128 +++++++++++++----- .../utils/_tests_/identifiers.test.ts | 2 +- .../src/anoncreds/utils/identifiers.ts | 128 +++++++++++++----- 4 files changed, 186 insertions(+), 74 deletions(-) diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts index 74488d4108..f60ebe04a1 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -13,7 +13,7 @@ import { describe('identifiers', () => { describe('indySdkAnonCredsRegistryIdentifierRegex', () => { - test('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { + test('matches against a legacy schema id, credential definition id and revocation registry id', () => { const did = '7Tqg6BwSSWapxgUDm9KKgg' const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts index 19f1df864c..decd85f10b 100644 --- a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -6,34 +6,28 @@ import { DID_INDY_REGEX } from '../../utils/did' const didIndyAnonCredsBase = - /(?did:indy:(?((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?)):(?([1-9A-HJ-NP-Za-km-z]{21,22})))\/anoncreds\/v0/ + /(did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22}))\/anoncreds\/v0/ // did:indy::/anoncreds/v0/SCHEMA// -const didIndySchemaIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/SCHEMA/(?.+)/(?[0-9.]+)$` -) +const didIndySchemaIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/SCHEMA/(.+)/([0-9.]+)$`) // :2:: -const legacyIndySchemaIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):2:(?.+):(?[0-9.]+)$/ +const legacyIndySchemaIdRegex = /^([a-zA-Z0-9]{21,22}):2:(.+):([0-9.]+)$/ // did:indy::/anoncreds/v0/CLAIM_DEF// -const didIndyCredentialDefinitionIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/CLAIM_DEF/(?[1-9][0-9]*)/(?.+)$` -) +const didIndyCredentialDefinitionIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/CLAIM_DEF/([1-9][0-9]*)/(.+)$`) // :3:CL:: -const legacyIndyCredentialDefinitionIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):3:CL:(?[1-9][0-9]*):(?.+)$/ +const legacyIndyCredentialDefinitionIdRegex = /^([a-zA-Z0-9]{21,22}):3:CL:([1-9][0-9]*):(.+)$/ // did:indy::/anoncreds/v0/REV_REG_DEF/// const didIndyRevocationRegistryIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/REV_REG_DEF/(?[1-9][0-9]*)/(?.+)/(?.+)$` + `^${didIndyAnonCredsBase.source}/REV_REG_DEF/([1-9][0-9]*)/(.+)/(.+)$` ) // :4::3:CL::CL_ACCUM: const legacyIndyRevocationRegistryIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):4:[a-zA-Z0-9]{21,22}:3:CL:(?[1-9][0-9]*):(?.+):CL_ACCUM:(?.+)$/ + /^([a-zA-Z0-9]{21,22}):4:[a-zA-Z0-9]{21,22}:3:CL:([1-9][0-9]*):(.+):CL_ACCUM:(.+)$/ // combines both legacy and did:indy anoncreds identifiers and also the issuer id const indySdkAnonCredsRegexes = [ @@ -58,7 +52,7 @@ const indySdkAnonCredsRegexes = [ ] export const indySdkAnonCredsRegistryIdentifierRegex = new RegExp( - indySdkAnonCredsRegexes.map((r) => r.source.replace(/(\?<[a-zA-Z]+>)?/g, '')).join('|') + indySdkAnonCredsRegexes.map((r) => r.source).join('|') ) export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, name: string, version: string) { @@ -110,12 +104,33 @@ interface ParsedSchemaId { namespace?: string } -export function parseSchemaId(schemaId: string) { - const match = schemaId.match(didIndySchemaIdRegex) ?? schemaId.match(legacyIndySchemaIdRegex) - - if (!match) throw new Error(`Invalid schema id: ${schemaId}`) - - return match.groups as unknown as ParsedSchemaId +export function parseSchemaId(schemaId: string): ParsedSchemaId { + const didIndyMatch = schemaId.match(didIndySchemaIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaName, schemaVersion] = didIndyMatch + + return { + did, + namespaceIdentifier, + schemaName, + schemaVersion, + namespace, + } + } + + const legacyMatch = schemaId.match(legacyIndySchemaIdRegex) + if (legacyMatch) { + const [, did, schemaName, schemaVersion] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaName, + schemaVersion, + } + } + + throw new Error(`Invalid schema id: ${schemaId}`) } interface ParsedCredentialDefinitionId { @@ -126,14 +141,33 @@ interface ParsedCredentialDefinitionId { namespace?: string } -export function parseCredentialDefinitionId(credentialDefinitionId: string) { - const match = - credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) ?? - credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) - - if (!match) throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) - - return match.groups as unknown as ParsedCredentialDefinitionId +export function parseCredentialDefinitionId(credentialDefinitionId: string): ParsedCredentialDefinitionId { + const didIndyMatch = credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaSeqNo, tag] = didIndyMatch + + return { + did, + namespaceIdentifier, + schemaSeqNo, + tag, + namespace, + } + } + + const legacyMatch = credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) + if (legacyMatch) { + const [, did, schemaSeqNo, tag] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaSeqNo, + tag, + } + } + + throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) } interface ParsedRevocationRegistryId { @@ -145,12 +179,34 @@ interface ParsedRevocationRegistryId { namespace?: string } -export function parseRevocationRegistryId(revocationRegistryId: string) { - const match = - revocationRegistryId.match(didIndyRevocationRegistryIdRegex) ?? - revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) - - if (!match) throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) - - return match.groups as unknown as ParsedRevocationRegistryId +export function parseRevocationRegistryId(revocationRegistryId: string): ParsedRevocationRegistryId { + const didIndyMatch = revocationRegistryId.match(didIndyRevocationRegistryIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = + didIndyMatch + + return { + did, + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag, + namespace, + } + } + + const legacyMatch = revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) + if (legacyMatch) { + const [, did, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag, + } + } + + throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) } diff --git a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts index 555605a1d9..1f01c18209 100644 --- a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts +++ b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts @@ -13,7 +13,7 @@ import { describe('identifiers', () => { describe('indyVdrAnonCredsRegistryIdentifierRegex', () => { - test('matches against a legacy indy did, schema id, credential definition id and revocation registry id', () => { + test('matches against a legacy schema id, credential definition id and revocation registry id', () => { const did = '7Tqg6BwSSWapxgUDm9KKgg' const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' diff --git a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts index e7e1a2bd49..cc05d2b3bb 100644 --- a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts @@ -6,34 +6,28 @@ import { DID_INDY_REGEX } from '../../utils/did' const didIndyAnonCredsBase = - /(?did:indy:(?((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?)):(?([1-9A-HJ-NP-Za-km-z]{21,22})))\/anoncreds\/v0/ + /(did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22}))\/anoncreds\/v0/ // did:indy::/anoncreds/v0/SCHEMA// -const didIndySchemaIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/SCHEMA/(?.+)/(?[0-9.]+)$` -) +const didIndySchemaIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/SCHEMA/(.+)/([0-9.]+)$`) // :2:: -const legacyIndySchemaIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):2:(?.+):(?[0-9.]+)$/ +const legacyIndySchemaIdRegex = /^([a-zA-Z0-9]{21,22}):2:(.+):([0-9.]+)$/ // did:indy::/anoncreds/v0/CLAIM_DEF// -const didIndyCredentialDefinitionIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/CLAIM_DEF/(?[1-9][0-9]*)/(?.+)$` -) +const didIndyCredentialDefinitionIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/CLAIM_DEF/([1-9][0-9]*)/(.+)$`) // :3:CL:: -const legacyIndyCredentialDefinitionIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):3:CL:(?[1-9][0-9]*):(?.+)$/ +const legacyIndyCredentialDefinitionIdRegex = /^([a-zA-Z0-9]{21,22}):3:CL:([1-9][0-9]*):(.+)$/ // did:indy::/anoncreds/v0/REV_REG_DEF/// const didIndyRevocationRegistryIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/REV_REG_DEF/(?[1-9][0-9]*)/(?.+)/(?.+)$` + `^${didIndyAnonCredsBase.source}/REV_REG_DEF/([1-9][0-9]*)/(.+)/(.+)$` ) // :4::3:CL::CL_ACCUM: const legacyIndyRevocationRegistryIdRegex = - /^(?(?[a-zA-Z0-9]{21,22})):4:[a-zA-Z0-9]{21,22}:3:CL:(?[1-9][0-9]*):(?.+):CL_ACCUM:(?.+)$/ + /^([a-zA-Z0-9]{21,22}):4:[a-zA-Z0-9]{21,22}:3:CL:([1-9][0-9]*):(.+):CL_ACCUM:(.+)$/ // combines both legacy and did:indy anoncreds identifiers and also the issuer id const indyVdrAnonCredsRegexes = [ @@ -58,7 +52,7 @@ const indyVdrAnonCredsRegexes = [ ] export const indyVdrAnonCredsRegistryIdentifierRegex = new RegExp( - indyVdrAnonCredsRegexes.map((r) => r.source.replace(/(\?<[a-zA-Z]+>)?/g, '')).join('|') + indyVdrAnonCredsRegexes.map((r) => r.source).join('|') ) export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, name: string, version: string) { @@ -110,12 +104,33 @@ interface ParsedSchemaId { namespace?: string } -export function parseSchemaId(schemaId: string) { - const match = schemaId.match(didIndySchemaIdRegex) ?? schemaId.match(legacyIndySchemaIdRegex) - - if (!match) throw new Error(`Invalid schema id: ${schemaId}`) - - return match.groups as unknown as ParsedSchemaId +export function parseSchemaId(schemaId: string): ParsedSchemaId { + const didIndyMatch = schemaId.match(didIndySchemaIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaName, schemaVersion] = didIndyMatch + + return { + did, + namespaceIdentifier, + schemaName, + schemaVersion, + namespace, + } + } + + const legacyMatch = schemaId.match(legacyIndySchemaIdRegex) + if (legacyMatch) { + const [, did, schemaName, schemaVersion] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaName, + schemaVersion, + } + } + + throw new Error(`Invalid schema id: ${schemaId}`) } interface ParsedCredentialDefinitionId { @@ -126,14 +141,33 @@ interface ParsedCredentialDefinitionId { namespace?: string } -export function parseCredentialDefinitionId(credentialDefinitionId: string) { - const match = - credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) ?? - credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) - - if (!match) throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) - - return match.groups as unknown as ParsedCredentialDefinitionId +export function parseCredentialDefinitionId(credentialDefinitionId: string): ParsedCredentialDefinitionId { + const didIndyMatch = credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaSeqNo, tag] = didIndyMatch + + return { + did, + namespaceIdentifier, + schemaSeqNo, + tag, + namespace, + } + } + + const legacyMatch = credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) + if (legacyMatch) { + const [, did, schemaSeqNo, tag] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaSeqNo, + tag, + } + } + + throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) } interface ParsedRevocationRegistryId { @@ -145,12 +179,34 @@ interface ParsedRevocationRegistryId { namespace?: string } -export function parseRevocationRegistryId(revocationRegistryId: string) { - const match = - revocationRegistryId.match(didIndyRevocationRegistryIdRegex) ?? - revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) - - if (!match) throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) - - return match.groups as unknown as ParsedRevocationRegistryId +export function parseRevocationRegistryId(revocationRegistryId: string): ParsedRevocationRegistryId { + const didIndyMatch = revocationRegistryId.match(didIndyRevocationRegistryIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = + didIndyMatch + + return { + did, + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag, + namespace, + } + } + + const legacyMatch = revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) + if (legacyMatch) { + const [, did, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag, + } + } + + throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) } From d59366aaf75dfabf4933c684a7a5cf700ccaca2a Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Wed, 15 Mar 2023 11:04:52 +0100 Subject: [PATCH 087/139] docs: fix example usage of indy-sdk-react-native package (#1382) Signed-off-by: Jim Ezesinachi --- packages/indy-sdk/src/IndySdkModuleConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/indy-sdk/src/IndySdkModuleConfig.ts b/packages/indy-sdk/src/IndySdkModuleConfig.ts index 3a269a93f5..5bb066bebb 100644 --- a/packages/indy-sdk/src/IndySdkModuleConfig.ts +++ b/packages/indy-sdk/src/IndySdkModuleConfig.ts @@ -22,7 +22,7 @@ export interface IndySdkModuleConfigOptions { * ## React Native * * ```ts - * import * as indySdk from 'indy-sdk-react-native' + * import indySdk from 'indy-sdk-react-native' * * const indySdkModule = new IndySdkModule({ * indySdk From f27fb9921e11e5bcd654611d97d9fa1c446bc2d5 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 15 Mar 2023 09:23:10 -0300 Subject: [PATCH 088/139] feat: basic message pthid/thid support (#1381) Signed-off-by: Ariel Gentile --- packages/askar/tests/askar-sqlite.e2e.test.ts | 11 +++-- .../basic-messages/BasicMessagesApi.ts | 17 ++++++- .../__tests__/basic-messages.e2e.test.ts | 49 +++++++++++++++++++ .../repository/BasicMessageRecord.ts | 11 ++++- .../services/BasicMessageService.ts | 24 ++++++++- packages/core/tests/wallet.test.ts | 11 +++-- 6 files changed, 113 insertions(+), 10 deletions(-) diff --git a/packages/askar/tests/askar-sqlite.e2e.test.ts b/packages/askar/tests/askar-sqlite.e2e.test.ts index 3de47f3183..41280f0684 100644 --- a/packages/askar/tests/askar-sqlite.e2e.test.ts +++ b/packages/askar/tests/askar-sqlite.e2e.test.ts @@ -137,9 +137,14 @@ describeRunInNodeVersion([18], 'Askar SQLite agents', () => { await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) // Expect same basic message record to exist in new wallet - expect(await bobBasicMessageRepository.getById(bobAgent.context, basicMessageRecord.id)).toMatchObject( - basicMessageRecord - ) + expect(await bobBasicMessageRepository.getById(bobAgent.context, basicMessageRecord.id)).toMatchObject({ + id: basicMessageRecord.id, + connectionId: basicMessageRecord.connectionId, + content: basicMessageRecord.content, + createdAt: basicMessageRecord.createdAt, + updatedAt: basicMessageRecord.updatedAt, + type: basicMessageRecord.type, + }) }) test('changing wallet key', async () => { diff --git a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts index ff788e00fe..82e94cf9e4 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts @@ -41,13 +41,14 @@ export class BasicMessagesApi { * @throws {MessageSendingError} If message is undeliverable * @returns the created record */ - public async sendMessage(connectionId: string, message: string) { + public async sendMessage(connectionId: string, message: string, parentThreadId?: string) { const connection = await this.connectionService.getById(this.agentContext, connectionId) const { message: basicMessage, record: basicMessageRecord } = await this.basicMessageService.createMessage( this.agentContext, message, - connection + connection, + parentThreadId ) const outboundMessageContext = new OutboundMessageContext(basicMessage, { agentContext: this.agentContext, @@ -81,6 +82,18 @@ export class BasicMessagesApi { return this.basicMessageService.getById(this.agentContext, basicMessageRecordId) } + /** + * Retrieve a basic message record by thread id + * + * @param threadId The thread id + * @throws {RecordNotFoundError} If no record is found + * @throws {RecordDuplicateError} If multiple records are found + * @returns The connection record + */ + public async getByThreadId(basicMessageRecordId: string) { + return this.basicMessageService.getByThreadId(this.agentContext, basicMessageRecordId) + } + /** * Delete a basic message record by id * diff --git a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts index 1ed5be6fb7..77f746030c 100644 --- a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts @@ -84,6 +84,55 @@ describe('Basic Messages E2E', () => { }) }) + test('Alice and Faber exchange messages using threadId', async () => { + testLogger.test('Alice sends message to Faber') + const helloRecord = await aliceAgent.basicMessages.sendMessage(aliceConnection.id, 'Hello') + + expect(helloRecord.content).toBe('Hello') + + testLogger.test('Faber waits for message from Alice') + const helloMessage = await waitForBasicMessage(faberAgent, { + content: 'Hello', + }) + + testLogger.test('Faber sends message to Alice') + const replyRecord = await faberAgent.basicMessages.sendMessage(faberConnection.id, 'How are you?', helloMessage.id) + expect(replyRecord.content).toBe('How are you?') + expect(replyRecord.parentThreadId).toBe(helloMessage.id) + + testLogger.test('Alice waits until she receives message from faber') + const replyMessage = await waitForBasicMessage(aliceAgent, { + content: 'How are you?', + }) + expect(replyMessage.content).toBe('How are you?') + expect(replyMessage.thread?.parentThreadId).toBe(helloMessage.id) + + // Both sender and recipient shall be able to find the threaded messages + // Hello message + const aliceHelloMessage = await aliceAgent.basicMessages.getByThreadId(helloMessage.id) + const faberHelloMessage = await faberAgent.basicMessages.getByThreadId(helloMessage.id) + expect(aliceHelloMessage).toMatchObject({ + content: helloRecord.content, + threadId: helloRecord.threadId, + }) + expect(faberHelloMessage).toMatchObject({ + content: helloRecord.content, + threadId: helloRecord.threadId, + }) + + // Reply message + const aliceReplyMessages = await aliceAgent.basicMessages.findAllByQuery({ parentThreadId: helloMessage.id }) + const faberReplyMessages = await faberAgent.basicMessages.findAllByQuery({ parentThreadId: helloMessage.id }) + expect(aliceReplyMessages.length).toBe(1) + expect(aliceReplyMessages[0]).toMatchObject({ + content: replyRecord.content, + parentThreadId: replyRecord.parentThreadId, + threadId: replyRecord.threadId, + }) + expect(faberReplyMessages.length).toBe(1) + expect(faberReplyMessages[0]).toMatchObject(replyRecord) + }) + test('Alice is unable to send a message', async () => { testLogger.test('Alice sends message to Faber that is undeliverable') diff --git a/packages/core/src/modules/basic-messages/repository/BasicMessageRecord.ts b/packages/core/src/modules/basic-messages/repository/BasicMessageRecord.ts index 4a0de5decd..42199106c6 100644 --- a/packages/core/src/modules/basic-messages/repository/BasicMessageRecord.ts +++ b/packages/core/src/modules/basic-messages/repository/BasicMessageRecord.ts @@ -8,6 +8,8 @@ export type CustomBasicMessageTags = TagsBase export type DefaultBasicMessageTags = { connectionId: string role: BasicMessageRole + threadId?: string + parentThreadId?: string } export type BasicMessageTags = RecordTags @@ -18,7 +20,8 @@ export interface BasicMessageStorageProps { connectionId: string role: BasicMessageRole tags?: CustomBasicMessageTags - + threadId?: string + parentThreadId?: string content: string sentTime: string } @@ -28,6 +31,8 @@ export class BasicMessageRecord extends BaseRecord { await bobAgent.wallet.initialize({ id: backupWalletName, key: backupWalletName }) // Expect same basic message record to exist in new wallet - expect(await bobBasicMessageRepository.getById(bobAgent.context, basicMessageRecord.id)).toMatchObject( - basicMessageRecord - ) + expect(await bobBasicMessageRepository.getById(bobAgent.context, basicMessageRecord.id)).toMatchObject({ + id: basicMessageRecord.id, + connectionId: basicMessageRecord.connectionId, + content: basicMessageRecord.content, + createdAt: basicMessageRecord.createdAt, + updatedAt: basicMessageRecord.updatedAt, + type: basicMessageRecord.type, + }) }) test('changing wallet key', async () => { From 0351eec52a9f5e581508819df3005be7b995e59e Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 15 Mar 2023 12:02:50 -0300 Subject: [PATCH 089/139] fix: connection id in sessions for new connections (#1383) Signed-off-by: Ariel Gentile --- packages/core/src/agent/TransportService.ts | 10 +++++++++ .../handlers/ConnectionRequestHandler.ts | 22 ++++++++++--------- .../handlers/DidExchangeRequestHandler.ts | 22 ++++++++++--------- .../modules/routing/__tests__/pickup.test.ts | 7 ++++++ 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index 9455a045bb..b4eed7fe1e 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -4,6 +4,7 @@ import type { DidDocument } from '../modules/dids' import type { EncryptedMessage } from '../types' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' +import { AriesFrameworkError } from '../error' import { injectable } from '../plugins' @injectable() @@ -18,6 +19,15 @@ export class TransportService { return Object.values(this.transportSessionTable).find((session) => session?.connectionId === connectionId) } + public setConnectionIdForSession(sessionId: string, connectionId: string) { + const session = this.findSessionById(sessionId) + if (!session) { + throw new AriesFrameworkError(`Session not found with id ${sessionId}`) + } + session.connectionId = connectionId + this.saveSession(session) + } + public hasInboundEndpoint(didDocument: DidDocument): boolean { return Boolean(didDocument.service?.find((s) => s.serviceEndpoint !== DID_COMM_TRANSPORT_QUEUE)) } diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 0cbead3793..db107c7d4d 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -5,6 +5,7 @@ import type { RoutingService } from '../../routing/services/RoutingService' import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { ConnectionService } from '../services/ConnectionService' +import { TransportService } from '../../../agent/TransportService' import { OutboundMessageContext } from '../../../agent/models' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { tryParseDid } from '../../dids/domain/parse' @@ -34,7 +35,7 @@ export class ConnectionRequestHandler implements MessageHandler { } public async handle(messageContext: MessageHandlerInboundMessage) { - const { agentContext, connection, recipientKey, senderKey, message } = messageContext + const { agentContext, connection, recipientKey, senderKey, message, sessionId } = messageContext if (!recipientKey || !senderKey) { throw new AriesFrameworkError('Unable to process connection request without senderVerkey or recipientKey') @@ -62,30 +63,31 @@ export class ConnectionRequestHandler implements MessageHandler { ) } - const receivedDidRecord = await this.didRepository.findReceivedDidByRecipientKey( - messageContext.agentContext, - senderKey - ) + const receivedDidRecord = await this.didRepository.findReceivedDidByRecipientKey(agentContext, senderKey) if (receivedDidRecord) { throw new AriesFrameworkError(`A received did record for sender key ${senderKey.fingerprint} already exists.`) } const connectionRecord = await this.connectionService.processRequest(messageContext, outOfBandRecord) + // Associate the new connection with the session created for the inbound message + if (sessionId) { + const transportService = agentContext.dependencyManager.resolve(TransportService) + transportService.setConnectionIdForSession(sessionId, connectionRecord.id) + } + if (connectionRecord?.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - const routing = outOfBandRecord.reusable - ? await this.routingService.getRouting(messageContext.agentContext) - : undefined + const routing = outOfBandRecord.reusable ? await this.routingService.getRouting(agentContext) : undefined const { message } = await this.connectionService.createResponse( - messageContext.agentContext, + agentContext, connectionRecord, outOfBandRecord, routing ) return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, + agentContext, connection: connectionRecord, outOfBand: outOfBandRecord, }) diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index 3983fd0a89..9f2f9f01f3 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -5,6 +5,7 @@ import type { RoutingService } from '../../routing/services/RoutingService' import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { DidExchangeProtocol } from '../DidExchangeProtocol' +import { TransportService } from '../../../agent/TransportService' import { OutboundMessageContext } from '../../../agent/models' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { tryParseDid } from '../../dids/domain/parse' @@ -35,7 +36,7 @@ export class DidExchangeRequestHandler implements MessageHandler { } public async handle(messageContext: MessageHandlerInboundMessage) { - const { agentContext, recipientKey, senderKey, message, connection } = messageContext + const { agentContext, recipientKey, senderKey, message, connection, sessionId } = messageContext if (!recipientKey || !senderKey) { throw new AriesFrameworkError('Unable to process connection request without senderKey or recipientKey') @@ -65,10 +66,7 @@ export class DidExchangeRequestHandler implements MessageHandler { ) } - const receivedDidRecord = await this.didRepository.findReceivedDidByRecipientKey( - messageContext.agentContext, - senderKey - ) + const receivedDidRecord = await this.didRepository.findReceivedDidByRecipientKey(agentContext, senderKey) if (receivedDidRecord) { throw new AriesFrameworkError(`A received did record for sender key ${senderKey.fingerprint} already exists.`) } @@ -83,21 +81,25 @@ export class DidExchangeRequestHandler implements MessageHandler { const connectionRecord = await this.didExchangeProtocol.processRequest(messageContext, outOfBandRecord) + // Associate the new connection with the session created for the inbound message + if (sessionId) { + const transportService = agentContext.dependencyManager.resolve(TransportService) + transportService.setConnectionIdForSession(sessionId, connectionRecord.id) + } + if (connectionRecord.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { // TODO We should add an option to not pass routing and therefore do not rotate keys and use the keys from the invitation // TODO: Allow rotation of keys used in the invitation for new ones not only when out-of-band is reusable - const routing = outOfBandRecord.reusable - ? await this.routingService.getRouting(messageContext.agentContext) - : undefined + const routing = outOfBandRecord.reusable ? await this.routingService.getRouting(agentContext) : undefined const message = await this.didExchangeProtocol.createResponse( - messageContext.agentContext, + agentContext, connectionRecord, outOfBandRecord, routing ) return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, + agentContext, connection: connectionRecord, outOfBand: outOfBandRecord, }) diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts b/packages/core/src/modules/routing/__tests__/pickup.test.ts index c67bac9b89..54a37efd81 100644 --- a/packages/core/src/modules/routing/__tests__/pickup.test.ts +++ b/packages/core/src/modules/routing/__tests__/pickup.test.ts @@ -78,6 +78,13 @@ describe('E2E Pick Up protocol', () => { mediatorRecipientConnection = await mediatorAgent.connections.returnWhenIsConnected(mediatorRecipientConnection!.id) + // Now they are connected, reinitialize recipient agent in order to lose the session (as with SubjectTransport it remains open) + await recipientAgent.shutdown() + + recipientAgent = new Agent(recipientOptions) + recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) + await recipientAgent.initialize() + const message = 'hello pickup V1' await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) From c17013c808a278d624210ce9e4333860cd78fc19 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 17 Mar 2023 10:19:23 -0300 Subject: [PATCH 090/139] feat(anoncreds): use legacy prover did (#1374) Signed-off-by: Ariel Gentile --- packages/anoncreds-rs/package.json | 4 +- .../src/services/AnonCredsRsHolderService.ts | 14 ++++- .../AnonCredsRsHolderService.test.ts | 4 +- .../__tests__/AnonCredsRsServices.test.ts | 4 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 8 ++- .../LegacyIndyCredentialFormatService.ts | 18 +++++- .../formats/LegacyIndyProofFormatService.ts | 10 ++++ packages/anoncreds/src/index.ts | 1 + .../services/AnonCredsHolderServiceOptions.ts | 1 + .../tests/InMemoryAnonCredsRegistry.ts | 60 ++++++++++++------- .../services/IndySdkHolderService.ts | 4 ++ yarn.lock | 18 +++--- 12 files changed, 103 insertions(+), 43 deletions(-) diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index 1539c8dd9d..cbb80f2623 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.9", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.10", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.9", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.10", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index 88d167d722..052c1d7495 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -30,8 +30,9 @@ import { AnonCredsCredentialRecord, AnonCredsLinkSecretRepository, AnonCredsCredentialRepository, + legacyIndyCredentialDefinitionIdRegex, } from '@aries-framework/anoncreds' -import { utils, injectable } from '@aries-framework/core' +import { TypedArrayEncoder, AriesFrameworkError, utils, injectable } from '@aries-framework/core' import { anoncreds, Credential, @@ -193,7 +194,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { agentContext: AgentContext, options: CreateCredentialRequestOptions ): Promise { - const { credentialDefinition, credentialOffer } = options + const { useLegacyProverDid, credentialDefinition, credentialOffer } = options let createReturnObj: | { credentialRequest: CredentialRequest; credentialRequestMetadata: CredentialRequestMetadata } | undefined @@ -212,8 +213,15 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { ) } + const isLegacyIdentifier = credentialOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex) + if (!isLegacyIdentifier && useLegacyProverDid) { + throw new AriesFrameworkError('Cannot use legacy prover_did with non-legacy identifiers') + } createReturnObj = CredentialRequest.create({ - entropy: anoncreds.generateNonce(), // FIXME: find a better source of entropy + entropy: !useLegacyProverDid || !isLegacyIdentifier ? anoncreds.generateNonce() : undefined, + proverDid: useLegacyProverDid + ? TypedArrayEncoder.toBase58(TypedArrayEncoder.fromString(anoncreds.generateNonce().slice(0, 16))) + : undefined, credentialDefinition: credentialDefinition as unknown as JsonObject, credentialOffer: credentialOffer as unknown as JsonObject, masterSecret: { value: { ms: linkSecretRecord.value } }, diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index 3240dd5ff9..203818a159 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -17,6 +17,7 @@ import { } from '@aries-framework/anoncreds' import { anoncreds, RevocationRegistryDefinition } from '@hyperledger/anoncreds-nodejs' +import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { AnonCredsCredentialDefinitionRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialDefinitionRepository' import { AnonCredsCredentialRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialRepository' import { AnonCredsLinkSecretRepository } from '../../../../anoncreds/src/repository/AnonCredsLinkSecretRepository' @@ -56,7 +57,8 @@ const agentContext = getAgentContext({ agentConfig, }) -describe('AnonCredsRsHolderService', () => { +// FIXME: Re-include in tests when NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { const getByCredentialIdMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'getByCredentialId') beforeEach(() => { diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index e0ba7089a9..dcaadd8916 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -20,6 +20,7 @@ import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { encodeCredentialValue } from '../../../../anoncreds/src/utils/credential' import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' @@ -46,7 +47,8 @@ const agentContext = getAgentContext({ agentConfig, }) -describe('AnonCredsRsServices', () => { +// FIXME: Re-include in tests when NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'AnonCredsRsServices', () => { test('issuance flow without revocation', async () => { const issuerId = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index eadaffd740..cad6411f59 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -30,6 +30,7 @@ import { import { Subject } from 'rxjs' import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' +import { describeRunInNodeVersion } from '../../../tests/runInVersion' import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' @@ -37,7 +38,7 @@ import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderServi import { AnonCredsRsIssuerService } from '../src/services/AnonCredsRsIssuerService' import { AnonCredsRsVerifierService } from '../src/services/AnonCredsRsVerifierService' -const registry = new InMemoryAnonCredsRegistry() +const registry = new InMemoryAnonCredsRegistry({ useLegacyIdentifiers: true }) const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], }) @@ -69,9 +70,10 @@ const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService( const legacyIndyProofFormatService = new LegacyIndyProofFormatService() // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) -const indyDid = 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt' +const indyDid = 'LjgpST2rjsoxYegQDRm7EL' -describe('Legacy indy format services using anoncreds-rs', () => { +// FIXME: Re-include in tests when NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', () => { test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { const schema = await anonCredsIssuerService.createSchema(agentContext, { attrNames: ['name', 'age'], diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 33aba1e7bc..6c6630c6f7 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -45,6 +45,7 @@ import { AnonCredsError } from '../error' import { AnonCredsCredentialProposal } from '../models/AnonCredsCredentialProposal' import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' +import { legacyIndyCredentialDefinitionIdRegex, legacyIndySchemaIdRegex } from '../utils' import { convertAttributesToCredentialValues, assertCredentialValuesMatch, @@ -146,10 +147,14 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic if (!credentialDefinitionId) { throw new AriesFrameworkError( - 'No credentialDefinitionId in proposal or provided as input to accept proposal method.' + 'No credential definition id in proposal or provided as input to accept proposal method.' ) } + if (!credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex)) { + throw new AriesFrameworkError(`${credentialDefinitionId} is not a valid legacy indy credential definition id`) + } + if (!attributes) { throw new AriesFrameworkError('No attributes in proposal or provided as input to accept proposal method.') } @@ -205,7 +210,10 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const credOffer = attachment.getDataAsJson() - if (!credOffer.schema_id || !credOffer.cred_def_id) { + if ( + !credOffer.schema_id.match(legacyIndySchemaIdRegex) || + !credOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex) + ) { throw new ProblemReportError('Invalid credential offer', { problemCode: CredentialProblemReportReason.IssuanceAbandoned, }) @@ -226,6 +234,11 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const credentialOffer = offerAttachment.getDataAsJson() + if (!credentialOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex)) { + throw new AriesFrameworkError( + `${credentialOffer.cred_def_id} is not a valid legacy indy credential definition id` + ) + } // Get credential definition const registry = registryService.getRegistryForIdentifier(agentContext, credentialOffer.cred_def_id) const { credentialDefinition, resolutionMetadata } = await registry.getCredentialDefinition( @@ -243,6 +256,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic credentialOffer, credentialDefinition, linkSecretId: credentialFormats?.indy?.linkSecretId, + useLegacyProverDid: true, }) if (!credentialRequest.prover_did) { diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts index c2e5e2d1d5..17a74c8d1a 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -62,6 +62,8 @@ import { checkValidCredentialValueEncoding, encodeCredentialValue, assertNoDuplicateGroupsNamesInProofRequest, + legacyIndyCredentialDefinitionIdRegex, + legacyIndySchemaIdRegex, } from '../utils' const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' @@ -471,6 +473,10 @@ export class LegacyIndyProofFormatService implements ProofFormatService private revocationRegistryDefinitions: Record private revocationStatusLists: Record> + private useLegacyIdentifiers: boolean public constructor({ existingSchemas = {}, existingCredentialDefinitions = {}, existingRevocationRegistryDefinitions = {}, existingRevocationStatusLists = {}, + useLegacyIdentifiers = false, }: { existingSchemas?: Record existingCredentialDefinitions?: Record existingRevocationRegistryDefinitions?: Record existingRevocationStatusLists?: Record> + useLegacyIdentifiers?: boolean } = {}) { this.schemas = existingSchemas this.credentialDefinitions = existingCredentialDefinitions this.revocationRegistryDefinitions = existingRevocationRegistryDefinitions this.revocationStatusLists = existingRevocationStatusLists + this.useLegacyIdentifiers = useLegacyIdentifiers } public async getSchema(agentContext: AgentContext, schemaId: string): Promise { @@ -94,21 +98,23 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { agentContext: AgentContext, options: RegisterSchemaOptions ): Promise { - const { namespaceIdentifier, namespace } = parseIndyDid(options.schema.issuerId) - const didIndySchemaId = getDidIndySchemaId( - namespace, - namespaceIdentifier, - options.schema.name, - options.schema.version - ) - const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version) + let legacyIssuerId + let didIndySchemaId = '' + if (this.useLegacyIdentifiers) { + legacyIssuerId = options.schema.issuerId + } else { + const { namespace, namespaceIdentifier } = parseIndyDid(options.schema.issuerId) + legacyIssuerId = namespaceIdentifier + didIndySchemaId = getDidIndySchemaId(namespace, namespaceIdentifier, options.schema.name, options.schema.version) + this.schemas[didIndySchemaId] = options.schema + } + const legacySchemaId = getLegacySchemaId(legacyIssuerId, options.schema.name, options.schema.version) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) - this.schemas[didIndySchemaId] = options.schema this.schemas[legacySchemaId] = { ...options.schema, - issuerId: namespaceIdentifier, + issuerId: legacyIssuerId, } return { @@ -121,7 +127,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { schemaState: { state: 'finished', schema: options.schema, - schemaId: didIndySchemaId, + schemaId: this.useLegacyIdentifiers ? legacySchemaId : didIndySchemaId, }, } } @@ -163,24 +169,32 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { ) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) - const { namespaceIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId) + let legacyIssuerId + let didIndyCredentialDefinitionId = '' + if (this.useLegacyIdentifiers) { + legacyIssuerId = options.credentialDefinition.issuerId + } else { + const { namespace, namespaceIdentifier } = parseIndyDid(options.credentialDefinition.issuerId) + legacyIssuerId = namespaceIdentifier + didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( + namespace, + namespaceIdentifier, + indyLedgerSeqNo, + options.credentialDefinition.tag + ) + + this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition + } - const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( - namespace, - namespaceIdentifier, - indyLedgerSeqNo, - options.credentialDefinition.tag - ) const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( - namespaceIdentifier, + legacyIssuerId, indyLedgerSeqNo, options.credentialDefinition.tag ) - this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition this.credentialDefinitions[legacyCredentialDefinitionId] = { ...options.credentialDefinition, - issuerId: namespaceIdentifier, + issuerId: legacyIssuerId, schemaId: legacySchemaId, } @@ -190,7 +204,9 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { credentialDefinitionState: { state: 'finished', credentialDefinition: options.credentialDefinition, - credentialDefinitionId: didIndyCredentialDefinitionId, + credentialDefinitionId: this.useLegacyIdentifiers + ? legacyCredentialDefinitionId + : didIndyCredentialDefinitionId, }, } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index d54a46a016..d4e7861e87 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -211,6 +211,10 @@ export class IndySdkHolderService implements AnonCredsHolderService { ): Promise { assertIndySdkWallet(agentContext.wallet) + if (!options.useLegacyProverDid) { + throw new AriesFrameworkError('Indy SDK only supports legacy prover did for credential requests') + } + const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) // We just generate a prover did like string, as it's not used for anything and we don't need diff --git a/yarn.lock b/yarn.lock index 17b793081f..ab6d703c0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -864,12 +864,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.9": - version "0.1.0-dev.9" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.9.tgz#5f09d5f147ce204abd9b3cea486c23e556cc8fdc" - integrity sha512-Ht5Gt1DfnFiRIY+AlTpe8Hcdryb+jKl79hGQ3kszdct/1JFZ70A81ssfwX0VKYTdCHt5jK5cJzVaOjOVmBKZiw== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.10": + version "0.1.0-dev.10" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.10.tgz#80c093ecb08a277fb494399b64aad1b900c3103f" + integrity sha512-ju5mJPwuyebAPziuf+eUOwxEws02G2FHEp/qG3GV3kxtlx7THW7HVB7dMSNqhRVKCsbcNnZtWJB1UiPvWqboUg== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.9" + "@hyperledger/anoncreds-shared" "0.1.0-dev.10" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -877,10 +877,10 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.9", "@hyperledger/anoncreds-shared@^0.1.0-dev.9": - version "0.1.0-dev.9" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.9.tgz#7f6a033997e2641432a51ff2b609d603b3f9ab50" - integrity sha512-xbWEB9Z9PwkxC2awx74xt1OULMxihbK2v7818Dtrrmun75gNBXF8Jdorn9+t+TEd62QLrJpVUJ1ZCKhXPx81zw== +"@hyperledger/anoncreds-shared@0.1.0-dev.10", "@hyperledger/anoncreds-shared@^0.1.0-dev.10": + version "0.1.0-dev.10" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.10.tgz#9d67f64e98ff41971644c95b03dabafd741df4df" + integrity sha512-POvcwQrUcPrwoZehQa38pN1dnjyeUlrQ6VlksbBRS8SUHJuyixZsD+d3XoumqaNfl9Z1DCjfuOgEiPlec01gXQ== "@hyperledger/aries-askar-nodejs@^0.1.0-dev.4": version "0.1.0-dev.4" From 5f71dc2b403f6cb0fc9bb13f35051d377c2d1250 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 18 Mar 2023 08:01:20 -0300 Subject: [PATCH 091/139] feat(anoncreds): add AnonCreds format services (#1385) Signed-off-by: Ariel Gentile --- .../anoncreds-rs/tests/anoncreds-flow.test.ts | 358 ++++++++ .../AnonCredsCredentialFormatService.ts | 635 ++++++++++++++ .../formats/AnonCredsProofFormatService.ts | 796 ++++++++++++++++++ packages/anoncreds/src/formats/index.ts | 2 + packages/anoncreds/src/models/exchange.ts | 1 + 5 files changed, 1792 insertions(+) create mode 100644 packages/anoncreds-rs/tests/anoncreds-flow.test.ts create mode 100644 packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts create mode 100644 packages/anoncreds/src/formats/AnonCredsProofFormatService.ts diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts new file mode 100644 index 0000000000..493d80daff --- /dev/null +++ b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts @@ -0,0 +1,358 @@ +import type { AnonCredsCredentialRequest } from '@aries-framework/anoncreds' +import type { Wallet } from '@aries-framework/core' + +import { + AnonCredsModuleConfig, + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, + AnonCredsSchemaRecord, + AnonCredsSchemaRepository, + AnonCredsCredentialDefinitionRepository, + AnonCredsCredentialDefinitionRecord, + AnonCredsCredentialDefinitionPrivateRepository, + AnonCredsCredentialDefinitionPrivateRecord, + AnonCredsKeyCorrectnessProofRepository, + AnonCredsKeyCorrectnessProofRecord, + AnonCredsLinkSecretRepository, + AnonCredsLinkSecretRecord, + AnonCredsProofFormatService, + AnonCredsCredentialFormatService, +} from '@aries-framework/anoncreds' +import { + CredentialState, + CredentialExchangeRecord, + CredentialPreviewAttribute, + InjectionSymbols, + ProofState, + ProofExchangeRecord, +} from '@aries-framework/core' +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../tests/InMemoryStorageService' +import { describeRunInNodeVersion } from '../../../tests/runInVersion' +import { AnonCredsRegistryService } from '../../anoncreds/src/services/registry/AnonCredsRegistryService' +import { InMemoryAnonCredsRegistry } from '../../anoncreds/tests/InMemoryAnonCredsRegistry' +import { agentDependencies, getAgentConfig, getAgentContext } from '../../core/tests/helpers' +import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderService' +import { AnonCredsRsIssuerService } from '../src/services/AnonCredsRsIssuerService' +import { AnonCredsRsVerifierService } from '../src/services/AnonCredsRsVerifierService' + +const registry = new InMemoryAnonCredsRegistry({ useLegacyIdentifiers: false }) +const anonCredsModuleConfig = new AnonCredsModuleConfig({ + registries: [registry], +}) + +const agentConfig = getAgentConfig('AnonCreds format services using anoncreds-rs') +const anonCredsVerifierService = new AnonCredsRsVerifierService() +const anonCredsHolderService = new AnonCredsRsHolderService() +const anonCredsIssuerService = new AnonCredsRsIssuerService() + +const wallet = { generateNonce: () => Promise.resolve('947121108704767252195123') } as Wallet + +const inMemoryStorageService = new InMemoryStorageService() +const agentContext = getAgentContext({ + registerInstances: [ + [InjectionSymbols.Stop$, new Subject()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.StorageService, inMemoryStorageService], + [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], + [AnonCredsHolderServiceSymbol, anonCredsHolderService], + [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + [AnonCredsRegistryService, new AnonCredsRegistryService()], + [AnonCredsModuleConfig, anonCredsModuleConfig], + ], + agentConfig, + wallet, +}) + +const anoncredsCredentialFormatService = new AnonCredsCredentialFormatService() +const anoncredsProofFormatService = new AnonCredsProofFormatService() + +const indyDid = 'did:indy:local:LjgpST2rjsoxYegQDRm7EL' + +// FIXME: Re-include in tests when NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'AnonCreds format services using anoncreds-rs', () => { + test('issuance and verification flow starting from proposal without negotiation and without revocation', async () => { + const schema = await anonCredsIssuerService.createSchema(agentContext, { + attrNames: ['name', 'age'], + issuerId: indyDid, + name: 'Employee Credential', + version: '1.0.0', + }) + + const { schemaState } = await registry.registerSchema(agentContext, { + schema, + options: {}, + }) + + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = + await anonCredsIssuerService.createCredentialDefinition(agentContext, { + issuerId: indyDid, + schemaId: schemaState.schemaId as string, + schema, + tag: 'Employee Credential', + supportRevocation: false, + }) + + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { + credentialDefinition, + options: {}, + }) + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + if (!credentialDefinitionPrivate || !keyCorrectnessProof) { + throw new Error('Failed to get private part of credential definition') + } + + await agentContext.dependencyManager.resolve(AnonCredsSchemaRepository).save( + agentContext, + new AnonCredsSchemaRecord({ + schema: schemaState.schema, + schemaId: schemaState.schemaId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionRepository).save( + agentContext, + new AnonCredsCredentialDefinitionRecord({ + credentialDefinition: credentialDefinitionState.credentialDefinition, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionPrivateRepository).save( + agentContext, + new AnonCredsCredentialDefinitionPrivateRecord({ + value: credentialDefinitionPrivate, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsKeyCorrectnessProofRepository).save( + agentContext, + new AnonCredsKeyCorrectnessProofRecord({ + value: keyCorrectnessProof, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + const linkSecret = await anonCredsHolderService.createLinkSecret(agentContext, { linkSecretId: 'linkSecretId' }) + expect(linkSecret.linkSecretId).toBe('linkSecretId') + + await agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository).save( + agentContext, + new AnonCredsLinkSecretRecord({ + value: linkSecret.linkSecretValue, + linkSecretId: linkSecret.linkSecretId, + }) + ) + + const holderCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalSent, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const issuerCredentialRecord = new CredentialExchangeRecord({ + protocolVersion: 'v1', + state: CredentialState.ProposalReceived, + threadId: 'f365c1a5-2baf-4873-9432-fa87c888a0aa', + }) + + const credentialAttributes = [ + new CredentialPreviewAttribute({ + name: 'name', + value: 'John', + }), + new CredentialPreviewAttribute({ + name: 'age', + value: '25', + }), + ] + + // Holder creates proposal + holderCredentialRecord.credentialAttributes = credentialAttributes + const { attachment: proposalAttachment } = await anoncredsCredentialFormatService.createProposal(agentContext, { + credentialRecord: holderCredentialRecord, + credentialFormats: { + anoncreds: { + attributes: credentialAttributes, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + }, + }) + + // Issuer processes and accepts proposal + await anoncredsCredentialFormatService.processProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: proposalAttachment, + }) + // Set attributes on the credential record, this is normally done by the protocol service + issuerCredentialRecord.credentialAttributes = credentialAttributes + const { attachment: offerAttachment } = await anoncredsCredentialFormatService.acceptProposal(agentContext, { + credentialRecord: issuerCredentialRecord, + proposalAttachment: proposalAttachment, + }) + + // Holder processes and accepts offer + await anoncredsCredentialFormatService.processOffer(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: offerAttachment, + }) + const { attachment: requestAttachment } = await anoncredsCredentialFormatService.acceptOffer(agentContext, { + credentialRecord: holderCredentialRecord, + offerAttachment, + credentialFormats: { + anoncreds: { + linkSecretId: linkSecret.linkSecretId, + }, + }, + }) + + // Make sure the request contains an entropy and does not contain a prover_did field + expect((requestAttachment.getDataAsJson() as AnonCredsCredentialRequest).entropy).toBeDefined() + expect((requestAttachment.getDataAsJson() as AnonCredsCredentialRequest).prover_did).toBeUndefined() + + // Issuer processes and accepts request + await anoncredsCredentialFormatService.processRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + attachment: requestAttachment, + }) + const { attachment: credentialAttachment } = await anoncredsCredentialFormatService.acceptRequest(agentContext, { + credentialRecord: issuerCredentialRecord, + requestAttachment, + offerAttachment, + }) + + // Holder processes and accepts credential + await anoncredsCredentialFormatService.processCredential(agentContext, { + credentialRecord: holderCredentialRecord, + attachment: credentialAttachment, + requestAttachment, + }) + + expect(holderCredentialRecord.credentials).toEqual([ + { credentialRecordType: 'anoncreds', credentialRecordId: expect.any(String) }, + ]) + + const credentialId = holderCredentialRecord.credentials[0].credentialRecordId + const anonCredsCredential = await anonCredsHolderService.getCredential(agentContext, { + credentialId, + }) + + expect(anonCredsCredential).toEqual({ + credentialId, + attributes: { + age: '25', + name: 'John', + }, + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + revocationRegistryId: null, + credentialRevocationId: undefined, // FIXME: should be null? + }) + + expect(holderCredentialRecord.metadata.data).toEqual({ + '_anonCreds/anonCredsCredential': { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + '_anonCreds/anonCredsCredentialRequest': { + master_secret_blinding_data: expect.any(Object), + master_secret_name: expect.any(String), + nonce: expect.any(String), + }, + }) + + expect(issuerCredentialRecord.metadata.data).toEqual({ + '_anonCreds/anonCredsCredential': { + schemaId: schemaState.schemaId, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }, + }) + + const holderProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalSent, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + const verifierProofRecord = new ProofExchangeRecord({ + protocolVersion: 'v1', + state: ProofState.ProposalReceived, + threadId: '4f5659a4-1aea-4f42-8c22-9a9985b35e38', + }) + + const { attachment: proofProposalAttachment } = await anoncredsProofFormatService.createProposal(agentContext, { + proofFormats: { + anoncreds: { + attributes: [ + { + name: 'name', + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + value: 'John', + referent: '1', + }, + ], + predicates: [ + { + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + name: 'age', + predicate: '>=', + threshold: 18, + }, + ], + name: 'Proof Request', + version: '1.0', + }, + }, + proofRecord: holderProofRecord, + }) + + await anoncredsProofFormatService.processProposal(agentContext, { + attachment: proofProposalAttachment, + proofRecord: verifierProofRecord, + }) + + const { attachment: proofRequestAttachment } = await anoncredsProofFormatService.acceptProposal(agentContext, { + proofRecord: verifierProofRecord, + proposalAttachment: proofProposalAttachment, + }) + + await anoncredsProofFormatService.processRequest(agentContext, { + attachment: proofRequestAttachment, + proofRecord: holderProofRecord, + }) + + const { attachment: proofAttachment } = await anoncredsProofFormatService.acceptRequest(agentContext, { + proofRecord: holderProofRecord, + requestAttachment: proofRequestAttachment, + proposalAttachment: proofProposalAttachment, + }) + + const isValid = await anoncredsProofFormatService.processPresentation(agentContext, { + attachment: proofAttachment, + proofRecord: verifierProofRecord, + requestAttachment: proofRequestAttachment, + }) + + expect(isValid).toBe(true) + }) +}) diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts new file mode 100644 index 0000000000..28d7d47185 --- /dev/null +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts @@ -0,0 +1,635 @@ +import type { AnonCredsCredentialFormat, AnonCredsCredentialProposalFormat } from './AnonCredsCredentialFormat' +import type { + AnonCredsCredential, + AnonCredsCredentialOffer, + AnonCredsCredentialRequest, + AnonCredsCredentialRequestMetadata, +} from '../models' +import type { AnonCredsIssuerService, AnonCredsHolderService, GetRevocationRegistryDefinitionReturn } from '../services' +import type { AnonCredsCredentialMetadata } from '../utils/metadata' +import type { + CredentialFormatService, + AgentContext, + CredentialFormatCreateProposalOptions, + CredentialFormatCreateProposalReturn, + CredentialFormatProcessOptions, + CredentialFormatAcceptProposalOptions, + CredentialFormatCreateOfferReturn, + CredentialFormatCreateOfferOptions, + CredentialFormatAcceptOfferOptions, + CredentialFormatCreateReturn, + CredentialFormatAcceptRequestOptions, + CredentialFormatProcessCredentialOptions, + CredentialFormatAutoRespondProposalOptions, + CredentialFormatAutoRespondOfferOptions, + CredentialFormatAutoRespondRequestOptions, + CredentialFormatAutoRespondCredentialOptions, + CredentialExchangeRecord, + CredentialPreviewAttributeOptions, + LinkedAttachment, +} from '@aries-framework/core' + +import { + ProblemReportError, + MessageValidator, + CredentialFormatSpec, + AriesFrameworkError, + Attachment, + JsonEncoder, + utils, + CredentialProblemReportReason, + JsonTransformer, +} from '@aries-framework/core' + +import { AnonCredsError } from '../error' +import { AnonCredsCredentialProposal } from '../models/AnonCredsCredentialProposal' +import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' +import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' +import { + convertAttributesToCredentialValues, + assertCredentialValuesMatch, + checkCredentialValuesMatch, + assertAttributesMatch, + createAndLinkAttachmentsToPreview, +} from '../utils/credential' +import { AnonCredsCredentialMetadataKey, AnonCredsCredentialRequestMetadataKey } from '../utils/metadata' + +const ANONCREDS_CREDENTIAL_OFFER = 'anoncreds/credential-offer@v1.0' +const ANONCREDS_CREDENTIAL_REQUEST = 'anoncreds/credential-request@v1.0' +const ANONCREDS_CREDENTIAL_FILTER = 'anoncreds/credential-filter@v1.0' +const ANONCREDS_CREDENTIAL = 'anoncreds/credential@v1.0' + +export class AnonCredsCredentialFormatService implements CredentialFormatService { + /** formatKey is the key used when calling agent.credentials.xxx with credentialFormats.anoncreds */ + public readonly formatKey = 'anoncreds' as const + + /** + * credentialRecordType is the type of record that stores the credential. It is stored in the credential + * record binding in the credential exchange record. + */ + public readonly credentialRecordType = 'anoncreds' as const + + /** + * Create a {@link AttachmentFormats} object dependent on the message type. + * + * @param options The object containing all the options for the proposed credential + * @returns object containing associated attachment, format and optionally the credential preview + * + */ + public async createProposal( + agentContext: AgentContext, + { credentialFormats, credentialRecord }: CredentialFormatCreateProposalOptions + ): Promise { + const format = new CredentialFormatSpec({ + format: ANONCREDS_CREDENTIAL_FILTER, + }) + + const anoncredsFormat = credentialFormats.anoncreds + + if (!anoncredsFormat) { + throw new AriesFrameworkError('Missing anoncreds payload in createProposal') + } + + // We want all properties except for `attributes` and `linkedAttachments` attributes. + // The easiest way is to destructure and use the spread operator. But that leaves the other properties unused + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { attributes, linkedAttachments, ...anoncredsCredentialProposal } = anoncredsFormat + const proposal = new AnonCredsCredentialProposal(anoncredsCredentialProposal) + + try { + MessageValidator.validateSync(proposal) + } catch (error) { + throw new AriesFrameworkError( + `Invalid proposal supplied: ${anoncredsCredentialProposal} in AnonCredsFormatService` + ) + } + + const attachment = this.getFormatData(JsonTransformer.toJSON(proposal), format.attachmentId) + + const { previewAttributes } = this.getCredentialLinkedAttachments( + anoncredsFormat.attributes, + anoncredsFormat.linkedAttachments + ) + + // Set the metadata + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + schemaId: proposal.schemaId, + credentialDefinitionId: proposal.credentialDefinitionId, + }) + + return { format, attachment, previewAttributes } + } + + public async processProposal( + agentContext: AgentContext, + { attachment }: CredentialFormatProcessOptions + ): Promise { + const proposalJson = attachment.getDataAsJson() + + JsonTransformer.fromJSON(proposalJson, AnonCredsCredentialProposal) + } + + public async acceptProposal( + agentContext: AgentContext, + { + attachmentId, + credentialFormats, + credentialRecord, + proposalAttachment, + }: CredentialFormatAcceptProposalOptions + ): Promise { + const anoncredsFormat = credentialFormats?.anoncreds + + const proposalJson = proposalAttachment.getDataAsJson() + const credentialDefinitionId = anoncredsFormat?.credentialDefinitionId ?? proposalJson.cred_def_id + + const attributes = anoncredsFormat?.attributes ?? credentialRecord.credentialAttributes + + if (!credentialDefinitionId) { + throw new AriesFrameworkError( + 'No credential definition id in proposal or provided as input to accept proposal method.' + ) + } + + if (!attributes) { + throw new AriesFrameworkError('No attributes in proposal or provided as input to accept proposal method.') + } + + const { format, attachment, previewAttributes } = await this.createAnonCredsOffer(agentContext, { + credentialRecord, + attachmentId, + attributes, + credentialDefinitionId, + linkedAttachments: anoncredsFormat?.linkedAttachments, + }) + + return { format, attachment, previewAttributes } + } + + /** + * Create a credential attachment format for a credential request. + * + * @param options The object containing all the options for the credential offer + * @returns object containing associated attachment, formats and offersAttach elements + * + */ + public async createOffer( + agentContext: AgentContext, + { credentialFormats, credentialRecord, attachmentId }: CredentialFormatCreateOfferOptions + ): Promise { + const anoncredsFormat = credentialFormats.anoncreds + + if (!anoncredsFormat) { + throw new AriesFrameworkError('Missing anoncreds credential format data') + } + + const { format, attachment, previewAttributes } = await this.createAnonCredsOffer(agentContext, { + credentialRecord, + attachmentId, + attributes: anoncredsFormat.attributes, + credentialDefinitionId: anoncredsFormat.credentialDefinitionId, + linkedAttachments: anoncredsFormat.linkedAttachments, + }) + + return { format, attachment, previewAttributes } + } + + public async processOffer( + agentContext: AgentContext, + { attachment, credentialRecord }: CredentialFormatProcessOptions + ) { + agentContext.config.logger.debug( + `Processing anoncreds credential offer for credential record ${credentialRecord.id}` + ) + + const credOffer = attachment.getDataAsJson() + + if (!credOffer.schema_id || !credOffer.cred_def_id) { + throw new ProblemReportError('Invalid credential offer', { + problemCode: CredentialProblemReportReason.IssuanceAbandoned, + }) + } + } + + public async acceptOffer( + agentContext: AgentContext, + { + credentialRecord, + attachmentId, + offerAttachment, + credentialFormats, + }: CredentialFormatAcceptOfferOptions + ): Promise { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentialOffer = offerAttachment.getDataAsJson() + + // Get credential definition + const registry = registryService.getRegistryForIdentifier(agentContext, credentialOffer.cred_def_id) + const { credentialDefinition, resolutionMetadata } = await registry.getCredentialDefinition( + agentContext, + credentialOffer.cred_def_id + ) + + if (!credentialDefinition) { + throw new AnonCredsError( + `Unable to retrieve credential definition with id ${credentialOffer.cred_def_id}: ${resolutionMetadata.error} ${resolutionMetadata.message}` + ) + } + + const { credentialRequest, credentialRequestMetadata } = await holderService.createCredentialRequest(agentContext, { + credentialOffer, + credentialDefinition, + linkSecretId: credentialFormats?.anoncreds?.linkSecretId, + }) + + credentialRecord.metadata.set( + AnonCredsCredentialRequestMetadataKey, + credentialRequestMetadata + ) + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + credentialDefinitionId: credentialOffer.cred_def_id, + schemaId: credentialOffer.schema_id, + }) + + const format = new CredentialFormatSpec({ + attachmentId, + format: ANONCREDS_CREDENTIAL_REQUEST, + }) + + const attachment = this.getFormatData(credentialRequest, format.attachmentId) + return { format, attachment } + } + + /** + * Starting from a request is not supported for anoncreds credentials, this method only throws an error. + */ + public async createRequest(): Promise { + throw new AriesFrameworkError('Starting from a request is not supported for anoncreds credentials') + } + + /** + * We don't have any models to validate an anoncreds request object, for now this method does nothing + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async processRequest(agentContext: AgentContext, options: CredentialFormatProcessOptions): Promise { + // not needed for anoncreds + } + + public async acceptRequest( + agentContext: AgentContext, + { + credentialRecord, + attachmentId, + offerAttachment, + requestAttachment, + }: CredentialFormatAcceptRequestOptions + ): Promise { + // Assert credential attributes + const credentialAttributes = credentialRecord.credentialAttributes + if (!credentialAttributes) { + throw new AriesFrameworkError( + `Missing required credential attribute values on credential record with id ${credentialRecord.id}` + ) + } + + const anonCredsIssuerService = + agentContext.dependencyManager.resolve(AnonCredsIssuerServiceSymbol) + + const credentialOffer = offerAttachment?.getDataAsJson() + if (!credentialOffer) throw new AriesFrameworkError('Missing anoncreds credential offer in createCredential') + + const credentialRequest = requestAttachment.getDataAsJson() + if (!credentialRequest) throw new AriesFrameworkError('Missing anoncreds credential request in createCredential') + + const { credential, credentialRevocationId } = await anonCredsIssuerService.createCredential(agentContext, { + credentialOffer, + credentialRequest, + credentialValues: convertAttributesToCredentialValues(credentialAttributes), + }) + + if (credential.rev_reg_id) { + credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { + credentialRevocationId: credentialRevocationId, + revocationRegistryId: credential.rev_reg_id, + }) + credentialRecord.setTags({ + anonCredsRevocationRegistryId: credential.rev_reg_id, + anonCredsCredentialRevocationId: credentialRevocationId, + }) + } + + const format = new CredentialFormatSpec({ + attachmentId, + format: ANONCREDS_CREDENTIAL, + }) + + const attachment = this.getFormatData(credential, format.attachmentId) + return { format, attachment } + } + + /** + * Processes an incoming credential - retrieve metadata, retrieve payload and store it in wallet + * @param options the issue credential message wrapped inside this object + * @param credentialRecord the credential exchange record for this credential + */ + public async processCredential( + agentContext: AgentContext, + { credentialRecord, attachment }: CredentialFormatProcessCredentialOptions + ): Promise { + const credentialRequestMetadata = credentialRecord.metadata.get( + AnonCredsCredentialRequestMetadataKey + ) + + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + if (!credentialRequestMetadata) { + throw new AriesFrameworkError( + `Missing required request metadata for credential exchange with thread id with id ${credentialRecord.id}` + ) + } + + if (!credentialRecord.credentialAttributes) { + throw new AriesFrameworkError( + 'Missing credential attributes on credential record. Unable to check credential attributes' + ) + } + + const anonCredsCredential = attachment.getDataAsJson() + + const credentialDefinitionResult = await registryService + .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) + .getCredentialDefinition(agentContext, anonCredsCredential.cred_def_id) + if (!credentialDefinitionResult.credentialDefinition) { + throw new AriesFrameworkError( + `Unable to resolve credential definition ${anonCredsCredential.cred_def_id}: ${credentialDefinitionResult.resolutionMetadata.error} ${credentialDefinitionResult.resolutionMetadata.message}` + ) + } + + const schemaResult = await registryService + .getRegistryForIdentifier(agentContext, anonCredsCredential.cred_def_id) + .getSchema(agentContext, anonCredsCredential.schema_id) + if (!schemaResult.schema) { + throw new AriesFrameworkError( + `Unable to resolve schema ${anonCredsCredential.schema_id}: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` + ) + } + + // Resolve revocation registry if credential is revocable + let revocationRegistryResult: null | GetRevocationRegistryDefinitionReturn = null + if (anonCredsCredential.rev_reg_id) { + revocationRegistryResult = await registryService + .getRegistryForIdentifier(agentContext, anonCredsCredential.rev_reg_id) + .getRevocationRegistryDefinition(agentContext, anonCredsCredential.rev_reg_id) + + if (!revocationRegistryResult.revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Unable to resolve revocation registry definition ${anonCredsCredential.rev_reg_id}: ${revocationRegistryResult.resolutionMetadata.error} ${revocationRegistryResult.resolutionMetadata.message}` + ) + } + } + + // assert the credential values match the offer values + const recordCredentialValues = convertAttributesToCredentialValues(credentialRecord.credentialAttributes) + assertCredentialValuesMatch(anonCredsCredential.values, recordCredentialValues) + + const credentialId = await anonCredsHolderService.storeCredential(agentContext, { + credentialId: utils.uuid(), + credentialRequestMetadata, + credential: anonCredsCredential, + credentialDefinitionId: credentialDefinitionResult.credentialDefinitionId, + credentialDefinition: credentialDefinitionResult.credentialDefinition, + schema: schemaResult.schema, + revocationRegistry: revocationRegistryResult?.revocationRegistryDefinition + ? { + definition: revocationRegistryResult.revocationRegistryDefinition, + id: revocationRegistryResult.revocationRegistryDefinitionId, + } + : undefined, + }) + + // If the credential is revocable, store the revocation identifiers in the credential record + if (anonCredsCredential.rev_reg_id) { + const credential = await anonCredsHolderService.getCredential(agentContext, { credentialId }) + + credentialRecord.metadata.add(AnonCredsCredentialMetadataKey, { + credentialRevocationId: credential.credentialRevocationId, + revocationRegistryId: credential.revocationRegistryId, + }) + credentialRecord.setTags({ + anonCredsRevocationRegistryId: credential.revocationRegistryId, + anonCredsCredentialRevocationId: credential.credentialRevocationId, + }) + } + + credentialRecord.credentials.push({ + credentialRecordType: this.credentialRecordType, + credentialRecordId: credentialId, + }) + } + + public supportsFormat(format: string): boolean { + const supportedFormats = [ + ANONCREDS_CREDENTIAL_REQUEST, + ANONCREDS_CREDENTIAL_OFFER, + ANONCREDS_CREDENTIAL_FILTER, + ANONCREDS_CREDENTIAL, + ] + + return supportedFormats.includes(format) + } + + /** + * Gets the attachment object for a given attachmentId. We need to get out the correct attachmentId for + * anoncreds and then find the corresponding attachment (if there is one) + * @param formats the formats object containing the attachmentId + * @param messageAttachments the attachments containing the payload + * @returns The Attachment if found or undefined + * + */ + public getAttachment(formats: CredentialFormatSpec[], messageAttachments: Attachment[]): Attachment | undefined { + const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachmentId) + const supportedAttachment = messageAttachments.find((attachment) => supportedAttachmentIds.includes(attachment.id)) + + return supportedAttachment + } + + public async deleteCredentialById(agentContext: AgentContext, credentialRecordId: string): Promise { + const anonCredsHolderService = + agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + await anonCredsHolderService.deleteCredential(agentContext, credentialRecordId) + } + + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondProposalOptions + ) { + const proposalJson = proposalAttachment.getDataAsJson() + const offerJson = offerAttachment.getDataAsJson() + + // We want to make sure the credential definition matches. + // TODO: If no credential definition is present on the proposal, we could check whether the other fields + // of the proposal match with the credential definition id. + return proposalJson.cred_def_id === offerJson.cred_def_id + } + + public async shouldAutoRespondToOffer( + agentContext: AgentContext, + { offerAttachment, proposalAttachment }: CredentialFormatAutoRespondOfferOptions + ) { + const proposalJson = proposalAttachment.getDataAsJson() + const offerJson = offerAttachment.getDataAsJson() + + // We want to make sure the credential definition matches. + // TODO: If no credential definition is present on the proposal, we could check whether the other fields + // of the proposal match with the credential definition id. + return proposalJson.cred_def_id === offerJson.cred_def_id + } + + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + { offerAttachment, requestAttachment }: CredentialFormatAutoRespondRequestOptions + ) { + const credentialOfferJson = offerAttachment.getDataAsJson() + const credentialRequestJson = requestAttachment.getDataAsJson() + + return credentialOfferJson.cred_def_id === credentialRequestJson.cred_def_id + } + + public async shouldAutoRespondToCredential( + agentContext: AgentContext, + { credentialRecord, requestAttachment, credentialAttachment }: CredentialFormatAutoRespondCredentialOptions + ) { + const credentialJson = credentialAttachment.getDataAsJson() + const credentialRequestJson = requestAttachment.getDataAsJson() + + // make sure the credential definition matches + if (credentialJson.cred_def_id !== credentialRequestJson.cred_def_id) return false + + // If we don't have any attributes stored we can't compare so always return false. + if (!credentialRecord.credentialAttributes) return false + const attributeValues = convertAttributesToCredentialValues(credentialRecord.credentialAttributes) + + // check whether the values match the values in the record + return checkCredentialValuesMatch(attributeValues, credentialJson.values) + } + + private async createAnonCredsOffer( + agentContext: AgentContext, + { + credentialRecord, + attachmentId, + credentialDefinitionId, + attributes, + linkedAttachments, + }: { + credentialDefinitionId: string + credentialRecord: CredentialExchangeRecord + attachmentId?: string + attributes: CredentialPreviewAttributeOptions[] + linkedAttachments?: LinkedAttachment[] + } + ): Promise { + const anonCredsIssuerService = + agentContext.dependencyManager.resolve(AnonCredsIssuerServiceSymbol) + + // if the proposal has an attachment Id use that, otherwise the generated id of the formats object + const format = new CredentialFormatSpec({ + attachmentId: attachmentId, + format: ANONCREDS_CREDENTIAL, + }) + + const offer = await anonCredsIssuerService.createCredentialOffer(agentContext, { + credentialDefinitionId, + }) + + const { previewAttributes } = this.getCredentialLinkedAttachments(attributes, linkedAttachments) + if (!previewAttributes) { + throw new AriesFrameworkError('Missing required preview attributes for anoncreds offer') + } + + await this.assertPreviewAttributesMatchSchemaAttributes(agentContext, offer, previewAttributes) + + credentialRecord.metadata.set(AnonCredsCredentialMetadataKey, { + schemaId: offer.schema_id, + credentialDefinitionId: offer.cred_def_id, + }) + + const attachment = this.getFormatData(offer, format.attachmentId) + + return { format, attachment, previewAttributes } + } + + private async assertPreviewAttributesMatchSchemaAttributes( + agentContext: AgentContext, + offer: AnonCredsCredentialOffer, + attributes: CredentialPreviewAttributeOptions[] + ): Promise { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const registry = registryService.getRegistryForIdentifier(agentContext, offer.schema_id) + + const schemaResult = await registry.getSchema(agentContext, offer.schema_id) + + if (!schemaResult.schema) { + throw new AriesFrameworkError( + `Unable to resolve schema ${offer.schema_id} from registry: ${schemaResult.resolutionMetadata.error} ${schemaResult.resolutionMetadata.message}` + ) + } + + assertAttributesMatch(schemaResult.schema, attributes) + } + + /** + * Get linked attachments for anoncreds format from a proposal message. This allows attachments + * to be copied across to old style credential records + * + * @param options ProposeCredentialOptions object containing (optionally) the linked attachments + * @return array of linked attachments or undefined if none present + */ + private getCredentialLinkedAttachments( + attributes?: CredentialPreviewAttributeOptions[], + linkedAttachments?: LinkedAttachment[] + ): { + attachments?: Attachment[] + previewAttributes?: CredentialPreviewAttributeOptions[] + } { + if (!linkedAttachments && !attributes) { + return {} + } + + let previewAttributes = attributes ?? [] + let attachments: Attachment[] | undefined + + if (linkedAttachments) { + // there are linked attachments so transform into the attribute field of the CredentialPreview object for + // this proposal + previewAttributes = createAndLinkAttachmentsToPreview(linkedAttachments, previewAttributes) + attachments = linkedAttachments.map((linkedAttachment) => linkedAttachment.attachment) + } + + return { attachments, previewAttributes } + } + + /** + * Returns an object of type {@link Attachment} for use in credential exchange messages. + * It looks up the correct format identifier and encodes the data as a base64 attachment. + * + * @param data The data to include in the attach object + * @param id the attach id from the formats component of the message + */ + public getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ + id, + mimeType: 'application/json', + data: { + base64: JsonEncoder.toBase64(data), + }, + }) + + return attachment + } +} diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts new file mode 100644 index 0000000000..b8cf7afb64 --- /dev/null +++ b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts @@ -0,0 +1,796 @@ +import type { + AnonCredsProofFormat, + AnonCredsCredentialsForProofRequest, + AnonCredsGetCredentialsForProofRequestOptions, +} from './AnonCredsProofFormat' +import type { + AnonCredsCredentialDefinition, + AnonCredsCredentialInfo, + AnonCredsProof, + AnonCredsRequestedAttribute, + AnonCredsRequestedAttributeMatch, + AnonCredsRequestedPredicate, + AnonCredsRequestedPredicateMatch, + AnonCredsSchema, + AnonCredsSelectedCredentials, + AnonCredsProofRequest, +} from '../models' +import type { + AnonCredsHolderService, + AnonCredsVerifierService, + CreateProofOptions, + GetCredentialsForProofRequestReturn, + VerifyProofOptions, +} from '../services' +import type { + ProofFormatService, + AgentContext, + ProofFormatCreateReturn, + FormatCreateRequestOptions, + ProofFormatCreateProposalOptions, + ProofFormatProcessOptions, + ProofFormatAcceptProposalOptions, + ProofFormatAcceptRequestOptions, + ProofFormatProcessPresentationOptions, + ProofFormatGetCredentialsForRequestOptions, + ProofFormatGetCredentialsForRequestReturn, + ProofFormatSelectCredentialsForRequestOptions, + ProofFormatSelectCredentialsForRequestReturn, + ProofFormatAutoRespondProposalOptions, + ProofFormatAutoRespondRequestOptions, + ProofFormatAutoRespondPresentationOptions, +} from '@aries-framework/core' + +import { + AriesFrameworkError, + Attachment, + AttachmentData, + JsonEncoder, + ProofFormatSpec, + JsonTransformer, +} from '@aries-framework/core' + +import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../models/AnonCredsProofRequest' +import { AnonCredsVerifierServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' +import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' +import { + sortRequestedCredentialsMatches, + createRequestFromPreview, + areAnonCredsProofRequestsEqual, + assertRevocationInterval, + downloadTailsFile, + checkValidCredentialValueEncoding, + encodeCredentialValue, + assertNoDuplicateGroupsNamesInProofRequest, +} from '../utils' + +const ANONCREDS_PRESENTATION_PROPOSAL = 'anoncreds/proof-request@v1.0' +const ANONCREDS_PRESENTATION_REQUEST = 'anoncreds/proof-request@v1.0' +const ANONCREDS_PRESENTATION = 'anoncreds/proof@v1.0' + +export class AnonCredsProofFormatService implements ProofFormatService { + public readonly formatKey = 'anoncreds' as const + + public async createProposal( + agentContext: AgentContext, + { attachmentId, proofFormats }: ProofFormatCreateProposalOptions + ): Promise { + const format = new ProofFormatSpec({ + format: ANONCREDS_PRESENTATION_PROPOSAL, + attachmentId, + }) + + const anoncredsFormat = proofFormats.anoncreds + if (!anoncredsFormat) { + throw Error('Missing anoncreds format to create proposal attachment format') + } + + const proofRequest = createRequestFromPreview({ + attributes: anoncredsFormat.attributes ?? [], + predicates: anoncredsFormat.predicates ?? [], + name: anoncredsFormat.name ?? 'Proof request', + version: anoncredsFormat.version ?? '1.0', + nonce: await agentContext.wallet.generateNonce(), + }) + const attachment = this.getFormatData(proofRequest, format.attachmentId) + + return { attachment, format } + } + + public async processProposal(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const proposalJson = attachment.getDataAsJson() + + // fromJson also validates + JsonTransformer.fromJSON(proposalJson, AnonCredsProofRequestClass) + + // Assert attribute and predicate (group) names do not match + assertNoDuplicateGroupsNamesInProofRequest(proposalJson) + } + + public async acceptProposal( + agentContext: AgentContext, + { proposalAttachment, attachmentId }: ProofFormatAcceptProposalOptions + ): Promise { + const format = new ProofFormatSpec({ + format: ANONCREDS_PRESENTATION_REQUEST, + attachmentId, + }) + + const proposalJson = proposalAttachment.getDataAsJson() + + const request = { + ...proposalJson, + // We never want to reuse the nonce from the proposal, as this will allow replay attacks + nonce: await agentContext.wallet.generateNonce(), + } + + const attachment = this.getFormatData(request, format.attachmentId) + + return { attachment, format } + } + + public async createRequest( + agentContext: AgentContext, + { attachmentId, proofFormats }: FormatCreateRequestOptions + ): Promise { + const format = new ProofFormatSpec({ + format: ANONCREDS_PRESENTATION_REQUEST, + attachmentId, + }) + + const anoncredsFormat = proofFormats.anoncreds + if (!anoncredsFormat) { + throw Error('Missing anoncreds format in create request attachment format') + } + + const request = { + name: anoncredsFormat.name, + version: anoncredsFormat.version, + nonce: await agentContext.wallet.generateNonce(), + requested_attributes: anoncredsFormat.requested_attributes ?? {}, + requested_predicates: anoncredsFormat.requested_predicates ?? {}, + non_revoked: anoncredsFormat.non_revoked, + } satisfies AnonCredsProofRequest + + // Assert attribute and predicate (group) names do not match + assertNoDuplicateGroupsNamesInProofRequest(request) + + const attachment = this.getFormatData(request, format.attachmentId) + + return { attachment, format } + } + + public async processRequest(agentContext: AgentContext, { attachment }: ProofFormatProcessOptions): Promise { + const requestJson = attachment.getDataAsJson() + + // fromJson also validates + JsonTransformer.fromJSON(requestJson, AnonCredsProofRequestClass) + + // Assert attribute and predicate (group) names do not match + assertNoDuplicateGroupsNamesInProofRequest(requestJson) + } + + public async acceptRequest( + agentContext: AgentContext, + { proofFormats, requestAttachment, attachmentId }: ProofFormatAcceptRequestOptions + ): Promise { + const format = new ProofFormatSpec({ + format: ANONCREDS_PRESENTATION, + attachmentId, + }) + const requestJson = requestAttachment.getDataAsJson() + + const anoncredsFormat = proofFormats?.anoncreds + + const selectedCredentials = + anoncredsFormat ?? + (await this._selectCredentialsForRequest(agentContext, requestJson, { + filterByNonRevocationRequirements: true, + })) + + const proof = await this.createProof(agentContext, requestJson, selectedCredentials) + const attachment = this.getFormatData(proof, format.attachmentId) + + return { + attachment, + format, + } + } + + public async processPresentation( + agentContext: AgentContext, + { requestAttachment, attachment }: ProofFormatProcessPresentationOptions + ): Promise { + const verifierService = + agentContext.dependencyManager.resolve(AnonCredsVerifierServiceSymbol) + + const proofRequestJson = requestAttachment.getDataAsJson() + + // NOTE: we don't do validation here, as this is handled by the AnonCreds implementation, however + // this can lead to confusing error messages. We should consider doing validation here as well. + // Defining a class-transformer/class-validator class seems a bit overkill, and the usage of interfaces + // for the anoncreds package keeps things simple. Maybe we can try to use something like zod to validate + const proofJson = attachment.getDataAsJson() + + for (const [referent, attribute] of Object.entries(proofJson.requested_proof.revealed_attrs)) { + if (!checkValidCredentialValueEncoding(attribute.raw, attribute.encoded)) { + throw new AriesFrameworkError( + `The encoded value for '${referent}' is invalid. ` + + `Expected '${encodeCredentialValue(attribute.raw)}'. ` + + `Actual '${attribute.encoded}'` + ) + } + } + + for (const [, attributeGroup] of Object.entries(proofJson.requested_proof.revealed_attr_groups ?? {})) { + for (const [attributeName, attribute] of Object.entries(attributeGroup.values)) { + if (!checkValidCredentialValueEncoding(attribute.raw, attribute.encoded)) { + throw new AriesFrameworkError( + `The encoded value for '${attributeName}' is invalid. ` + + `Expected '${encodeCredentialValue(attribute.raw)}'. ` + + `Actual '${attribute.encoded}'` + ) + } + } + } + + const schemas = await this.getSchemas(agentContext, new Set(proofJson.identifiers.map((i) => i.schema_id))) + const credentialDefinitions = await this.getCredentialDefinitions( + agentContext, + new Set(proofJson.identifiers.map((i) => i.cred_def_id)) + ) + + const revocationRegistries = await this.getRevocationRegistriesForProof(agentContext, proofJson) + + return await verifierService.verifyProof(agentContext, { + proofRequest: proofRequestJson, + proof: proofJson, + schemas, + credentialDefinitions, + revocationRegistries, + }) + } + + public async getCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment, proofFormats }: ProofFormatGetCredentialsForRequestOptions + ): Promise> { + const proofRequestJson = requestAttachment.getDataAsJson() + + // Set default values + const { filterByNonRevocationRequirements = true } = proofFormats?.anoncreds ?? {} + + const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequestJson, { + filterByNonRevocationRequirements, + }) + + return credentialsForRequest + } + + public async selectCredentialsForRequest( + agentContext: AgentContext, + { requestAttachment, proofFormats }: ProofFormatSelectCredentialsForRequestOptions + ): Promise> { + const proofRequestJson = requestAttachment.getDataAsJson() + + // Set default values + const { filterByNonRevocationRequirements = true } = proofFormats?.anoncreds ?? {} + + const selectedCredentials = this._selectCredentialsForRequest(agentContext, proofRequestJson, { + filterByNonRevocationRequirements, + }) + + return selectedCredentials + } + + public async shouldAutoRespondToProposal( + agentContext: AgentContext, + { proposalAttachment, requestAttachment }: ProofFormatAutoRespondProposalOptions + ): Promise { + const proposalJson = proposalAttachment.getDataAsJson() + const requestJson = requestAttachment.getDataAsJson() + + const areRequestsEqual = areAnonCredsProofRequestsEqual(proposalJson, requestJson) + agentContext.config.logger.debug(`AnonCreds request and proposal are are equal: ${areRequestsEqual}`, { + proposalJson, + requestJson, + }) + + return areRequestsEqual + } + + public async shouldAutoRespondToRequest( + agentContext: AgentContext, + { proposalAttachment, requestAttachment }: ProofFormatAutoRespondRequestOptions + ): Promise { + const proposalJson = proposalAttachment.getDataAsJson() + const requestJson = requestAttachment.getDataAsJson() + + return areAnonCredsProofRequestsEqual(proposalJson, requestJson) + } + + public async shouldAutoRespondToPresentation( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _agentContext: AgentContext, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: ProofFormatAutoRespondPresentationOptions + ): Promise { + // The presentation is already verified in processPresentation, so we can just return true here. + // It's only an ack, so it's just that we received the presentation. + return true + } + + public supportsFormat(formatIdentifier: string): boolean { + const supportedFormats = [ANONCREDS_PRESENTATION_PROPOSAL, ANONCREDS_PRESENTATION_REQUEST, ANONCREDS_PRESENTATION] + return supportedFormats.includes(formatIdentifier) + } + + private async _getCredentialsForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + options: AnonCredsGetCredentialsForProofRequestOptions + ): Promise { + const credentialsForProofRequest: AnonCredsCredentialsForProofRequest = { + attributes: {}, + predicates: {}, + } + + for (const [referent, requestedAttribute] of Object.entries(proofRequest.requested_attributes)) { + const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) + + credentialsForProofRequest.attributes[referent] = sortRequestedCredentialsMatches( + await Promise.all( + credentials.map(async (credential) => { + const { isRevoked, timestamp } = await this.getRevocationStatus( + agentContext, + proofRequest, + requestedAttribute, + credential.credentialInfo + ) + + return { + credentialId: credential.credentialInfo.credentialId, + revealed: true, + credentialInfo: credential.credentialInfo, + timestamp, + revoked: isRevoked, + } satisfies AnonCredsRequestedAttributeMatch + }) + ) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter( + (r) => !r.revoked + ) + } + } + + for (const [referent, requestedPredicate] of Object.entries(proofRequest.requested_predicates)) { + const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent) + + credentialsForProofRequest.predicates[referent] = sortRequestedCredentialsMatches( + await Promise.all( + credentials.map(async (credential) => { + const { isRevoked, timestamp } = await this.getRevocationStatus( + agentContext, + proofRequest, + requestedPredicate, + credential.credentialInfo + ) + + return { + credentialId: credential.credentialInfo.credentialId, + credentialInfo: credential.credentialInfo, + timestamp, + revoked: isRevoked, + } satisfies AnonCredsRequestedPredicateMatch + }) + ) + ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (options.filterByNonRevocationRequirements) { + credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter( + (r) => !r.revoked + ) + } + } + + return credentialsForProofRequest + } + + private async _selectCredentialsForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + options: AnonCredsGetCredentialsForProofRequestOptions + ): Promise { + const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, options) + + const selectedCredentials: AnonCredsSelectedCredentials = { + attributes: {}, + predicates: {}, + selfAttestedAttributes: {}, + } + + Object.keys(credentialsForRequest.attributes).forEach((attributeName) => { + const attributeArray = credentialsForRequest.attributes[attributeName] + + if (attributeArray.length === 0) { + throw new AriesFrameworkError('Unable to automatically select requested attributes.') + } + + selectedCredentials.attributes[attributeName] = attributeArray[0] + }) + + Object.keys(credentialsForRequest.predicates).forEach((attributeName) => { + if (credentialsForRequest.predicates[attributeName].length === 0) { + throw new AriesFrameworkError('Unable to automatically select requested predicates.') + } else { + selectedCredentials.predicates[attributeName] = credentialsForRequest.predicates[attributeName][0] + } + }) + + return selectedCredentials + } + + private async getCredentialsForProofRequestReferent( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + attributeReferent: string + ): Promise { + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentials = await holderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent, + }) + + return credentials + } + + /** + * Build schemas object needed to create and verify proof objects. + * + * Creates object with `{ schemaId: AnonCredsSchema }` mapping + * + * @param schemaIds List of schema ids + * @returns Object containing schemas for specified schema ids + * + */ + private async getSchemas(agentContext: AgentContext, schemaIds: Set) { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + + const schemas: { [key: string]: AnonCredsSchema } = {} + + for (const schemaId of schemaIds) { + const schemaRegistry = registryService.getRegistryForIdentifier(agentContext, schemaId) + const schemaResult = await schemaRegistry.getSchema(agentContext, schemaId) + + if (!schemaResult.schema) { + throw new AriesFrameworkError(`Schema not found for id ${schemaId}: ${schemaResult.resolutionMetadata.message}`) + } + + schemas[schemaId] = schemaResult.schema + } + + return schemas + } + + /** + * Build credential definitions object needed to create and verify proof objects. + * + * Creates object with `{ credentialDefinitionId: AnonCredsCredentialDefinition }` mapping + * + * @param credentialDefinitionIds List of credential definition ids + * @returns Object containing credential definitions for specified credential definition ids + * + */ + private async getCredentialDefinitions(agentContext: AgentContext, credentialDefinitionIds: Set) { + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + + const credentialDefinitions: { [key: string]: AnonCredsCredentialDefinition } = {} + + for (const credentialDefinitionId of credentialDefinitionIds) { + const credentialDefinitionRegistry = registryService.getRegistryForIdentifier( + agentContext, + credentialDefinitionId + ) + + const credentialDefinitionResult = await credentialDefinitionRegistry.getCredentialDefinition( + agentContext, + credentialDefinitionId + ) + + if (!credentialDefinitionResult.credentialDefinition) { + throw new AriesFrameworkError( + `Credential definition not found for id ${credentialDefinitionId}: ${credentialDefinitionResult.resolutionMetadata.message}` + ) + } + + credentialDefinitions[credentialDefinitionId] = credentialDefinitionResult.credentialDefinition + } + + return credentialDefinitions + } + + private async getRevocationStatus( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + requestedItem: AnonCredsRequestedAttribute | AnonCredsRequestedPredicate, + credentialInfo: AnonCredsCredentialInfo + ) { + const requestNonRevoked = requestedItem.non_revoked ?? proofRequest.non_revoked + const credentialRevocationId = credentialInfo.credentialRevocationId + const revocationRegistryId = credentialInfo.revocationRegistryId + + // If revocation interval is not present or the credential is not revocable then we + // don't need to fetch the revocation status + if (!requestNonRevoked || !credentialRevocationId || !revocationRegistryId) { + return { isRevoked: undefined, timestamp: undefined } + } + + agentContext.config.logger.trace( + `Fetching credential revocation status for credential revocation id '${credentialRevocationId}' with revocation interval with from '${requestNonRevoked.from}' and to '${requestNonRevoked.to}'` + ) + + // Make sure the revocation interval follows best practices from Aries RFC 0441 + assertRevocationInterval(requestNonRevoked) + + const registryService = agentContext.dependencyManager.resolve(AnonCredsRegistryService) + const registry = registryService.getRegistryForIdentifier(agentContext, revocationRegistryId) + + const revocationStatusResult = await registry.getRevocationStatusList( + agentContext, + revocationRegistryId, + requestNonRevoked.to ?? Date.now() + ) + + if (!revocationStatusResult.revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${revocationStatusResult.resolutionMetadata.message}` + ) + } + + // Item is revoked when the value at the index is 1 + const isRevoked = revocationStatusResult.revocationStatusList.revocationList[parseInt(credentialRevocationId)] === 1 + + agentContext.config.logger.trace( + `Credential with credential revocation index '${credentialRevocationId}' is ${ + isRevoked ? '' : 'not ' + }revoked with revocation interval with to '${requestNonRevoked.to}' & from '${requestNonRevoked.from}'` + ) + + return { + isRevoked, + timestamp: revocationStatusResult.revocationStatusList.timestamp, + } + } + + /** + * Create anoncreds proof from a given proof request and requested credential object. + * + * @param proofRequest The proof request to create the proof for + * @param requestedCredentials The requested credentials object specifying which credentials to use for the proof + * @returns anoncreds proof object + */ + private async createProof( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + selectedCredentials: AnonCredsSelectedCredentials + ): Promise { + const holderService = agentContext.dependencyManager.resolve(AnonCredsHolderServiceSymbol) + + const credentialObjects = await Promise.all( + [...Object.values(selectedCredentials.attributes), ...Object.values(selectedCredentials.predicates)].map( + async (c) => c.credentialInfo ?? holderService.getCredential(agentContext, { credentialId: c.credentialId }) + ) + ) + + const schemas = await this.getSchemas(agentContext, new Set(credentialObjects.map((c) => c.schemaId))) + const credentialDefinitions = await this.getCredentialDefinitions( + agentContext, + new Set(credentialObjects.map((c) => c.credentialDefinitionId)) + ) + + const revocationRegistries = await this.getRevocationRegistriesForRequest( + agentContext, + proofRequest, + selectedCredentials + ) + + return await holderService.createProof(agentContext, { + proofRequest, + selectedCredentials, + schemas, + credentialDefinitions, + revocationRegistries, + }) + } + + private async getRevocationRegistriesForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + selectedCredentials: AnonCredsSelectedCredentials + ) { + const revocationRegistries: CreateProofOptions['revocationRegistries'] = {} + + try { + agentContext.config.logger.debug(`Retrieving revocation registries for proof request`, { + proofRequest, + selectedCredentials, + }) + + const referentCredentials = [] + + // Retrieve information for referents and push to single array + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.attributes)) { + referentCredentials.push({ + referent, + credentialInfo: selectedCredential.credentialInfo, + nonRevoked: proofRequest.requested_attributes[referent].non_revoked ?? proofRequest.non_revoked, + }) + } + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.predicates)) { + referentCredentials.push({ + referent, + credentialInfo: selectedCredential.credentialInfo, + nonRevoked: proofRequest.requested_predicates[referent].non_revoked ?? proofRequest.non_revoked, + }) + } + + for (const { referent, credentialInfo, nonRevoked } of referentCredentials) { + if (!credentialInfo) { + throw new AriesFrameworkError( + `Credential for referent '${referent} does not have credential info for revocation state creation` + ) + } + + // Prefer referent-specific revocation interval over global revocation interval + const credentialRevocationId = credentialInfo.credentialRevocationId + const revocationRegistryId = credentialInfo.revocationRegistryId + + // If revocation interval is present and the credential is revocable then create revocation state + if (nonRevoked && credentialRevocationId && revocationRegistryId) { + agentContext.config.logger.trace( + `Presentation is requesting proof of non revocation for referent '${referent}', creating revocation state for credential`, + { + nonRevoked, + credentialRevocationId, + revocationRegistryId, + } + ) + + // Make sure the revocation interval follows best practices from Aries RFC 0441 + assertRevocationInterval(nonRevoked) + + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, revocationRegistryId) + + // Fetch revocation registry definition if not in revocation registries list yet + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId + ) + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } + + const { tailsLocation, tailsHash } = revocationRegistryDefinition.value + const { tailsFilePath } = await downloadTailsFile(agentContext, tailsLocation, tailsHash) + + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + tailsFilePath, + revocationStatusLists: {}, + } + } + + // TODO: can we check if the revocation status list is already fetched? We don't know which timestamp the query will return. This + // should probably be solved using caching + // Fetch the revocation status list + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, nonRevoked.to ?? Date.now()) + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId].revocationStatusLists[revocationStatusList.timestamp] = + revocationStatusList + } + } + + agentContext.config.logger.debug(`Retrieved revocation registries for proof request`, { + revocationRegistries, + }) + + return revocationRegistries + } catch (error) { + agentContext.config.logger.error(`Error retrieving revocation registry for proof request`, { + error, + proofRequest, + selectedCredentials, + }) + + throw error + } + } + + private async getRevocationRegistriesForProof(agentContext: AgentContext, proof: AnonCredsProof) { + const revocationRegistries: VerifyProofOptions['revocationRegistries'] = {} + + for (const identifier of proof.identifiers) { + const revocationRegistryId = identifier.rev_reg_id + const timestamp = identifier.timestamp + + // Skip if no revocation registry id is present + if (!revocationRegistryId || !timestamp) continue + + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, revocationRegistryId) + + // Fetch revocation registry definition if not already fetched + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId + ) + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + revocationStatusLists: {}, + } + } + + // Fetch revocation status list by timestamp if not already fetched + if (!revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp]) { + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestamp) + + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp] = revocationStatusList + } + } + + return revocationRegistries + } + + /** + * Returns an object of type {@link Attachment} for use in credential exchange messages. + * It looks up the correct format identifier and encodes the data as a base64 attachment. + * + * @param data The data to include in the attach object + * @param id the attach id from the formats component of the message + */ + private getFormatData(data: unknown, id: string): Attachment { + const attachment = new Attachment({ + id, + mimeType: 'application/json', + data: new AttachmentData({ + base64: JsonEncoder.toBase64(data), + }), + }) + + return attachment + } +} diff --git a/packages/anoncreds/src/formats/index.ts b/packages/anoncreds/src/formats/index.ts index 25f0a81917..07f76522ba 100644 --- a/packages/anoncreds/src/formats/index.ts +++ b/packages/anoncreds/src/formats/index.ts @@ -1,7 +1,9 @@ export * from './AnonCredsCredentialFormat' export * from './LegacyIndyCredentialFormat' +export { AnonCredsCredentialFormatService } from './AnonCredsCredentialFormatService' export { LegacyIndyCredentialFormatService } from './LegacyIndyCredentialFormatService' export * from './AnonCredsProofFormat' export * from './LegacyIndyProofFormat' +export { AnonCredsProofFormatService } from './AnonCredsProofFormatService' export { LegacyIndyProofFormatService } from './LegacyIndyProofFormatService' diff --git a/packages/anoncreds/src/models/exchange.ts b/packages/anoncreds/src/models/exchange.ts index 82c76119c2..0e0ae355c9 100644 --- a/packages/anoncreds/src/models/exchange.ts +++ b/packages/anoncreds/src/models/exchange.ts @@ -34,6 +34,7 @@ export interface AnonCredsCredentialOffer { export interface AnonCredsCredentialRequest { // prover_did is deprecated, however it is kept for backwards compatibility with legacy anoncreds implementations prover_did?: string + entropy?: string cred_def_id: string blinded_ms: Record blinded_ms_correctness_proof: Record From 4a6b99c617de06edbaf1cb07c8adfa8de9b3ec15 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Sat, 18 Mar 2023 19:25:55 +0100 Subject: [PATCH 092/139] feat: indy sdk aries askar migration script (#1289) Signed-off-by: blu3beri --- packages/askar/package.json | 4 +- .../indy-sdk-to-askar-migration/README.md | 31 ++ .../jest.config.ts | 14 + .../indy-sdk-to-askar-migration/package.json | 40 ++ .../src/IndySdkToAskarMigrationUpdater.ts | 443 ++++++++++++++++++ .../errors/IndySdkToAskarMigrationError.ts | 6 + .../indy-sdk-to-askar-migration/src/index.ts | 1 + .../indy-sdk-to-askar-migration/src/utils.ts | 40 ++ .../tests/migrate.test.ts | 120 +++++ .../tests/setup.ts | 1 + .../tsconfig.build.json | 8 + .../indy-sdk-to-askar-migration/tsconfig.json | 6 + yarn.lock | 18 +- 13 files changed, 721 insertions(+), 11 deletions(-) create mode 100644 packages/indy-sdk-to-askar-migration/README.md create mode 100644 packages/indy-sdk-to-askar-migration/jest.config.ts create mode 100644 packages/indy-sdk-to-askar-migration/package.json create mode 100644 packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts create mode 100644 packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts create mode 100644 packages/indy-sdk-to-askar-migration/src/index.ts create mode 100644 packages/indy-sdk-to-askar-migration/src/utils.ts create mode 100644 packages/indy-sdk-to-askar-migration/tests/migrate.test.ts create mode 100644 packages/indy-sdk-to-askar-migration/tests/setup.ts create mode 100644 packages/indy-sdk-to-askar-migration/tsconfig.build.json create mode 100644 packages/indy-sdk-to-askar-migration/tsconfig.json diff --git a/packages/askar/package.json b/packages/askar/package.json index dceb761c97..e30054715f 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.4", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.5", "bn.js": "^5.2.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", @@ -34,7 +34,7 @@ }, "devDependencies": { "@types/bn.js": "^5.1.0", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.4", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.5", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/indy-sdk-to-askar-migration/README.md b/packages/indy-sdk-to-askar-migration/README.md new file mode 100644 index 0000000000..866e4180b0 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/README.md @@ -0,0 +1,31 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript Indy SDK To Askar Migration Module

+

+ License + typescript + @aries-framework/indy-sdk-to-askar-migration version + +

+
+ +Indy SDK to Askar migration module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript.git). diff --git a/packages/indy-sdk-to-askar-migration/jest.config.ts b/packages/indy-sdk-to-askar-migration/jest.config.ts new file mode 100644 index 0000000000..55c67d70a6 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + name: packageJson.name, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json new file mode 100644 index 0000000000..87373300f3 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -0,0 +1,40 @@ +{ + "name": "@aries-framework/indy-sdk-to-askar-migration", + "main": "build/index", + "types": "build/index", + "version": "0.3.3", + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/indy-sdk-to-askar-migration", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/indy-sdk-to-askar-migration" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/anoncreds": "^0.3.3", + "@aries-framework/askar": "^0.3.3", + "@aries-framework/core": "^0.3.3", + "@aries-framework/indy-sdk": "^0.3.3", + "@aries-framework/node": "^0.3.3", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.5" + }, + "devDependencies": { + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.5", + "indy-sdk": "^1.16.0-dev-1655", + "rimraf": "^4.0.7", + "typescript": "~4.9.4" + } +} diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts new file mode 100644 index 0000000000..d57b506154 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -0,0 +1,443 @@ +import type { AnonCredsCredentialValue } from '@aries-framework/anoncreds' +import type { Agent, FileSystem, WalletConfig } from '@aries-framework/core' +import type { EntryObject } from '@hyperledger/aries-askar-shared' + +import { AnonCredsCredentialRecord, AnonCredsLinkSecretRecord } from '@aries-framework/anoncreds' +import { AskarWallet } from '@aries-framework/askar' +import { InjectionSymbols, KeyDerivationMethod, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' +import { Migration, Key, KeyAlgs, Store, StoreKeyMethod } from '@hyperledger/aries-askar-shared' + +import { IndySdkToAskarMigrationError } from './errors/IndySdkToAskarMigrationError' +import { transformFromRecordTagValues } from './utils' + +/** + * + * Migration class to move a wallet form the indy-sdk structure to the new + * askar wallet structure. + * + * Right now, this is ONLY supported within React Native environments AND only sqlite. + * + * The reason it only works within React Native is that we ONLY update the + * keys, masterSecret and credentials for now. If you have an agent in Node.JS + * where it only contains these records, it may be used but we cannot + * guarantee a successful migration. + * + */ +export class IndySdkToAskarMigrationUpdater { + private store?: Store + private walletConfig: WalletConfig + private defaultLinkSecretId: string + private agent: Agent + private dbPath: string + private fs: FileSystem + private deleteOnFinish: boolean + + private constructor( + walletConfig: WalletConfig, + agent: Agent, + dbPath: string, + deleteOnFinish = false, + defaultLinkSecretId?: string + ) { + this.walletConfig = walletConfig + this.dbPath = dbPath + this.agent = agent + this.fs = this.agent.dependencyManager.resolve(InjectionSymbols.FileSystem) + this.defaultLinkSecretId = defaultLinkSecretId ?? walletConfig.id + this.deleteOnFinish = deleteOnFinish + } + + public static async initialize({ + dbPath, + agent, + deleteOnFinish, + defaultLinkSecretId, + }: { + dbPath: string + agent: Agent + deleteOnFinish?: boolean + defaultLinkSecretId?: string + }) { + const { + config: { walletConfig }, + } = agent + if (typeof process?.versions?.node !== 'undefined') { + agent.config.logger.warn( + 'Node.JS is not fully supported. Using this will likely leave the wallet in a half-migrated state' + ) + } + + if (!walletConfig) { + throw new IndySdkToAskarMigrationError('Wallet config is required for updating the wallet') + } + + if (walletConfig.storage && walletConfig.storage.type !== 'sqlite') { + throw new IndySdkToAskarMigrationError('Only sqlite wallets are supported, right now') + } + + if (agent.isInitialized) { + throw new IndySdkToAskarMigrationError('Wallet migration can not be done on an initialized agent') + } + + if (!(agent.dependencyManager.resolve(InjectionSymbols.Wallet) instanceof AskarWallet)) { + throw new IndySdkToAskarMigrationError("Wallet on the agent must be of instance 'AskarWallet'") + } + + return new IndySdkToAskarMigrationUpdater(walletConfig, agent, dbPath, deleteOnFinish, defaultLinkSecretId) + } + + /** + * This function migrates the old database to the new structure. + * + * This doubles checks some fields as later it might be possiblt to run this function + */ + private async migrate() { + const specUri = this.dbPath + const kdfLevel = this.walletConfig.keyDerivationMethod ?? 'ARGON2I_MOD' + const walletName = this.walletConfig.id + const walletKey = this.walletConfig.key + const storageType = this.walletConfig.storage?.type ?? 'sqlite' + + if (storageType !== 'sqlite') { + throw new IndySdkToAskarMigrationError("Storage type defined and not of type 'sqlite'") + } + + if (!walletKey) { + throw new IndySdkToAskarMigrationError('Wallet key is not defined in the wallet configuration') + } + + this.agent.config.logger.info('Migration indy-sdk database structure to askar') + await Migration.migrate({ specUri, walletKey, kdfLevel, walletName }) + } + + /* + * Checks whether the destination locations are allready used. This might + * happen if you want to migrate a wallet when you already have a new wallet + * with the same id. + */ + private async assertDestinationsAreFree() { + const areAllDestinationsTaken = + (await this.fs.exists(this.backupFile)) || (await this.fs.exists(this.newWalletPath)) + + if (areAllDestinationsTaken) { + throw new IndySdkToAskarMigrationError( + `Files already exist at paths that will be used for backing up. Please remove them manually. Backup path: '${this.backupFile}' and new wallet path: ${this.newWalletPath} ` + ) + } + } + + /** + * Location of the new wallet + */ + private get newWalletPath() { + return `${this.fs.dataPath}/wallet/${this.walletConfig.id}/sqlite.db` + } + + /** + * Temporary backup location of the pre-migrated script + */ + private get backupFile() { + return `${this.fs.tempPath}/${this.walletConfig.id}.bak.db` + } + + /** + * Backup the database file. This function makes sure that the the indy-sdk + * database file is backed up within our temporary directory path. If some + * error occurs, `this.revertDatbase()` will be called to revert the backup. + */ + private async backupDatabase() { + const src = this.dbPath + const dest = this.backupFile + this.agent.config.logger.trace(`Creating backup from '${src}' to '${dest}'`) + + // Create the directories for the backup + await this.fs.createDirectory(dest) + + // Copy the supplied database to the backup destination + await this.fs.copyFile(src, dest) + + if (!(await this.fs.exists(dest))) { + throw new IndySdkToAskarMigrationError('Could not locate the new backup file') + } + } + + /** + * Reverts backed up database file to the original path, if its missing, and + * deletes the backup. We do some additional, possible redundant, exists checks + * here to be extra sure that only a happy flow occurs. + */ + private async restoreDatabase() { + // "Impossible" state. Since we do not continue if `this.backupDatabase()` + // fails, this file should always be there. If this error is thrown, we + // cannot correctly restore the state. + if (!(await this.fs.exists(this.backupFile))) { + throw new IndySdkToAskarMigrationError('Backup file could not be found while trying to restore the state') + } + + /** + * Since we used `copy` to get the file, it should still be there. We + * double-check here to be sure. + */ + if (!(await this.fs.exists(this.dbPath))) { + return + } else { + this.agent.config.logger.trace(`Moving '${this.backupFile}' back to the original path: '${this.dbPath}`) + + // Move the backedup file back to the original path + await this.fs.copyFile(this.backupFile, this.dbPath) + + this.agent.config.logger.trace(`Cleaned up the backed up file at '${this.backupFile}'`) + } + } + + // Delete the backup as `this.fs.copyFile` only copies and no deletion + // Since we use `tempPath` which is cleared when certain events happen, + // e.g. cron-job and system restart (depending on the os) we could omit + // this call `await this.fs.delete(this.backupFile)`. + private async cleanBackup() { + this.agent.config.logger.trace(`Deleting the backup file at '${this.backupFile}'`) + await this.fs.delete(this.backupFile) + } + + /** + * Move the migrated and updated database file to the new location according + * to the `FileSystem.dataPath`. + */ + private async moveToNewLocation() { + const src = this.dbPath + // New path for the database + const dest = this.newWalletPath + + // create the wallet directory + await this.fs.createDirectory(dest) + + this.agent.config.logger.trace(`Moving upgraded database from ${src} to ${dest}`) + + // Copy the file from the database path to the new location + await this.fs.copyFile(src, dest) + + // Delete the original, only if specified by the user + if (this.deleteOnFinish) await this.fs.delete(this.dbPath) + } + + /** + * Function that updates the values from an indy-sdk structure to the new askar structure. + * + * - Assert that the paths that will be used are free + * - Create a backup of the database + * - Migrate the database to askar structure + * - Update the Keys + * - Update the Master Secret (Link Secret) + * - Update the credentials + * If any of those failed: + * - Revert the database + * - Clear the backup from the temporary directory + */ + public async update() { + await this.assertDestinationsAreFree() + + await this.backupDatabase() + try { + // Migrate the database + await this.migrate() + + const keyMethod = + this.walletConfig?.keyDerivationMethod == KeyDerivationMethod.Raw ? StoreKeyMethod.Raw : StoreKeyMethod.Kdf + this.store = await Store.open({ uri: `sqlite://${this.dbPath}`, passKey: this.walletConfig.key, keyMethod }) + + // Update the values to reflect the new structure + await this.updateKeys() + await this.updateCredentialDefinitions() + await this.updateMasterSecret() + await this.updateCredentials() + + // Move the migrated and updated file to the expected location for afj + await this.moveToNewLocation() + } catch (err) { + this.agent.config.logger.error('Migration failed. Restoring state.') + + await this.restoreDatabase() + + throw new IndySdkToAskarMigrationError(`Migration failed. State has been restored. ${err.message}`, { + cause: err.cause, + }) + } finally { + await this.cleanBackup() + } + } + + private async updateKeys() { + if (!this.store) { + throw new IndySdkToAskarMigrationError('Update keys can not be called outside of the `update()` function') + } + + const category = 'Indy::Key' + + this.agent.config.logger.info(`Migrating category: ${category}`) + + let updateCount = 0 + const session = this.store.transaction() + for (;;) { + const txn = await session.open() + const keys = await txn.fetchAll({ category, limit: 50 }) + if (!keys || keys.length === 0) { + await txn.close() + break + } + + for (const row of keys) { + this.agent.config.logger.debug(`Migrating ${row.name} to the new askar format`) + const signKey: string = JSON.parse(row.value as string).signkey + const keySk = TypedArrayEncoder.fromBase58(signKey) + const key = Key.fromSecretBytes({ + algorithm: KeyAlgs.Ed25519, + secretKey: keySk.subarray(0, 32), + }) + await txn.insertKey({ name: row.name, key }) + + await txn.remove({ category, name: row.name }) + key.handle.free() + updateCount++ + } + await txn.commit() + } + + this.agent.config.logger.info(`Migrated ${updateCount} records of type ${category}`) + } + + private async updateCredentialDefinitions() { + if (!this.store) { + throw new IndySdkToAskarMigrationError('Update keys can not be called outside of the `update()` function') + } + + const category = 'Indy::CredentialDefinition' + + this.agent.config.logger.info(`Migrating category: ${category}`) + + const session = this.store.transaction() + for (;;) { + const txn = await session.open() + const keys = await txn.fetchAll({ category, limit: 50 }) + if (!keys || keys.length === 0) { + await txn.close() + break + } else { + // This will be entered if there are credential definitions in the wallet + await txn.close() + throw new IndySdkToAskarMigrationError('Migration of Credential Definitions is not yet supported') + } + } + } + + private async updateMasterSecret() { + if (!this.store) { + throw new IndySdkToAskarMigrationError( + 'Update master secret can not be called outside of the `update()` function' + ) + } + + const category = 'Indy::MasterSecret' + + this.agent.config.logger.info(`Migrating category: ${category}`) + + let updateCount = 0 + const session = this.store.transaction() + + for (;;) { + const txn = await session.open() + const masterSecrets = await txn.fetchAll({ category, limit: 50 }) + if (!masterSecrets || masterSecrets.length === 0) { + await txn.close() + break + } + + if (!masterSecrets.some((ms: EntryObject) => ms.name === this.defaultLinkSecretId)) { + throw new IndySdkToAskarMigrationError('defaultLinkSecretId can not be established.') + } + + this.agent.config.logger.info(`Default link secret id for migration is ${this.defaultLinkSecretId}`) + + for (const row of masterSecrets) { + this.agent.config.logger.debug(`Migrating ${row.name} to the new askar format`) + + const isDefault = masterSecrets.length === 0 ?? row.name === this.walletConfig.id + + const { + value: { ms }, + } = JSON.parse(row.value as string) as { value: { ms: string } } + + const record = new AnonCredsLinkSecretRecord({ linkSecretId: row.name, value: ms }) + record.setTag('isDefault', isDefault) + const value = JsonTransformer.serialize(record) + + const tags = transformFromRecordTagValues(record.getTags()) + + await txn.insert({ category: record.type, name: record.id, value, tags }) + + await txn.remove({ category, name: row.name }) + updateCount++ + } + await txn.commit() + } + + this.agent.config.logger.info(`Migrated ${updateCount} records of type ${category}`) + } + + private async updateCredentials() { + if (!this.store) { + throw new IndySdkToAskarMigrationError('Update credentials can not be called outside of the `update()` function') + } + + const category = 'Indy::Credential' + + this.agent.config.logger.info(`Migrating category: ${category}`) + + let updateCount = 0 + const session = this.store.transaction() + for (;;) { + const txn = await session.open() + const credentials = await txn.fetchAll({ category, limit: 50 }) + if (!credentials || credentials.length === 0) { + await txn.close() + break + } + + for (const row of credentials) { + this.agent.config.logger.debug(`Migrating ${row.name} to the new askar format`) + const data = JSON.parse(row.value as string) as { + schema_id: string + cred_def_id: string + rev_reg_id?: string + values: Record + signature: Record + signature_correctness_proof: Record + rev_reg?: Record + witness?: Record + } + const [issuerId] = data.cred_def_id.split(':') + const [schemaIssuerId, , schemaName, schemaVersion] = data.schema_id.split(':') + + const record = new AnonCredsCredentialRecord({ + credential: data, + issuerId, + schemaName, + schemaIssuerId, + schemaVersion, + credentialId: row.name, + linkSecretId: this.defaultLinkSecretId, + }) + + const tags = transformFromRecordTagValues(record.getTags()) + const value = JsonTransformer.serialize(record) + + await txn.insert({ category: record.type, name: record.id, value, tags }) + + await txn.remove({ category, name: row.name }) + updateCount++ + } + await txn.commit() + } + + this.agent.config.logger.info(`Migrated ${updateCount} records of type ${category}`) + } +} diff --git a/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts b/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts new file mode 100644 index 0000000000..4621d3969c --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/src/errors/IndySdkToAskarMigrationError.ts @@ -0,0 +1,6 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +/** + * @internal + */ +export class IndySdkToAskarMigrationError extends AriesFrameworkError {} diff --git a/packages/indy-sdk-to-askar-migration/src/index.ts b/packages/indy-sdk-to-askar-migration/src/index.ts new file mode 100644 index 0000000000..daac1c7b49 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/src/index.ts @@ -0,0 +1 @@ +export { IndySdkToAskarMigrationUpdater } from './IndySdkToAskarMigrationUpdater' diff --git a/packages/indy-sdk-to-askar-migration/src/utils.ts b/packages/indy-sdk-to-askar-migration/src/utils.ts new file mode 100644 index 0000000000..74bccccec8 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/src/utils.ts @@ -0,0 +1,40 @@ +import type { TagsBase } from '@aries-framework/core' + +/** + * Adopted from `AskarStorageService` implementation and should be kept in sync. + */ +export const transformFromRecordTagValues = (tags: TagsBase): { [key: string]: string | undefined } => { + const transformedTags: { [key: string]: string | undefined } = {} + + for (const [key, value] of Object.entries(tags)) { + // If the value is of type null we use the value undefined + // Askar doesn't support null as a value + if (value === null) { + transformedTags[key] = undefined + } + // If the value is a boolean use the Askar + // '1' or '0' syntax + else if (typeof value === 'boolean') { + transformedTags[key] = value ? '1' : '0' + } + // If the value is 1 or 0, we need to add something to the value, otherwise + // the next time we deserialize the tag values it will be converted to boolean + else if (value === '1' || value === '0') { + transformedTags[key] = `n__${value}` + } + // If the value is an array we create a tag for each array + // item ("tagName:arrayItem" = "1") + else if (Array.isArray(value)) { + value.forEach((item) => { + const tagName = `${key}:${item}` + transformedTags[tagName] = '1' + }) + } + // Otherwise just use the value + else { + transformedTags[key] = value + } + } + + return transformedTags +} diff --git a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts new file mode 100644 index 0000000000..5d8b6f64ab --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts @@ -0,0 +1,120 @@ +import type { InitConfig } from '@aries-framework/core' + +import { AskarModule } from '@aries-framework/askar' +import { utils, KeyDerivationMethod, Agent } from '@aries-framework/core' +import { IndySdkModule } from '@aries-framework/indy-sdk' +import { agentDependencies } from '@aries-framework/node' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { registerAriesAskar } from '@hyperledger/aries-askar-shared' +import indy from 'indy-sdk' +import { homedir } from 'os' + +import { describeRunInNodeVersion } from '../../../tests/runInVersion' +import { IndySdkToAskarMigrationUpdater } from '../src' +import { IndySdkToAskarMigrationError } from '../src/errors/IndySdkToAskarMigrationError' + +// FIXME: Re-include in tests when NodeJS wrapper performance is improved +describeRunInNodeVersion([18], 'Indy SDK To Askar Migration', () => { + const config: InitConfig = { + label: 'test-agent', + walletConfig: { + id: `walletwallet.0-${utils.uuid()}`, + key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', + keyDerivationMethod: KeyDerivationMethod.Raw, + }, + } + + const invalidConfig: InitConfig = { + label: 'invalid-test-agent', + walletConfig: { + id: `walletwallet.1-${utils.uuid()}`, + key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', + keyDerivationMethod: KeyDerivationMethod.Raw, + }, + } + + const invalidAgent = new Agent({ + config: invalidConfig, + modules: { + indySdk: new IndySdkModule({ indySdk: indy }), + }, + dependencies: agentDependencies, + }) + + const invalidNewAgent = new Agent({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + config: { ...invalidConfig, walletConfig: { ...invalidConfig.walletConfig!, key: 'wrong-key' } }, + modules: { askar: new AskarModule() }, + dependencies: agentDependencies, + }) + + const oldAgent = new Agent({ + config, + modules: { + indySdk: new IndySdkModule({ indySdk: indy }), + }, + dependencies: agentDependencies, + }) + + const newAgent = new Agent({ + config, + modules: { askar: new AskarModule() }, + dependencies: agentDependencies, + }) + + const oldAgentDbPath = `${homedir()}/.indy_client/wallet/${oldAgent.config.walletConfig?.id}/sqlite.db` + const invalidAgentDbPath = `${homedir()}/.indy_client/wallet/${invalidAgent.config.walletConfig?.id}/sqlite.db` + + beforeAll(() => { + registerAriesAskar({ askar: ariesAskar }) + }) + + test('indy-sdk sqlite to aries-askar sqlite', async () => { + const genericRecordContent = { foo: 'bar' } + + await oldAgent.initialize() + + const record = await oldAgent.genericRecords.save({ content: genericRecordContent }) + + await oldAgent.shutdown() + + const updater = await IndySdkToAskarMigrationUpdater.initialize({ dbPath: oldAgentDbPath, agent: newAgent }) + await updater.update() + + await newAgent.initialize() + + await expect(newAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ content: genericRecordContent }) + + await newAgent.shutdown() + }) + + /* + * - Initialize an agent + * - Save a generic record + * - try to migrate with invalid state (wrong key) + * - Migration will be attempted, fails, and restores + * - Check if the record can still be accessed + */ + test('indy-sdk sqlite to aries-askar sqlite fails and restores', async () => { + const genericRecordContent = { foo: 'bar' } + + await invalidAgent.initialize() + + const record = await invalidAgent.genericRecords.save({ content: genericRecordContent }) + + await invalidAgent.shutdown() + + const updater = await IndySdkToAskarMigrationUpdater.initialize({ + dbPath: invalidAgentDbPath, + agent: invalidNewAgent, + }) + + await expect(updater.update()).rejects.toThrowError(IndySdkToAskarMigrationError) + + await invalidAgent.initialize() + + await expect(invalidAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ + content: genericRecordContent, + }) + }) +}) diff --git a/packages/indy-sdk-to-askar-migration/tests/setup.ts b/packages/indy-sdk-to-askar-migration/tests/setup.ts new file mode 100644 index 0000000000..226f7031fa --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/tests/setup.ts @@ -0,0 +1 @@ +jest.setTimeout(20000) diff --git a/packages/indy-sdk-to-askar-migration/tsconfig.build.json b/packages/indy-sdk-to-askar-migration/tsconfig.build.json new file mode 100644 index 0000000000..2b075bbd85 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build", + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/packages/indy-sdk-to-askar-migration/tsconfig.json b/packages/indy-sdk-to-askar-migration/tsconfig.json new file mode 100644 index 0000000000..46efe6f721 --- /dev/null +++ b/packages/indy-sdk-to-askar-migration/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"] + } +} diff --git a/yarn.lock b/yarn.lock index ab6d703c0a..c2bba8a78d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -882,12 +882,12 @@ resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.10.tgz#9d67f64e98ff41971644c95b03dabafd741df4df" integrity sha512-POvcwQrUcPrwoZehQa38pN1dnjyeUlrQ6VlksbBRS8SUHJuyixZsD+d3XoumqaNfl9Z1DCjfuOgEiPlec01gXQ== -"@hyperledger/aries-askar-nodejs@^0.1.0-dev.4": - version "0.1.0-dev.4" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.4.tgz#016f89886732366eff9cba54eb6fcbf02cc5a212" - integrity sha512-Wh1SoxakBpQvgbFrLq+NIJ0l02N8SjBRDZBs/c55gIeenXzDJY/ZgfM6faLdDv4XeE6agXd4yl35f4s7+3zK+Q== +"@hyperledger/aries-askar-nodejs@^0.1.0-dev.5": + version "0.1.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.5.tgz#602db32f49dd1f9e7f83d378a9813d2bde5886f8" + integrity sha512-38lXtmnhhca+s14V3zTefAyZGIg6nRpKi4Emnr1q5hQXmn1WZn7/ybYJbcI/VHoAPNLA+DydSI99U5KuPkrC1w== dependencies: - "@hyperledger/aries-askar-shared" "0.1.0-dev.4" + "@hyperledger/aries-askar-shared" "0.1.0-dev.5" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" node-cache "^5.1.2" @@ -895,10 +895,10 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.1.0-dev.4", "@hyperledger/aries-askar-shared@^0.1.0-dev.4": - version "0.1.0-dev.4" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.4.tgz#439fbaa3911be56c134cbacfddd1553eb0b06dde" - integrity sha512-z+bWVbFD3S7IuYlG2XTxCjyaJWmS/wiHtYRxgWXjF6o4iR2vW+/y0NhTpiX2gO4Gx62zxFfh50b0oQTOM6/XqQ== +"@hyperledger/aries-askar-shared@0.1.0-dev.5", "@hyperledger/aries-askar-shared@^0.1.0-dev.5": + version "0.1.0-dev.5" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.5.tgz#06ae815b873693b7b483004d027059d50f24fb60" + integrity sha512-nufXqMslytelijC0uXBTuaj9OHjHo5mJe0hABKfYJqe9p1RYmmKlnqi+B/mjWrr2R8yG8NLrNnHbwPcBArLrPA== dependencies: fast-text-encoding "^1.0.3" From 2efc0097138585391940fbb2eb504e50df57ec87 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 18 Mar 2023 22:17:47 +0100 Subject: [PATCH 093/139] feat(anoncreds): add getCredential(s) methods (#1386) Signed-off-by: Timo Glastra --- .../src/services/AnonCredsRsHolderService.ts | 28 ++++++++++ .../AnonCredsRsHolderService.test.ts | 51 +++++++++++++++++++ packages/anoncreds/src/AnonCredsApi.ts | 9 ++++ .../src/services/AnonCredsHolderService.ts | 2 + .../services/AnonCredsHolderServiceOptions.ts | 9 ++++ .../services/IndySdkHolderService.ts | 23 +++++++++ 6 files changed, 122 insertions(+) diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index 052c1d7495..e7b9e0068f 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -17,6 +17,7 @@ import type { AnonCredsRequestedPredicateMatch, AnonCredsCredentialRequest, AnonCredsCredentialRequestMetadata, + GetCredentialsOptions, } from '@aries-framework/anoncreds' import type { AgentContext, Query, SimpleQuery } from '@aries-framework/core' import type { @@ -306,6 +307,33 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { } } + public async getCredentials( + agentContext: AgentContext, + options: GetCredentialsOptions + ): Promise { + const credentialRecords = await agentContext.dependencyManager + .resolve(AnonCredsCredentialRepository) + .findByQuery(agentContext, { + credentialDefinitionId: options.credentialDefinitionId, + schemaId: options.schemaId, + issuerId: options.issuerId, + schemaName: options.schemaName, + schemaVersion: options.schemaVersion, + schemaIssuerId: options.schemaIssuerId, + }) + + return credentialRecords.map((credentialRecord) => ({ + attributes: Object.fromEntries( + Object.entries(credentialRecord.credential.values).map(([key, value]) => [key, value.raw]) + ), + credentialDefinitionId: credentialRecord.credential.cred_def_id, + credentialId: credentialRecord.credentialId, + schemaId: credentialRecord.credential.schema_id, + credentialRevocationId: credentialRecord.credentialRevocationId, + revocationRegistryId: credentialRecord.credential.rev_reg_id, + })) + } + public async deleteCredential(agentContext: AgentContext, credentialId: string): Promise { const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) const credentialRecord = await credentialRepository.getByCredentialId(agentContext, credentialId) diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index 203818a159..c8044e74df 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -60,6 +60,7 @@ const agentContext = getAgentContext({ // FIXME: Re-include in tests when NodeJS wrapper performance is improved describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { const getByCredentialIdMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'getByCredentialId') + const findByQueryMock = jest.spyOn(anoncredsCredentialRepositoryMock, 'findByQuery') beforeEach(() => { getByCredentialIdMock.mockClear() @@ -433,6 +434,56 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { }) }) + test('getCredentials', async () => { + findByQueryMock.mockResolvedValueOnce([ + new AnonCredsCredentialRecord({ + credential: { + cred_def_id: 'credDefId', + schema_id: 'schemaId', + signature: 'signature', + signature_correctness_proof: 'signatureCorrectnessProof', + values: { attr1: { raw: 'value1', encoded: 'encvalue1' }, attr2: { raw: 'value2', encoded: 'encvalue2' } }, + rev_reg_id: 'revRegId', + } as AnonCredsCredential, + credentialId: 'myCredentialId', + credentialRevocationId: 'credentialRevocationId', + linkSecretId: 'linkSecretId', + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + }), + ]) + + const credentialInfo = await anonCredsHolderService.getCredentials(agentContext, { + credentialDefinitionId: 'credDefId', + schemaId: 'schemaId', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + issuerId: 'issuerDid', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + credentialDefinitionId: 'credDefId', + schemaId: 'schemaId', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + issuerId: 'issuerDid', + }) + expect(credentialInfo).toMatchObject([ + { + attributes: { attr1: 'value1', attr2: 'value2' }, + credentialDefinitionId: 'credDefId', + credentialId: 'myCredentialId', + revocationRegistryId: 'revRegId', + schemaId: 'schemaId', + credentialRevocationId: 'credentialRevocationId', + }, + ]) + }) + test('storeCredential', async () => { const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = createCredentialDefinition({ attributeNames: ['name', 'age', 'sex', 'height'], diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index 9e56a51ea5..77333e1afd 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -10,6 +10,7 @@ import type { RegisterCredentialDefinitionReturn, RegisterSchemaOptions, RegisterSchemaReturn, + GetCredentialsOptions, } from './services' import type { Extensible } from './services/registry/base' @@ -347,6 +348,14 @@ export class AnonCredsApi { } } + public async getCredential(credentialId: string) { + return this.anonCredsHolderService.getCredential(this.agentContext, { credentialId }) + } + + public async getCredentials(options: GetCredentialsOptions) { + return this.anonCredsHolderService.getCredentials(this.agentContext, options) + } + private async storeCredentialDefinitionRecord( result: RegisterCredentialDefinitionReturn, credentialDefinitionPrivate?: Record, diff --git a/packages/anoncreds/src/services/AnonCredsHolderService.ts b/packages/anoncreds/src/services/AnonCredsHolderService.ts index 85e51ce529..47cefac8e3 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderService.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderService.ts @@ -8,6 +8,7 @@ import type { GetCredentialsForProofRequestReturn, CreateLinkSecretReturn, CreateLinkSecretOptions, + GetCredentialsOptions, } from './AnonCredsHolderServiceOptions' import type { AnonCredsCredentialInfo } from '../models' import type { AnonCredsProof } from '../models/exchange' @@ -29,6 +30,7 @@ export interface AnonCredsHolderService { // We could come up with a hack (as we've received the credential at one point), but for // now I think it's not that much of an issue getCredential(agentContext: AgentContext, options: GetCredentialOptions): Promise + getCredentials(agentContext: AgentContext, options: GetCredentialsOptions): Promise createCredentialRequest( agentContext: AgentContext, diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index bc044f5ef4..e3a677e27d 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -60,6 +60,15 @@ export interface GetCredentialOptions { credentialId: string } +export interface GetCredentialsOptions { + credentialDefinitionId: string + schemaId: string + schemaIssuerId: string + schemaName: string + schemaVersion: string + issuerId: string +} + // TODO: Maybe we can make this a bit more specific? export type WalletQuery = Record export interface ReferentWalletQuery { diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index d4e7861e87..4d804f8e99 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -13,6 +13,7 @@ import type { AnonCredsCredentialRequestMetadata, CreateLinkSecretOptions, CreateLinkSecretReturn, + GetCredentialsOptions, } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' import type { @@ -205,6 +206,28 @@ export class IndySdkHolderService implements AnonCredsHolderService { } } + public async getCredentials(agentContext: AgentContext, options: GetCredentialsOptions) { + assertIndySdkWallet(agentContext.wallet) + + const credentials = await this.indySdk.proverGetCredentials(agentContext.wallet.handle, { + cred_def_id: options.credentialDefinitionId, + schema_id: options.schemaId, + schema_issuer_did: options.schemaIssuerId, + schema_name: options.schemaName, + schema_version: options.schemaVersion, + issuer_did: options.issuerId, + }) + + return credentials.map((credential) => ({ + credentialDefinitionId: credential.cred_def_id, + attributes: credential.attrs, + credentialId: credential.referent, + schemaId: credential.schema_id, + credentialRevocationId: credential.cred_rev_id, + revocationRegistryId: credential.rev_reg_id, + })) + } + public async createCredentialRequest( agentContext: AgentContext, options: CreateCredentialRequestOptions From 555999686a831e6988564fd5c9c937fc1023f567 Mon Sep 17 00:00:00 2001 From: Victor Anene <62852943+Vickysomtee@users.noreply.github.com> Date: Sun, 19 Mar 2023 17:52:52 +0100 Subject: [PATCH 094/139] feat(anoncreds): support credential attribute value and marker (#1369) Signed-off-by: Victor Anene --- packages/anoncreds-rs/package.json | 1 + .../src/services/AnonCredsRsHolderService.ts | 97 ++++++++++++------- .../AnonCredsRsHolderService.test.ts | 68 +++++++++++-- packages/anoncreds-rs/tests/setup.ts | 1 + .../src/models/AnonCredsRestrictionWrapper.ts | 11 +++ packages/anoncreds/src/models/index.ts | 1 + .../repository/AnonCredsCredentialRecord.ts | 16 ++- .../AnonCredsCredentialRecord.test.ts | 43 ++++++++ tests/InMemoryStorageService.ts | 83 ++++++++++------ 9 files changed, 241 insertions(+), 80 deletions(-) create mode 100644 packages/anoncreds/src/models/AnonCredsRestrictionWrapper.ts create mode 100644 packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index cbb80f2623..a8a5b3032c 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -34,6 +34,7 @@ }, "devDependencies": { "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.10", + "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" } diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index e7b9e0068f..93b1ed2b41 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -1,23 +1,23 @@ import type { + AnonCredsCredential, + AnonCredsCredentialInfo, + AnonCredsCredentialRequest, + AnonCredsCredentialRequestMetadata, AnonCredsHolderService, AnonCredsProof, + AnonCredsProofRequestRestriction, + AnonCredsRequestedAttributeMatch, + AnonCredsRequestedPredicateMatch, CreateCredentialRequestOptions, CreateCredentialRequestReturn, + CreateLinkSecretOptions, + CreateLinkSecretReturn, CreateProofOptions, GetCredentialOptions, - StoreCredentialOptions, GetCredentialsForProofRequestOptions, GetCredentialsForProofRequestReturn, - AnonCredsCredentialInfo, - CreateLinkSecretOptions, - CreateLinkSecretReturn, - AnonCredsProofRequestRestriction, - AnonCredsCredential, - AnonCredsRequestedAttributeMatch, - AnonCredsRequestedPredicateMatch, - AnonCredsCredentialRequest, - AnonCredsCredentialRequestMetadata, GetCredentialsOptions, + StoreCredentialOptions, } from '@aries-framework/anoncreds' import type { AgentContext, Query, SimpleQuery } from '@aries-framework/core' import type { @@ -29,13 +29,13 @@ import type { import { AnonCredsCredentialRecord, - AnonCredsLinkSecretRepository, AnonCredsCredentialRepository, + AnonCredsLinkSecretRepository, + AnonCredsRestrictionWrapper, legacyIndyCredentialDefinitionIdRegex, } from '@aries-framework/anoncreds' -import { TypedArrayEncoder, AriesFrameworkError, utils, injectable } from '@aries-framework/core' +import { AriesFrameworkError, JsonTransformer, TypedArrayEncoder, injectable, utils } from '@aries-framework/core' import { - anoncreds, Credential, CredentialRequest, CredentialRevocationState, @@ -43,6 +43,7 @@ import { Presentation, RevocationRegistryDefinition, RevocationStatusList, + anoncreds, } from '@hyperledger/anoncreds-shared' import { AnonCredsRsError } from '../errors/AnonCredsRsError' @@ -353,21 +354,35 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { if (!requestedAttribute) { throw new AnonCredsRsError(`Referent not found in proof request`) } - const attributes = requestedAttribute.name ? [requestedAttribute.name] : requestedAttribute.names - const restrictionQuery = requestedAttribute.restrictions - ? this.queryFromRestrictions(requestedAttribute.restrictions) - : undefined + const $and = [] - const query: Query = { - attributes, - ...restrictionQuery, - ...options.extraQuery, + // Make sure the attribute(s) that are requested are present using the marker tag + const attributes = requestedAttribute.names ?? [requestedAttribute.name] + const attributeQuery: SimpleQuery = {} + for (const attribute of attributes) { + attributeQuery[`attr::${attribute}::marker`] = true + } + $and.push(attributeQuery) + + // Add query for proof request restrictions + if (requestedAttribute.restrictions) { + const restrictionQuery = this.queryFromRestrictions(requestedAttribute.restrictions) + $and.push(restrictionQuery) + } + + // Add extra query + // TODO: we're not really typing the extraQuery, and it will work differently based on the anoncreds implmentation + // We should make the allowed properties more strict + if (options.extraQuery) { + $and.push(options.extraQuery) } const credentials = await agentContext.dependencyManager .resolve(AnonCredsCredentialRepository) - .findByQuery(agentContext, query) + .findByQuery(agentContext, { + $and, + }) return credentials.map((credentialRecord) => { const attributes: { [key: string]: string } = {} @@ -391,35 +406,43 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { private queryFromRestrictions(restrictions: AnonCredsProofRequestRestriction[]) { const query: Query[] = [] - for (const restriction of restrictions) { + const { restrictions: parsedRestrictions } = JsonTransformer.fromJSON({ restrictions }, AnonCredsRestrictionWrapper) + + for (const restriction of parsedRestrictions) { const queryElements: SimpleQuery = {} - if (restriction.cred_def_id) { - queryElements.credentialDefinitionId = restriction.cred_def_id + if (restriction.credentialDefinitionId) { + queryElements.credentialDefinitionId = restriction.credentialDefinitionId + } + + if (restriction.issuerId || restriction.issuerDid) { + queryElements.issuerId = restriction.issuerId ?? restriction.issuerDid } - if (restriction.issuer_id || restriction.issuer_did) { - queryElements.issuerId = restriction.issuer_id ?? restriction.issuer_did + if (restriction.schemaId) { + queryElements.schemaId = restriction.schemaId } - if (restriction.rev_reg_id) { - queryElements.revocationRegistryId = restriction.rev_reg_id + if (restriction.schemaIssuerId || restriction.schemaIssuerDid) { + queryElements.schemaIssuerId = restriction.schemaIssuerId ?? restriction.issuerDid } - if (restriction.schema_id) { - queryElements.schemaId = restriction.schema_id + if (restriction.schemaName) { + queryElements.schemaName = restriction.schemaName } - if (restriction.schema_issuer_id || restriction.schema_issuer_did) { - queryElements.schemaIssuerId = restriction.schema_issuer_id ?? restriction.schema_issuer_did + if (restriction.schemaVersion) { + queryElements.schemaVersion = restriction.schemaVersion } - if (restriction.schema_name) { - queryElements.schemaName = restriction.schema_name + for (const [attributeName, attributeValue] of Object.entries(restriction.attributeValues)) { + queryElements[`attr::${attributeName}::value`] = attributeValue } - if (restriction.schema_version) { - queryElements.schemaVersion = restriction.schema_version + for (const [attributeName, isAvailable] of Object.entries(restriction.attributeMarkers)) { + if (isAvailable) { + queryElements[`attr::${attributeName}::marker`] = isAvailable + } } query.push(queryElements) diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index c8044e74df..87bdcf30f8 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -292,6 +292,10 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { names: ['name', 'height'], restrictions: [{ cred_def_id: 'crededefid:uri', issuer_id: 'issuerid:uri' }], }, + attr5_referent: { + name: 'name', + restrictions: [{ 'attr::name::value': 'Alice', 'attr::name::marker': '1' }], + }, }, requested_predicates: { predicate1_referent: { name: 'age', p_type: '>=' as const, p_value: 18 }, @@ -322,8 +326,14 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { }) expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { - attributes: ['name'], - issuerId: 'issuer:uri', + $and: [ + { + 'attr::name::marker': true, + }, + { + issuerId: 'issuer:uri', + }, + ], }) }) @@ -334,7 +344,11 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { }) expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { - attributes: ['phoneNumber'], + $and: [ + { + 'attr::phoneNumber::marker': true, + }, + ], }) }) @@ -345,8 +359,14 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { }) expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { - attributes: ['age'], - $or: [{ schemaId: 'schemaid:uri', schemaName: 'schemaName' }, { schemaVersion: '1.0' }], + $and: [ + { + 'attr::age::marker': true, + }, + { + $or: [{ schemaId: 'schemaid:uri', schemaName: 'schemaName' }, { schemaVersion: '1.0' }], + }, + ], }) }) @@ -357,9 +377,35 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { }) expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { - attributes: ['name', 'height'], - credentialDefinitionId: 'crededefid:uri', - issuerId: 'issuerid:uri', + $and: [ + { + 'attr::name::marker': true, + 'attr::height::marker': true, + }, + { + credentialDefinitionId: 'crededefid:uri', + issuerId: 'issuerid:uri', + }, + ], + }) + }) + + test('referent with attribute values and marker restriction', async () => { + await anonCredsHolderService.getCredentialsForProofRequest(agentContext, { + proofRequest, + attributeReferent: 'attr5_referent', + }) + + expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { + $and: [ + { + 'attr::name::marker': true, + }, + { + 'attr::name::value': 'Alice', + 'attr::name::marker': true, + }, + ], }) }) @@ -370,7 +416,11 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { }) expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { - attributes: ['age'], + $and: [ + { + 'attr::age::marker': true, + }, + ], }) }) }) diff --git a/packages/anoncreds-rs/tests/setup.ts b/packages/anoncreds-rs/tests/setup.ts index 0254a395ff..4760c40357 100644 --- a/packages/anoncreds-rs/tests/setup.ts +++ b/packages/anoncreds-rs/tests/setup.ts @@ -1,3 +1,4 @@ import '@hyperledger/anoncreds-nodejs' +import 'reflect-metadata' jest.setTimeout(120000) diff --git a/packages/anoncreds/src/models/AnonCredsRestrictionWrapper.ts b/packages/anoncreds/src/models/AnonCredsRestrictionWrapper.ts new file mode 100644 index 0000000000..a701c9e6ec --- /dev/null +++ b/packages/anoncreds/src/models/AnonCredsRestrictionWrapper.ts @@ -0,0 +1,11 @@ +import { Type } from 'class-transformer' +import { ValidateNested } from 'class-validator' + +import { AnonCredsRestrictionTransformer, AnonCredsRestriction } from './AnonCredsRestriction' + +export class AnonCredsRestrictionWrapper { + @ValidateNested({ each: true }) + @Type(() => AnonCredsRestriction) + @AnonCredsRestrictionTransformer() + public restrictions!: AnonCredsRestriction[] +} diff --git a/packages/anoncreds/src/models/index.ts b/packages/anoncreds/src/models/index.ts index 6dd1a6e3bb..3ad7724723 100644 --- a/packages/anoncreds/src/models/index.ts +++ b/packages/anoncreds/src/models/index.ts @@ -1,3 +1,4 @@ export * from './internal' export * from './exchange' export * from './registry' +export * from './AnonCredsRestrictionWrapper' diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts index 3d4d0958b7..7515dd09c2 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts @@ -1,4 +1,5 @@ import type { AnonCredsCredential } from '../models' +import type { Tags } from '@aries-framework/core' import { BaseRecord, utils } from '@aries-framework/core' @@ -21,7 +22,10 @@ export type DefaultAnonCredsCredentialTags = { credentialRevocationId?: string revocationRegistryId?: string schemaId: string - attributes: string[] + + // the following keys can be used for every `attribute name` in credential. + [key: `attr::${string}::marker`]: true | undefined + [key: `attr::${string}::value`]: string | undefined } export type CustomAnonCredsCredentialTags = { @@ -62,7 +66,7 @@ export class AnonCredsCredentialRecord extends BaseRecord< } public getTags() { - return { + const tags: Tags = { ...this._tags, credentialDefinitionId: this.credential.cred_def_id, schemaId: this.credential.schema_id, @@ -70,7 +74,13 @@ export class AnonCredsCredentialRecord extends BaseRecord< credentialRevocationId: this.credentialRevocationId, revocationRegistryId: this.credential.rev_reg_id, linkSecretId: this.linkSecretId, - attributes: Object.keys(this.credential.values), } + + for (const [key, value] of Object.entries(this.credential.values)) { + tags[`attr::${key}::value`] = value.raw + tags[`attr::${key}::marker`] = true + } + + return tags } } diff --git a/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts b/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts new file mode 100644 index 0000000000..feb80ada78 --- /dev/null +++ b/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts @@ -0,0 +1,43 @@ +import type { AnonCredsCredential } from '@aries-framework/anoncreds' + +import { AnonCredsCredentialRecord } from '../AnonCredsCredentialRecord' + +describe('AnoncredsCredentialRecords', () => { + test('Returns the correct tags from the getTags methods based on the credential record values', () => { + const anoncredsCredentialRecords = new AnonCredsCredentialRecord({ + credential: { + cred_def_id: 'credDefId', + schema_id: 'schemaId', + signature: 'signature', + signature_correctness_proof: 'signatureCorrectnessProof', + values: { attr1: { raw: 'value1', encoded: 'encvalue1' }, attr2: { raw: 'value2', encoded: 'encvalue2' } }, + rev_reg_id: 'revRegId', + } as AnonCredsCredential, + credentialId: 'myCredentialId', + credentialRevocationId: 'credentialRevocationId', + linkSecretId: 'linkSecretId', + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + }) + + const tags = anoncredsCredentialRecords.getTags() + + expect(tags).toMatchObject({ + issuerId: 'issuerDid', + schemaIssuerId: 'schemaIssuerDid', + schemaName: 'schemaName', + schemaVersion: 'schemaVersion', + credentialDefinitionId: 'credDefId', + schemaId: 'schemaId', + credentialId: 'myCredentialId', + credentialRevocationId: 'credentialRevocationId', + linkSecretId: 'linkSecretId', + 'attr::attr1::value': 'value1', + 'attr::attr1::marker': true, + 'attr::attr2::value': 'value2', + 'attr::attr2::marker': true, + }) + }) +}) diff --git a/tests/InMemoryStorageService.ts b/tests/InMemoryStorageService.ts index 339080b404..c5ba42820f 100644 --- a/tests/InMemoryStorageService.ts +++ b/tests/InMemoryStorageService.ts @@ -2,13 +2,7 @@ import type { AgentContext } from '../packages/core/src/agent' import type { BaseRecord, TagsBase } from '../packages/core/src/storage/BaseRecord' import type { StorageService, BaseRecordConstructor, Query } from '../packages/core/src/storage/StorageService' -import { - RecordNotFoundError, - RecordDuplicateError, - JsonTransformer, - AriesFrameworkError, - injectable, -} from '@aries-framework/core' +import { RecordNotFoundError, RecordDuplicateError, JsonTransformer, injectable } from '@aries-framework/core' interface StorageRecord { value: Record @@ -17,14 +11,18 @@ interface StorageRecord { id: string } +interface InMemoryRecords { + [id: string]: StorageRecord +} + @injectable() // eslint-disable-next-line @typescript-eslint/no-explicit-any export class InMemoryStorageService = BaseRecord> implements StorageService { - public records: { [id: string]: StorageRecord } + public records: InMemoryRecords - public constructor(records: { [id: string]: StorageRecord } = {}) { + public constructor(records: InMemoryRecords = {}) { this.records = records } @@ -127,32 +125,55 @@ export class InMemoryStorageService = BaseRe recordClass: BaseRecordConstructor, query: Query ): Promise { - if (query.$and || query.$or || query.$not) { - throw new AriesFrameworkError( - 'Advanced wallet query features $and, $or or $not not supported in in memory storage' - ) - } - const records = Object.values(this.records) .filter((record) => record.type === recordClass.type) - .filter((record) => { - const tags = record.tags as TagsBase - - for (const [key, value] of Object.entries(query)) { - if (Array.isArray(value)) { - const tagValue = tags[key] - if (!Array.isArray(tagValue) || !value.every((v) => tagValue.includes(v))) { - return false - } - } else if (tags[key] !== value) { - return false - } - } - - return true - }) + .filter((record) => filterByQuery(record, query)) .map((record) => this.recordToInstance(record, recordClass)) return records } } + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function filterByQuery>(record: StorageRecord, query: Query) { + const { $and, $or, $not, ...restQuery } = query + + if ($not) { + throw new Error('$not query not supported in in memory storage') + } + + // Top level query + if (!matchSimpleQuery(record, restQuery)) return false + + // All $and queries MUST match + if ($and) { + const allAndMatch = ($and as Query[]).every((and) => filterByQuery(record, and)) + if (!allAndMatch) return false + } + + // Only one $or queries has to match + if ($or) { + const oneOrMatch = ($or as Query[]).some((or) => filterByQuery(record, or)) + if (!oneOrMatch) return false + } + + return true +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function matchSimpleQuery>(record: StorageRecord, query: Query) { + const tags = record.tags as TagsBase + + for (const [key, value] of Object.entries(query)) { + if (Array.isArray(value)) { + const tagValue = tags[key] + if (!Array.isArray(tagValue) || !value.every((v) => tagValue.includes(v))) { + return false + } + } else if (tags[key] !== value) { + return false + } + } + + return true +} From 343ce6a264482806c640fa88ce84cf0ff69f97b2 Mon Sep 17 00:00:00 2001 From: Victor Anene <62852943+Vickysomtee@users.noreply.github.com> Date: Sun, 19 Mar 2023 19:33:27 +0100 Subject: [PATCH 095/139] refactor: require native shared components implementation (#1349) Signed-off-by: Victor Anene --- demo/package.json | 5 ++ demo/src/BaseAgent.ts | 12 +++- packages/anoncreds-rs/package.json | 4 +- .../anoncreds-rs/src/AnonCredsRsModule.ts | 19 +++--- .../src/AnonCredsRsModuleConfig.ts | 58 +++++++++++++++++ packages/anoncreds-rs/src/types.ts | 4 -- packages/anoncreds/package.json | 1 + .../anoncreds/tests/legacyAnonCredsSetup.ts | 23 +++---- packages/askar/package.json | 4 +- packages/askar/src/AskarModule.ts | 19 +++--- packages/askar/src/AskarModuleConfig.ts | 54 ++++++++++++++++ .../__tests__/AskarStorageService.test.ts | 5 +- packages/askar/src/types.ts | 3 - packages/askar/tests/helpers.ts | 8 ++- packages/askar/tests/setup.ts | 8 +-- .../routing/__tests__/mediation.test.ts | 7 +++ .../indy-sdk-to-askar-migration/package.json | 4 +- .../tests/migrate.test.ts | 12 +++- packages/indy-vdr/package.json | 4 +- packages/indy-vdr/src/IndyVdrModule.ts | 11 ---- packages/indy-vdr/src/IndyVdrModuleConfig.ts | 44 ++++++++++++- .../src/__tests__/IndyVdrModule.test.ts | 3 + .../src/__tests__/IndyVdrModuleConfig.test.ts | 3 + packages/indy-vdr/tests/helpers.ts | 2 + .../indy-vdr-anoncreds-registry.e2e.test.ts | 2 + .../tests/indy-vdr-did-registrar.e2e.test.ts | 2 + .../indy-vdr-indy-did-resolver.e2e.test.ts | 2 + .../indy-vdr-sov-did-resolver.e2e.test.ts | 2 + packages/indy-vdr/tests/setup.ts | 5 +- yarn.lock | 62 +++++++++++-------- 30 files changed, 282 insertions(+), 110 deletions(-) create mode 100644 packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts delete mode 100644 packages/anoncreds-rs/src/types.ts create mode 100644 packages/askar/src/AskarModuleConfig.ts delete mode 100644 packages/askar/src/types.ts diff --git a/demo/package.json b/demo/package.json index 247a4513cd..289273b81d 100644 --- a/demo/package.json +++ b/demo/package.json @@ -13,6 +13,11 @@ "faber": "ts-node src/FaberInquirer.ts", "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, + "dependencies": { + "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6" + }, "devDependencies": { "@aries-framework/anoncreds": "*", "@aries-framework/anoncreds-rs": "*", diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index f06d0016fe..18f51f8c78 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -25,6 +25,9 @@ import { import { IndySdkAnonCredsRegistry, IndySdkModule, IndySdkSovDidResolver } from '@aries-framework/indy-sdk' import { IndyVdrAnonCredsRegistry, IndyVdrModule, IndyVdrSovDidResolver } from '@aries-framework/indy-vdr' import { agentDependencies, HttpInboundTransport } from '@aries-framework/node' +import { anoncreds } from '@hyperledger/anoncreds-nodejs' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { randomUUID } from 'crypto' import indySdk from 'indy-sdk' @@ -125,14 +128,19 @@ function getAskarAnonCredsIndyModules() { anoncreds: new AnonCredsModule({ registries: [new IndyVdrAnonCredsRegistry()], }), - anoncredsRs: new AnonCredsRsModule(), + anoncredsRs: new AnonCredsRsModule({ + anoncreds, + }), indyVdr: new IndyVdrModule({ + indyVdr, networks: [indyNetworkConfig], }), dids: new DidsModule({ resolvers: [new IndyVdrSovDidResolver()], }), - askar: new AskarModule(), + askar: new AskarModule({ + ariesAskar, + }), } as const } diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index a8a5b3032c..c6ab13dfd5 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.10", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.11", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.10", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/anoncreds-rs/src/AnonCredsRsModule.ts b/packages/anoncreds-rs/src/AnonCredsRsModule.ts index 4ceb7b8304..cca3f465fd 100644 --- a/packages/anoncreds-rs/src/AnonCredsRsModule.ts +++ b/packages/anoncreds-rs/src/AnonCredsRsModule.ts @@ -1,3 +1,4 @@ +import type { AnonCredsRsModuleConfigOptions } from './AnonCredsRsModuleConfig' import type { DependencyManager, Module } from '@aries-framework/core' import { @@ -6,20 +7,18 @@ import { AnonCredsVerifierServiceSymbol, } from '@aries-framework/anoncreds' +import { AnonCredsRsModuleConfig } from './AnonCredsRsModuleConfig' import { AnonCredsRsHolderService, AnonCredsRsIssuerService, AnonCredsRsVerifierService } from './services' export class AnonCredsRsModule implements Module { + public readonly config: AnonCredsRsModuleConfig + + public constructor(config: AnonCredsRsModuleConfigOptions) { + this.config = new AnonCredsRsModuleConfig(config) + } + public register(dependencyManager: DependencyManager) { - try { - // eslint-disable-next-line import/no-extraneous-dependencies - require('@hyperledger/anoncreds-nodejs') - } catch (error) { - try { - require('@hyperledger/anoncreds-react-native') - } catch (error) { - throw new Error('Could not load anoncreds bindings') - } - } + dependencyManager.registerInstance(AnonCredsRsModuleConfig, this.config) // Register services dependencyManager.registerSingleton(AnonCredsHolderServiceSymbol, AnonCredsRsHolderService) diff --git a/packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts b/packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts new file mode 100644 index 0000000000..2d676b4d52 --- /dev/null +++ b/packages/anoncreds-rs/src/AnonCredsRsModuleConfig.ts @@ -0,0 +1,58 @@ +import type { Anoncreds } from '@hyperledger/anoncreds-shared' + +/** + * @public + * AnonCredsRsModuleConfigOptions defines the interface for the options of the AnonCredsRsModuleConfig class. + */ +export interface AnonCredsRsModuleConfigOptions { + /** + * + * ## Node.JS + * + * ```ts + * import { anoncreds } from '@hyperledger/anoncreds-nodejs' + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * anoncredsRs: new AnoncredsRsModule({ + * anoncreds, + * }) + * } + * }) + * ``` + * + * ## React Native + * + * ```ts + * import { anoncreds } from '@hyperledger/anoncreds-react-native' + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * anoncredsRs: new AnoncredsRsModule({ + * anoncreds, + * }) + * } + * }) + * ``` + */ + anoncreds: Anoncreds +} + +/** + * @public + */ +export class AnonCredsRsModuleConfig { + private options: AnonCredsRsModuleConfigOptions + + public constructor(options: AnonCredsRsModuleConfigOptions) { + this.options = options + } + + public get anoncreds() { + return this.options.anoncreds + } +} diff --git a/packages/anoncreds-rs/src/types.ts b/packages/anoncreds-rs/src/types.ts deleted file mode 100644 index 2694976be7..0000000000 --- a/packages/anoncreds-rs/src/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { Anoncreds } from '@hyperledger/anoncreds-shared' - -export const AnonCredsRsSymbol = Symbol('AnonCredsRs') -export type { Anoncreds } diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index ed2353d6db..9f1ab9d6e2 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -31,6 +31,7 @@ }, "devDependencies": { "@aries-framework/node": "0.3.3", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.0.7", "rxjs": "^7.8.0", diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index d0e40a33a6..d0f408b069 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -27,21 +27,19 @@ import { V2ProofProtocol, DidsModule, } from '@aries-framework/core' +import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { randomUUID } from 'crypto' import { AnonCredsRsModule } from '../../anoncreds-rs/src' import { AskarModule } from '../../askar/src' +import { askarModuleConfig } from '../../askar/tests/helpers' import { sleep } from '../../core/src/utils/sleep' -import { uuid } from '../../core/src/utils/uuid' import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests' import { getAgentOptions, importExistingIndyDidFromPrivateKey, makeConnection, publicDidSeed, - genesisTransactions, - taaVersion, - taaAcceptanceMechanism, waitForCredentialRecordSubject, waitForProofExchangeRecordSubject, } from '../../core/tests/helpers' @@ -67,6 +65,7 @@ import { IndyVdrIndyDidResolver, IndyVdrIndyDidRegistrar, } from '../../indy-vdr/src' +import { indyVdrModuleConfig } from '../../indy-vdr/tests/helpers' import { V1CredentialProtocol, V1ProofProtocol, @@ -130,14 +129,6 @@ export const getAskarAnonCredsIndyModules = ({ const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() const legacyIndyProofFormatService = new LegacyIndyProofFormatService() - const indyNetworkConfig = { - id: `localhost-${uuid()}`, - isProduction: false, - genesisTransactions, - indyNamespace: 'pool:localtest', - transactionAuthorAgreement: { version: taaVersion, acceptanceMechanism: taaAcceptanceMechanism }, - } - const modules = { credentials: new CredentialsModule({ autoAcceptCredentials, @@ -164,15 +155,15 @@ export const getAskarAnonCredsIndyModules = ({ anoncreds: new AnonCredsModule({ registries: [new IndyVdrAnonCredsRegistry()], }), - anoncredsRs: new AnonCredsRsModule(), - indyVdr: new IndyVdrModule({ - networks: [indyNetworkConfig], + anoncredsRs: new AnonCredsRsModule({ + anoncreds, }), + indyVdr: new IndyVdrModule(indyVdrModuleConfig), dids: new DidsModule({ resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()], registrars: [new IndyVdrIndyDidRegistrar()], }), - askar: new AskarModule(), + askar: new AskarModule(askarModuleConfig), cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 100 }), }), diff --git a/packages/askar/package.json b/packages/askar/package.json index e30054715f..57cd37143f 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.5", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.6", "bn.js": "^5.2.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", @@ -34,7 +34,7 @@ }, "devDependencies": { "@types/bn.js": "^5.1.0", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.5", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", "reflect-metadata": "^0.1.13", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/askar/src/AskarModule.ts b/packages/askar/src/AskarModule.ts index 5eccb13b3d..8a22d82323 100644 --- a/packages/askar/src/AskarModule.ts +++ b/packages/askar/src/AskarModule.ts @@ -1,22 +1,21 @@ +import type { AskarModuleConfigOptions } from './AskarModuleConfig' import type { DependencyManager, Module } from '@aries-framework/core' import { AriesFrameworkError, InjectionSymbols } from '@aries-framework/core' +import { AskarModuleConfig } from './AskarModuleConfig' import { AskarStorageService } from './storage' import { AskarWallet } from './wallet' export class AskarModule implements Module { + public readonly config: AskarModuleConfig + + public constructor(config: AskarModuleConfigOptions) { + this.config = new AskarModuleConfig(config) + } + public register(dependencyManager: DependencyManager) { - try { - // eslint-disable-next-line import/no-extraneous-dependencies - require('@hyperledger/aries-askar-nodejs') - } catch (error) { - try { - require('@hyperledger/aries-askar-react-native') - } catch (error) { - throw new Error('Could not load aries-askar bindings') - } - } + dependencyManager.registerInstance(AskarModuleConfig, this.config) if (dependencyManager.isRegistered(InjectionSymbols.Wallet)) { throw new AriesFrameworkError('There is an instance of Wallet already registered') diff --git a/packages/askar/src/AskarModuleConfig.ts b/packages/askar/src/AskarModuleConfig.ts new file mode 100644 index 0000000000..38eebdde86 --- /dev/null +++ b/packages/askar/src/AskarModuleConfig.ts @@ -0,0 +1,54 @@ +import type { AriesAskar } from '@hyperledger/aries-askar-shared' + +export interface AskarModuleConfigOptions { + /** + * + * ## Node.JS + * + * ```ts + * import { ariesAskar } from '@hyperledger/aries-askar-nodejs' + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * ariesAskar: new AskarModule({ + * ariesAskar, + * }) + * } + * }) + * ``` + * + * ## React Native + * + * ```ts + * import { ariesAskar } from '@hyperledger/aries-askar-react-native' + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * ariesAskar: new AskarModule({ + * ariesAskar, + * }) + * } + * }) + * ``` + */ + ariesAskar: AriesAskar +} + +/** + * @public + */ +export class AskarModuleConfig { + private options: AskarModuleConfigOptions + + public constructor(options: AskarModuleConfigOptions) { + this.options = options + } + + public get ariesAskar() { + return this.options.ariesAskar + } +} diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index 956d0b124b..714a387c7d 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -6,7 +6,7 @@ import { RecordDuplicateError, RecordNotFoundError, } from '@aries-framework/core' -import { ariesAskar } from '@hyperledger/aries-askar-shared' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' @@ -30,8 +30,7 @@ describeRunInNodeVersion([18], 'AskarStorageService', () => { wallet, agentConfig, }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await wallet.createAndOpen(agentConfig.walletConfig!) + await wallet.createAndOpen(agentConfig.walletConfig) storageService = new AskarStorageService() }) diff --git a/packages/askar/src/types.ts b/packages/askar/src/types.ts deleted file mode 100644 index bc0baa2947..0000000000 --- a/packages/askar/src/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { AriesAskar } from '@hyperledger/aries-askar-shared' - -export type { AriesAskar } diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index 9321aca39d..3921e080ff 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -2,11 +2,15 @@ import type { AskarWalletPostgresStorageConfig } from '../src/wallet' import type { InitConfig } from '@aries-framework/core' import { LogLevel } from '@aries-framework/core' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import path from 'path' import { TestLogger } from '../../core/tests/logger' import { agentDependencies } from '../../node/src' import { AskarModule } from '../src/AskarModule' +import { AskarModuleConfig } from '../src/AskarModuleConfig' + +export const askarModuleConfig = new AskarModuleConfig({ ariesAskar }) export const genesisPath = process.env.GENESIS_TXN_PATH ? path.resolve(process.env.GENESIS_TXN_PATH) @@ -34,7 +38,7 @@ export function getPostgresAgentOptions( return { config, dependencies: agentDependencies, - modules: { askar: new AskarModule() }, + modules: { askar: new AskarModule(askarModuleConfig) }, } as const } @@ -54,6 +58,6 @@ export function getSqliteAgentOptions(name: string, extraConfig: Partial { content: message, }) + // polling interval is 100ms, so 500ms should be enough to make sure no messages are sent + await recipientAgent.mediationRecipient.stopMessagePickup() + await sleep(500) + expect(basicMessage.content).toBe(message) } @@ -159,6 +164,7 @@ describe('mediator establishment', () => { config: { ...recipientAgentOptions.config, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + mediatorPollingInterval: 100, }, }) }) @@ -175,6 +181,7 @@ describe('mediator establishment', () => { ...recipientAgentOptions.config, mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, useDidKeyInProtocols: false, + mediatorPollingInterval: 100, }, } ) diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 87373300f3..5f8171a183 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -29,10 +29,10 @@ "@aries-framework/core": "^0.3.3", "@aries-framework/indy-sdk": "^0.3.3", "@aries-framework/node": "^0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.5" + "@hyperledger/aries-askar-shared": "^0.1.0-dev.6" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.5", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", "indy-sdk": "^1.16.0-dev-1655", "rimraf": "^4.0.7", "typescript": "~4.9.4" diff --git a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts index 5d8b6f64ab..a5bd7e87b2 100644 --- a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts +++ b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts @@ -44,7 +44,11 @@ describeRunInNodeVersion([18], 'Indy SDK To Askar Migration', () => { const invalidNewAgent = new Agent({ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion config: { ...invalidConfig, walletConfig: { ...invalidConfig.walletConfig!, key: 'wrong-key' } }, - modules: { askar: new AskarModule() }, + modules: { + askar: new AskarModule({ + ariesAskar, + }), + }, dependencies: agentDependencies, }) @@ -58,7 +62,11 @@ describeRunInNodeVersion([18], 'Indy SDK To Askar Migration', () => { const newAgent = new Agent({ config, - modules: { askar: new AskarModule() }, + modules: { + askar: new AskarModule({ + ariesAskar, + }), + }, dependencies: agentDependencies, }) diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index 98b6d946f5..f7eee340a7 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -26,10 +26,10 @@ "dependencies": { "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", - "@hyperledger/indy-vdr-shared": "^0.1.0-dev.10" + "@hyperledger/indy-vdr-shared": "0.1.0-dev.12" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.10", + "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", "@stablelib/ed25519": "^1.0.2", "rimraf": "^4.0.7", "rxjs": "^7.2.0", diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts index 44dc54ff8a..d03db3c08d 100644 --- a/packages/indy-vdr/src/IndyVdrModule.ts +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -15,17 +15,6 @@ export class IndyVdrModule implements Module { } public register(dependencyManager: DependencyManager) { - try { - // eslint-disable-next-line import/no-extraneous-dependencies - require('@hyperledger/indy-vdr-nodejs') - } catch (error) { - try { - require('@hyperledger/indy-vdr-react-native') - } catch (error) { - throw new Error('Error registering bindings for Indy VDR') - } - } - // Config dependencyManager.registerInstance(IndyVdrModuleConfig, this.config) diff --git a/packages/indy-vdr/src/IndyVdrModuleConfig.ts b/packages/indy-vdr/src/IndyVdrModuleConfig.ts index 6b6b2eaddb..086846ddf7 100644 --- a/packages/indy-vdr/src/IndyVdrModuleConfig.ts +++ b/packages/indy-vdr/src/IndyVdrModuleConfig.ts @@ -1,10 +1,47 @@ import type { IndyVdrPoolConfig } from './pool' +import type { IndyVdr } from '@hyperledger/indy-vdr-shared' export interface IndyVdrModuleConfigOptions { + /** + * + * ## Node.JS + * + * ```ts + * import { indyVdr } from '@hyperledger/indy-vdr-nodejs'; + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * indyVdr: new IndyVdrModule({ + * indyVdr, + * }) + * } + * }) + * ``` + * + * ## React Native + * + * ```ts + * import { indyVdr } from '@hyperledger/indy-vdr-react-native'; + * + * const agent = new Agent({ + * config: {}, + * dependencies: agentDependencies, + * modules: { + * indyVdr: new IndyVdrModule({ + * indyVdr, + * }) + * } + * }) + * ``` + */ + indyVdr: IndyVdr + /** * Array of indy networks to connect to. * - * [@default](https://github.com/default) [] + * @default [] * * @example * ``` @@ -33,4 +70,9 @@ export class IndyVdrModuleConfig { public get networks() { return this.options.networks } + + /** See {@link IndyVdrModuleConfigOptions.indyVdr} */ + public get indyVdr() { + return this.options.indyVdr + } } diff --git a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts index 4d904fa133..1b9b55c8e4 100644 --- a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts +++ b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts @@ -1,5 +1,7 @@ import type { DependencyManager } from '@aries-framework/core' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' + import { IndyVdrModule } from '../IndyVdrModule' import { IndyVdrModuleConfig } from '../IndyVdrModuleConfig' import { IndyVdrPoolService } from '../pool' @@ -12,6 +14,7 @@ const dependencyManager = { describe('IndyVdrModule', () => { test('registers dependencies on the dependency manager', () => { const indyVdrModule = new IndyVdrModule({ + indyVdr, networks: [ { isProduction: false, diff --git a/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts b/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts index db86ddb519..b1abb77784 100644 --- a/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts +++ b/packages/indy-vdr/src/__tests__/IndyVdrModuleConfig.test.ts @@ -1,5 +1,7 @@ import type { IndyVdrPoolConfig } from '../pool' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' + import { IndyVdrModuleConfig } from '../IndyVdrModuleConfig' describe('IndyVdrModuleConfig', () => { @@ -7,6 +9,7 @@ describe('IndyVdrModuleConfig', () => { const networkConfig = {} as IndyVdrPoolConfig const config = new IndyVdrModuleConfig({ + indyVdr, networks: [networkConfig], }) diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index 2ea2390329..1d1b94494e 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -2,11 +2,13 @@ import type { IndyVdrDidCreateOptions } from '../src/dids/IndyVdrIndyDidRegistra import type { Agent } from '@aries-framework/core' import { DidCommV1Service, DidCommV2Service, DidDocumentService, KeyType } from '@aries-framework/core' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { genesisTransactions } from '../../core/tests/helpers' import { IndyVdrModuleConfig } from '../src/IndyVdrModuleConfig' export const indyVdrModuleConfig = new IndyVdrModuleConfig({ + indyVdr, networks: [ { genesisTransactions, diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index f3448d169a..1cba5741e0 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -1,4 +1,5 @@ import { Agent, DidsModule, Key, KeyType, TypedArrayEncoder } from '@aries-framework/core' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { RevocationRegistryDefinitionRequest, RevocationRegistryEntryRequest } from '@hyperledger/indy-vdr-shared' import { @@ -25,6 +26,7 @@ const agent = new Agent({ dependencies: agentDependencies, modules: { indyVdr: new IndyVdrModule({ + indyVdr, networks: indyVdrModuleConfig.networks, }), indySdk: new IndySdkModule({ diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index 65eee36cf3..b4b66f4449 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -11,6 +11,7 @@ import { Agent, DidsModule, } from '@aries-framework/core' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' @@ -30,6 +31,7 @@ const agent = new Agent( {}, { indyVdr: new IndyVdrModule({ + indyVdr, networks: indyVdrModuleConfig.networks, }), indySdk: new IndySdkModule({ diff --git a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts index 209b05e698..92068e5690 100644 --- a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts @@ -1,4 +1,5 @@ import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' @@ -14,6 +15,7 @@ const agent = new Agent( {}, { indyVdr: new IndyVdrModule({ + indyVdr, networks: indyVdrModuleConfig.networks, }), indySdk: new IndySdkModule({ diff --git a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts index 2ff501c641..14b2a7e202 100644 --- a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts @@ -1,4 +1,5 @@ import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' +import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' @@ -15,6 +16,7 @@ const agent = new Agent( {}, { indyVdr: new IndyVdrModule({ + indyVdr, networks: indyVdrModuleConfig.networks, }), indySdk: new IndySdkModule({ diff --git a/packages/indy-vdr/tests/setup.ts b/packages/indy-vdr/tests/setup.ts index e766b0e868..a5714a641c 100644 --- a/packages/indy-vdr/tests/setup.ts +++ b/packages/indy-vdr/tests/setup.ts @@ -1,6 +1,3 @@ -// Needed to register indy-vdr node bindings -import '../src/index' - -require('@hyperledger/indy-vdr-nodejs') +import '@hyperledger/indy-vdr-nodejs' jest.setTimeout(120000) diff --git a/yarn.lock b/yarn.lock index c2bba8a78d..dab29c0369 100644 --- a/yarn.lock +++ b/yarn.lock @@ -864,12 +864,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.10": - version "0.1.0-dev.10" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.10.tgz#80c093ecb08a277fb494399b64aad1b900c3103f" - integrity sha512-ju5mJPwuyebAPziuf+eUOwxEws02G2FHEp/qG3GV3kxtlx7THW7HVB7dMSNqhRVKCsbcNnZtWJB1UiPvWqboUg== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.11": + version "0.1.0-dev.11" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.11.tgz#301b9bc5a4bb0235212ac48da2bf41118b407cdd" + integrity sha512-4BSHOGOdXjF4pyJuEjwk0iaSHeqt5UdXRXNv+u9VJ7yYhqM/aJZNhtUAgHXu8KGZwimFcFsp2e0FoLqwO0vLHQ== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.10" + "@hyperledger/anoncreds-shared" "0.1.0-dev.11" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -877,17 +877,17 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.10", "@hyperledger/anoncreds-shared@^0.1.0-dev.10": - version "0.1.0-dev.10" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.10.tgz#9d67f64e98ff41971644c95b03dabafd741df4df" - integrity sha512-POvcwQrUcPrwoZehQa38pN1dnjyeUlrQ6VlksbBRS8SUHJuyixZsD+d3XoumqaNfl9Z1DCjfuOgEiPlec01gXQ== +"@hyperledger/anoncreds-shared@0.1.0-dev.11", "@hyperledger/anoncreds-shared@^0.1.0-dev.11": + version "0.1.0-dev.11" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.11.tgz#206328cabcd855ef20c863ab5c2615a3a4c2502c" + integrity sha512-nK05y/qNtI3P+hnkVZW/d5oduMa7slZfEh2gQ+ZmAEmwHEcSU8iJ+QTkKS3nRE+6igXUvVAztlGS7JZHf21KKw== -"@hyperledger/aries-askar-nodejs@^0.1.0-dev.5": - version "0.1.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.5.tgz#602db32f49dd1f9e7f83d378a9813d2bde5886f8" - integrity sha512-38lXtmnhhca+s14V3zTefAyZGIg6nRpKi4Emnr1q5hQXmn1WZn7/ybYJbcI/VHoAPNLA+DydSI99U5KuPkrC1w== +"@hyperledger/aries-askar-nodejs@^0.1.0-dev.6": + version "0.1.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.6.tgz#1067196269e3dc7904afa946234e069c31dfc2d9" + integrity sha512-VHORF/VbXXNA0zevS8diocVFfHpqp8XS33VuIEDFEG9n87Sc4sO0KxxCr5KdGeAf46yhiCdJd2bOKRjDHCObyQ== dependencies: - "@hyperledger/aries-askar-shared" "0.1.0-dev.5" + "@hyperledger/aries-askar-shared" "0.1.0-dev.6" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" node-cache "^5.1.2" @@ -895,29 +895,30 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.1.0-dev.5", "@hyperledger/aries-askar-shared@^0.1.0-dev.5": - version "0.1.0-dev.5" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.5.tgz#06ae815b873693b7b483004d027059d50f24fb60" - integrity sha512-nufXqMslytelijC0uXBTuaj9OHjHo5mJe0hABKfYJqe9p1RYmmKlnqi+B/mjWrr2R8yG8NLrNnHbwPcBArLrPA== +"@hyperledger/aries-askar-shared@0.1.0-dev.6", "@hyperledger/aries-askar-shared@^0.1.0-dev.6": + version "0.1.0-dev.6" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.6.tgz#af3bf4318312ee1af0ead8c0bea6a4445a265525" + integrity sha512-P62u1GNw2hvFh3T8hYTBiD2YsIzHIQOwa8+p8wEhB0AJi7Ixc3OcAtxxgbreosDtGrW+cxkinuSqufveuK9V1g== dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.10": - version "0.1.0-dev.10" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.10.tgz#f5fc988b3c11766475f7f084f814af8a4eca0caf" - integrity sha512-wETVax9HRidhRTRiRqwLTj/looNCvjZ3vdqMoqmPbmet5sM9eRkU9v4ipz4paqz4LiZipTx2fisLyYskKW9g6A== +"@hyperledger/indy-vdr-nodejs@0.1.0-dev.12": + version "0.1.0-dev.12" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.12.tgz#c8966bf5f446fff4c7bf0bf5afb4159159d2dd9e" + integrity sha512-rJDkk4u0zyn3n/PBw0pf3qSgbZ0n5VAJY5ykeMU2/bBfFXQ1KJex/148M4YsGEOxY0XUSoXgcT/oJYS3MSA6Nw== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.10" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.12" "@mapbox/node-pre-gyp" "^1.0.10" + "@types/ref-array-di" "^1.2.5" ffi-napi "^4.0.3" ref-array-di "^1.2.2" ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.10", "@hyperledger/indy-vdr-shared@^0.1.0-dev.10": - version "0.1.0-dev.10" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.10.tgz#2a6d09a3a6ae3458ae9209ab7b6f09435e88b9c9" - integrity sha512-7fr9/Iliu0u94qE8u1g/RoLJOwMH0kFdclttwBHfTAKTQyfoXUPe1yIZ8ITRgLwnmFYPQpRa54lU+uv15c37fg== +"@hyperledger/indy-vdr-shared@0.1.0-dev.12": + version "0.1.0-dev.12" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.12.tgz#a5051cb91b7698f80265b7506789fb50490477e2" + integrity sha512-CPVGTHVLFAVVU6uIhcbhAUWqDrn3u2R3D+ALdqgKwJY1Ca8kFiUvhFN1/DkHtZuEo549wPQmFqH2hCkXaiuF7Q== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -2684,6 +2685,13 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/ref-array-di@^1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@types/ref-array-di/-/ref-array-di-1.2.5.tgz#822b9be5b934398fafd5c26264d8de80d487747d" + integrity sha512-dA/Himb7ca/Tf5vqLOhi7LewAAoOXghlocw7gAqvNrmLybAtu+w2BLzEsbFWAtx5ElNzMEHDaRybueYViFROjQ== + dependencies: + "@types/ref-napi" "*" + "@types/ref-napi@*", "@types/ref-napi@^3.0.4": version "3.0.7" resolved "https://registry.yarnpkg.com/@types/ref-napi/-/ref-napi-3.0.7.tgz#20adc93a7a2f9f992dfb17409fd748e6f4bf403d" From dbfebb4720da731dbe11efdccdd061d1da3d1323 Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Mon, 20 Mar 2023 15:01:37 +0200 Subject: [PATCH 096/139] feat: default return route (#1327) Signed-off-by: Mike Richardson --- .../__tests__/v1-connectionless-proofs.e2e.test.ts | 10 +++++++++- .../core/src/modules/credentials/CredentialsApi.ts | 2 +- packages/core/src/modules/proofs/ProofsApi.ts | 7 ++----- .../core/src/modules/proofs/ProofsApiOptions.ts | 6 ++++++ .../v2-indy-connectionless-proofs.e2e.test.ts | 14 +++++++++++--- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index ea2049d00d..d8a5a1b3dc 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -43,7 +43,8 @@ describe('V1 Proofs - Connectionless - Indy', () => { } }) - test('Faber starts with connection-less proof requests to Alice', async () => { + // new method to test the return route and mediator together + const connectionlessTest = async (returnRoute?: boolean) => { const { holderAgent: aliceAgent, issuerAgent: faberAgent, @@ -134,6 +135,7 @@ describe('V1 Proofs - Connectionless - Indy', () => { await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, + useReturnRoute: returnRoute, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) @@ -143,6 +145,7 @@ describe('V1 Proofs - Connectionless - Indy', () => { state: ProofState.PresentationReceived, }) + const sentPresentationMessage = aliceAgent.proofs.findPresentationMessage(aliceProofExchangeRecord.id) // assert presentation is valid expect(faberProofExchangeRecord.isVerified).toBe(true) @@ -154,6 +157,11 @@ describe('V1 Proofs - Connectionless - Indy', () => { threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) + return sentPresentationMessage + } + + test('Faber starts with connection-less proof requests to Alice', async () => { + await connectionlessTest() }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index 06e7f6a712..6e60a807ba 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -550,7 +550,7 @@ export class CredentialsApi implements Credent serviceParams: { service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, + returnRoute: false, // hard wire to be false since it's the end of the protocol so not needed here }, }) ) diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 52b45d5733..1a327f71b8 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -341,7 +341,6 @@ export class ProofsApi implements ProofsApi { autoAcceptProof: options.autoAcceptProof, goalCode: options.goalCode, }) - // Set and save ~service decorator to record (to remember our verkey) message.service = ourService await this.didCommMessageRepository.saveOrUpdateAgentMessage(this.agentContext, { @@ -349,18 +348,16 @@ export class ProofsApi implements ProofsApi { role: DidCommMessageRole.Sender, associatedRecordId: proofRecord.id, }) - await this.messageSender.sendMessageToService( new OutboundMessageContext(message, { agentContext: this.agentContext, serviceParams: { service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, + returnRoute: options.useReturnRoute ?? true, // defaults to true if missing }, }) ) - return proofRecord } // Cannot send message without connectionId or ~service decorator @@ -495,7 +492,7 @@ export class ProofsApi implements ProofsApi { serviceParams: { service: recipientService.resolvedDidCommService, senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, + returnRoute: false, // hard wire to be false since it's the end of the protocol so not needed here }, }) ) diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts index 97bfca04eb..9fcb5cefa3 100644 --- a/packages/core/src/modules/proofs/ProofsApiOptions.ts +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -94,6 +94,12 @@ export interface RequestProofOptions extends BaseOptions { proofRecordId: string + + /** + * whether to enable return routing on the send presentation message. This value only + * has an effect for connectionless exchanges. + */ + useReturnRoute?: boolean proofFormats?: ProofFormatPayload, 'acceptRequest'> goalCode?: string diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index 7b6e8cad49..a261e07989 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -38,7 +38,7 @@ describe('V2 Connectionless Proofs - Indy', () => { } }) - test('Faber starts with connection-less proof requests to Alice', async () => { + const connectionlessTest = async (returnRoute?: boolean) => { const { issuerAgent: faberAgent, issuerReplay: faberReplay, @@ -128,8 +128,9 @@ describe('V2 Connectionless Proofs - Indy', () => { proofRecordId: aliceProofExchangeRecord.id, }) - await aliceAgent.proofs.acceptRequest({ + aliceProofExchangeRecord = await aliceAgent.proofs.acceptRequest({ proofRecordId: aliceProofExchangeRecord.id, + useReturnRoute: returnRoute, proofFormats: { indy: requestedCredentials.proofFormats.indy }, }) @@ -139,17 +140,24 @@ describe('V2 Connectionless Proofs - Indy', () => { state: ProofState.PresentationReceived, }) + const sentPresentationMessage = aliceAgent.proofs.findPresentationMessage(aliceProofExchangeRecord.id) + // assert presentation is valid expect(faberProofExchangeRecord.isVerified).toBe(true) // Faber accepts presentation await faberAgent.proofs.acceptPresentation({ proofRecordId: faberProofExchangeRecord.id }) - // Alice waits till it receives presentation ack + // Alice waits until it receives presentation ack aliceProofExchangeRecord = await waitForProofExchangeRecordSubject(aliceReplay, { threadId: aliceProofExchangeRecord.threadId, state: ProofState.Done, }) + return sentPresentationMessage + } + + test('Faber starts with connection-less proof requests to Alice', async () => { + await connectionlessTest() }) test('Faber starts with connection-less proof requests to Alice with auto-accept enabled', async () => { From 1125e81962ffa752bf40fa8f7f4226e186f22013 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 21 Mar 2023 02:18:53 +0100 Subject: [PATCH 097/139] fix: did cache key not being set correctly (#1394) Signed-off-by: Timo Glastra --- packages/indy-sdk/src/ledger/IndySdkPoolService.ts | 6 ++++-- packages/indy-vdr/src/pool/IndyVdrPoolService.ts | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index 23928fafa5..40bee58457 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -98,7 +98,9 @@ export class IndySdkPoolService { } const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache - const cachedNymResponse = await cache.get(agentContext, `IndySdkPoolService:${did}`) + const cacheKey = `IndySdkPoolService:${did}` + + const cachedNymResponse = await cache.get(agentContext, cacheKey) const pool = this.pools.find((pool) => pool.didIndyNamespace === cachedNymResponse?.indyNamespace) // If we have the nym response with associated pool in the cache, we'll use that @@ -145,7 +147,7 @@ export class IndySdkPoolService { value = productionOrNonProduction[0].value } - await cache.set(agentContext, `IndySdkPoolService:${did}`, { + await cache.set(agentContext, cacheKey, { nymResponse: value.did, indyNamespace: value.pool.didIndyNamespace, } satisfies CachedDidResponse) diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index d8e31f72a7..95194dc8da 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -73,9 +73,10 @@ export class IndyVdrPoolService { ) } - const didCache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + const cacheKey = `IndyVdrPoolService:${did}` - const cachedNymResponse = await didCache.get(agentContext, `IndyVdrPoolService:${did}`) + const cachedNymResponse = await cache.get(agentContext, cacheKey) const pool = this.pools.find((pool) => pool.indyNamespace === cachedNymResponse?.indyNamespace) // If we have the nym response with associated pool in the cache, we'll use that @@ -122,7 +123,7 @@ export class IndyVdrPoolService { value = productionOrNonProduction[0].value } - await didCache.set(agentContext, did, { + await cache.set(agentContext, cacheKey, { nymResponse: { did: value.did.nymResponse.did, verkey: value.did.nymResponse.verkey, From 9f0f8f21e7436c0a422d8c3a42a4cb601bcf7c77 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 21 Mar 2023 15:11:34 +0100 Subject: [PATCH 098/139] fix: incorrect type for anoncreds registration (#1396) Signed-off-by: Timo Glastra --- .../src/services/registry/CredentialDefinitionOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts index 815150c6c1..6e35c6ca76 100644 --- a/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts +++ b/packages/anoncreds/src/services/registry/CredentialDefinitionOptions.ts @@ -41,5 +41,5 @@ export interface RegisterCredentialDefinitionReturn { | RegisterCredentialDefinitionReturnStateFinished | RegisterCredentialDefinitionReturnStateFailed credentialDefinitionMetadata: Extensible - registrationMetadata: AnonCredsResolutionMetadata + registrationMetadata: Extensible } From d6e2ea2194a4860265fe299ef8ee4cb4799ab1a6 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Tue, 21 Mar 2023 13:55:56 -0300 Subject: [PATCH 099/139] fix: reference to indyLedgers in IndyXXXNotConfiguredError (#1397) Signed-off-by: Ariel Gentile --- packages/indy-sdk/src/ledger/IndySdkPoolService.ts | 10 +++++----- packages/indy-vdr/src/pool/IndyVdrPoolService.ts | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index 40bee58457..d66251a83c 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -93,7 +93,7 @@ export class IndySdkPoolService { if (pools.length === 0) { throw new IndySdkPoolNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + 'No indy ledgers configured. Provide at least one pool configuration in IndySdkModuleConfigOptions.networks' ) } @@ -129,7 +129,7 @@ export class IndySdkPoolService { // If there are self certified DIDs we always prefer it over non self certified DIDs // We take the first self certifying DID as we take the order in the - // indyLedgers config as the order of preference of ledgers + // IndySdkModuleConfigOptions.networks config as the order of preference of ledgers let value = successful.find((response) => isLegacySelfCertifiedDid(response.value.did.did, response.value.did.verkey) )?.value @@ -142,8 +142,8 @@ export class IndySdkPoolService { const nonProduction = successful.filter((s) => !s.value.pool.config.isProduction) const productionOrNonProduction = production.length >= 1 ? production : nonProduction - // We take the first value as we take the order in the indyLedgers config as - // the order of preference of ledgers + // We take the first value as we take the order in the IndySdkModuleConfigOptions.networks + // config as the order of preference of ledgers value = productionOrNonProduction[0].value } @@ -176,7 +176,7 @@ export class IndySdkPoolService { public getPoolForNamespace(indyNamespace?: string) { if (this.pools.length === 0) { throw new IndySdkPoolNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + 'No indy ledgers configured. Provide at least one pool configuration in IndySdkModuleConfigOptions.networks' ) } diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index 95194dc8da..69ee1026a0 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -69,7 +69,7 @@ export class IndyVdrPoolService { if (pools.length === 0) { throw new IndyVdrNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + 'No indy ledgers configured. Provide at least one pool configuration in IndyVdrModuleConfigOptions.networks' ) } @@ -105,7 +105,7 @@ export class IndyVdrPoolService { // If there are self certified DIDs we always prefer it over non self certified DIDs // We take the first self certifying DID as we take the order in the - // indyLedgers config as the order of preference of ledgers + // IndyVdrModuleConfigOptions.networks config as the order of preference of ledgers let value = successful.find((response) => isSelfCertifiedDid(response.value.did.nymResponse.did, response.value.did.nymResponse.verkey) )?.value @@ -118,8 +118,8 @@ export class IndyVdrPoolService { const nonProduction = successful.filter((s) => !s.value.pool.config.isProduction) const productionOrNonProduction = production.length >= 1 ? production : nonProduction - // We take the first value as we take the order in the indyLedgers config as - // the order of preference of ledgers + // We take the first value as we take the order in the IndyVdrModuleConfigOptions.networks + // config as the order of preference of ledgers value = productionOrNonProduction[0].value } @@ -154,7 +154,7 @@ export class IndyVdrPoolService { public getPoolForNamespace(indyNamespace: string) { if (this.pools.length === 0) { throw new IndyVdrNotConfiguredError( - "No indy ledgers configured. Provide at least one pool configuration in the 'indyLedgers' agent configuration" + 'No indy ledgers configured. Provide at least one pool configuration in IndyVdrModuleConfigOptions.networks' ) } From 996c08f8e32e58605408f5ed5b6d8116cea3b00c Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Wed, 22 Mar 2023 15:45:02 +0100 Subject: [PATCH 100/139] feat(openid4vc-client): openid authorization flow (#1384) Signed-off-by: Karim Stekelenburg --- .../src/modules/vc/W3cCredentialService.ts | 5 +- .../vc/models/W3cCredentialServiceOptions.ts | 1 + packages/openid4vc-client/package.json | 3 +- .../src/OpenId4VcClientApi.ts | 37 ++-- .../src/OpenId4VcClientService.ts | 160 ++++++++++++++---- .../tests/openid4vc-client.e2e.test.ts | 151 ++++++++++++++++- yarn.lock | 18 +- 7 files changed, 308 insertions(+), 67 deletions(-) diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index 53a57a8717..fc9d454fac 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -103,9 +103,10 @@ export class W3cCredentialService { */ public async verifyCredential( agentContext: AgentContext, - options: VerifyCredentialOptions, - verifyRevocationState = true + options: VerifyCredentialOptions ): Promise { + const verifyRevocationState = options.verifyRevocationState ?? true + const suites = this.getSignatureSuitesForCredential(agentContext, options.credential) const verifyOptions: Record = { diff --git a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts index 7851859949..419a11dcda 100644 --- a/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts +++ b/packages/core/src/modules/vc/models/W3cCredentialServiceOptions.ts @@ -17,6 +17,7 @@ export interface SignCredentialOptions { export interface VerifyCredentialOptions { credential: W3cVerifiableCredential proofPurpose?: ProofPurpose + verifyRevocationState?: boolean } export interface StoreCredentialOptions { diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index 97d83446a2..31acdd3ec9 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -25,7 +25,8 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@sphereon/openid4vci-client": "^0.3.6" + "@sphereon/openid4vci-client": "^0.4.0", + "@stablelib/random": "^1.0.2" }, "devDependencies": { "@aries-framework/node": "0.3.3", diff --git a/packages/openid4vc-client/src/OpenId4VcClientApi.ts b/packages/openid4vc-client/src/OpenId4VcClientApi.ts index ab671c46e1..d927903c4c 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientApi.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientApi.ts @@ -1,14 +1,13 @@ +import type { + GenerateAuthorizationUrlOptions, + PreAuthCodeFlowOptions, + AuthCodeFlowOptions, +} from './OpenId4VcClientService' import type { W3cCredentialRecord } from '@aries-framework/core' import { AgentContext, injectable } from '@aries-framework/core' -import { OpenId4VcClientService } from './OpenId4VcClientService' - -interface PreAuthorizedOptions { - issuerUri: string - kid: string - checkRevocationState?: boolean // default = true -} +import { AuthFlowType, OpenId4VcClientService } from './OpenId4VcClientService' /** * @public @@ -23,13 +22,29 @@ export class OpenId4VcClientApi { this.openId4VcClientService = openId4VcClientService } - public async requestCredentialPreAuthorized(options: PreAuthorizedOptions): Promise { + public async requestCredentialUsingPreAuthorizedCode(options: PreAuthCodeFlowOptions): Promise { + // set defaults + const verifyRevocationState = options.verifyRevocationState ?? true + + return this.openId4VcClientService.requestCredential(this.agentContext, { + ...options, + verifyRevocationState: verifyRevocationState, + flowType: AuthFlowType.PreAuthorizedCodeFlow, + }) + } + + public async requestCredentialUsingAuthorizationCode(options: AuthCodeFlowOptions): Promise { // set defaults - const checkRevocationState = options.checkRevocationState ?? true + const checkRevocationState = options.verifyRevocationState ?? true - return this.openId4VcClientService.requestCredentialPreAuthorized(this.agentContext, { + return this.openId4VcClientService.requestCredential(this.agentContext, { ...options, - checkRevocationState: checkRevocationState, + verifyRevocationState: checkRevocationState, + flowType: AuthFlowType.AuthorizationCodeFlow, }) } + + public async generateAuthorizationUrl(options: GenerateAuthorizationUrlOptions) { + return this.openId4VcClientService.generateAuthorizationUrl(options) + } } diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts index b2cfdfbafa..b458cd631d 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -1,34 +1,63 @@ -import type { AgentContext, W3cCredentialRecord } from '@aries-framework/core' -import type { EndpointMetadata, Jwt } from '@sphereon/openid4vci-client' +import type { AgentContext } from '@aries-framework/core' +import type { AccessTokenResponse, EndpointMetadata, Jwt } from '@sphereon/openid4vci-client' import { + AriesFrameworkError, + DidsApi, getKeyFromVerificationMethod, + Hasher, inject, + injectable, InjectionSymbols, isJwtAlgorithm, - Logger, - DidsApi, - AriesFrameworkError, - injectable, JsonEncoder, JsonTransformer, - W3cCredentialService, - W3cVerifiableCredential, JwsService, jwtKeyAlgMapping, + Logger, + TypedArrayEncoder, + W3cCredentialService, + W3cVerifiableCredential, } from '@aries-framework/core' import { Alg, AuthzFlowType, + CodeChallengeMethod, CredentialRequestClientBuilder, OpenID4VCIClient, ProofOfPossessionBuilder, } from '@sphereon/openid4vci-client' +import { randomStringForEntropy } from '@stablelib/random' -export interface PreAuthorizedOptions { +export interface PreAuthCodeFlowOptions { issuerUri: string kid: string - checkRevocationState: boolean + verifyRevocationState: boolean +} + +export interface AuthCodeFlowOptions extends PreAuthCodeFlowOptions { + clientId: string + authorizationCode: string + codeVerifier: string + redirectUri: string +} + +export enum AuthFlowType { + AuthorizationCodeFlow, + PreAuthorizedCodeFlow, +} + +export type RequestCredentialOptions = { flowType: AuthFlowType } & PreAuthCodeFlowOptions & + Partial + +// The code_challenge_method is omitted here +// because we assume it will always be SHA256 +// as clear text code_challenges are unsafe +export interface GenerateAuthorizationUrlOptions { + initiationUri: string + clientId: string + redirectUri: string + scope?: string[] } @injectable() @@ -134,40 +163,92 @@ export class OpenId4VcClientService { } } - public async requestCredentialPreAuthorized( - agentContext: AgentContext, - options: PreAuthorizedOptions - ): Promise { - this.logger.debug('Running pre-authorized flow with options', options) + private generateCodeVerifier(): string { + return randomStringForEntropy(256) + } + + public async generateAuthorizationUrl(options: GenerateAuthorizationUrlOptions) { + this.logger.debug('Generating authorization url') + + if (!options.scope || options.scope.length === 0) { + throw new AriesFrameworkError( + 'Only scoped based authorization requests are supported at this time. Please provide at least one scope' + ) + } + + const client = await OpenID4VCIClient.initiateFromURI({ + issuanceInitiationURI: options.initiationUri, + flowType: AuthzFlowType.AUTHORIZATION_CODE_FLOW, + }) + const codeVerifier = this.generateCodeVerifier() + const codeVerifierSha256 = Hasher.hash(TypedArrayEncoder.fromString(codeVerifier), 'sha2-256') + const base64Url = TypedArrayEncoder.toBase64URL(codeVerifierSha256) + + this.logger.debug('Converted code_verifier to code_challenge', { + codeVerifier: codeVerifier, + sha256: codeVerifierSha256.toString(), + base64Url: base64Url, + }) - // this value is hardcoded as it's the only supported format at this point + const authorizationUrl = client.createAuthorizationRequestUrl({ + clientId: options.clientId, + codeChallengeMethod: CodeChallengeMethod.SHA256, + codeChallenge: base64Url, + redirectUri: options.redirectUri, + scope: options.scope?.join(' '), + }) + + return { + authorizationUrl, + codeVerifier, + } + } + + public async requestCredential(agentContext: AgentContext, options: RequestCredentialOptions) { const credentialFormat = 'ldp_vc' + let flowType: AuthzFlowType + if (options.flowType === AuthFlowType.AuthorizationCodeFlow) { + flowType = AuthzFlowType.AUTHORIZATION_CODE_FLOW + } else if (options.flowType === AuthFlowType.PreAuthorizedCodeFlow) { + flowType = AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW + } else { + throw new AriesFrameworkError( + `Unsupported flowType ${options.flowType}. Valid values are ${Object.values(AuthFlowType)}` + ) + } + const client = await OpenID4VCIClient.initiateFromURI({ issuanceInitiationURI: options.issuerUri, - flowType: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW, + flowType, kid: options.kid, alg: Alg.EdDSA, }) - const accessToken = await client.acquireAccessToken({}) + let accessToken: AccessTokenResponse - this.logger.info('Fetched server accessToken', accessToken) - - // We currently need the ts-ignore because the type - // inside of OpenID4VCIClient needs to be updated. - // @ts-ignore - if (!accessToken.scope) { - throw new AriesFrameworkError( - "Access token response doesn't contain a scope. Only scoped issuer URIs are supported at this time." - ) + if (options.flowType === AuthFlowType.AuthorizationCodeFlow) { + if (!options.authorizationCode) + throw new AriesFrameworkError( + `The 'authorizationCode' parameter is required when 'flowType' is ${options.flowType}` + ) + if (!options.codeVerifier) + throw new AriesFrameworkError(`The 'codeVerifier' parameter is required when 'flowType' is ${options.flowType}`) + if (!options.redirectUri) + throw new AriesFrameworkError(`The 'redirectUri' parameter is required when 'flowType' is ${options.flowType}`) + + accessToken = await client.acquireAccessToken({ + clientId: options.clientId, + code: options.authorizationCode, + codeVerifier: options.codeVerifier, + redirectUri: options.redirectUri, + }) + } else { + accessToken = await client.acquireAccessToken({}) } const serverMetadata = await client.retrieveServerMetadata() - // @ts-ignore - this.assertCredentialHasFormat(credentialFormat, accessToken.scope, serverMetadata) - this.logger.info('Fetched server metadata', { issuer: serverMetadata.issuer, credentialEndpoint: serverMetadata.credential_endpoint, @@ -176,6 +257,13 @@ export class OpenId4VcClientService { this.logger.debug('Full server metadata', serverMetadata) + if (!accessToken.scope) { + throw new AriesFrameworkError( + "Access token response doesn't contain a scope. Only scoped issuer URIs are supported at this time." + ) + } + this.assertCredentialHasFormat(credentialFormat, accessToken.scope, serverMetadata) + // proof of possession const callbacks = this.getSignCallback(agentContext) @@ -199,9 +287,8 @@ export class OpenId4VcClientService { const credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({ proofInput, - // @ts-ignore credentialType: accessToken.scope, - format: 'ldp_vc', // Allows us to override the format + format: credentialFormat, }) this.logger.debug('Credential request response', credentialResponse) @@ -213,11 +300,10 @@ export class OpenId4VcClientService { const credential = JsonTransformer.fromJSON(credentialResponse.successBody.credential, W3cVerifiableCredential) // verify the signature - const result = await this.w3cCredentialService.verifyCredential( - agentContext, - { credential }, - options.checkRevocationState - ) + const result = await this.w3cCredentialService.verifyCredential(agentContext, { + credential, + verifyRevocationState: options.verifyRevocationState, + }) if (result && !result.verified) { throw new AriesFrameworkError(`Failed to validate credential, error = ${result.error}`) diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index 2c20b6acce..ce85f54bd9 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -1,6 +1,6 @@ import type { KeyDidCreateOptions } from '@aries-framework/core' -import { Agent, KeyType, W3cCredentialRecord, W3cVcModule, TypedArrayEncoder } from '@aries-framework/core' +import { Agent, KeyType, TypedArrayEncoder, W3cCredentialRecord, W3cVcModule } from '@aries-framework/core' import nock, { cleanAll, enableNetConnect } from 'nock' import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' @@ -78,16 +78,152 @@ describe('OpenId4VcClient', () => { privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), }, }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const keyInstance = didKeyToInstanceOfKey(did.didState.did!) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const kid = `${did.didState.did!}#${keyInstance.fingerprint}` + const keyInstance = didKeyToInstanceOfKey(did.didState.did as string) - const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialPreAuthorized({ + const kid = `${did.didState.did as string}#${keyInstance.fingerprint as string}` + + const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialUsingPreAuthorizedCode({ issuerUri, kid, - checkRevocationState: false, + verifyRevocationState: false, + }) + + expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) + + expect(w3cCredentialRecord.credential.type).toEqual([ + 'VerifiableCredential', + 'VerifiableCredentialExtension', + 'OpenBadgeCredential', + ]) + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + expect(w3cCredentialRecord.credential.credentialSubject.id).toEqual(did.didState.did) + }) + }) + describe('Authorization flow', () => { + beforeAll(async () => { + /** + * Below we're setting up some mock HTTP responses. + * These responses are based on the openid-initiate-issuance URI above + * */ + + // setup temporary redirect mock + nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { + Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', + }) + + // setup server metadata response + const httpMock = nock('https://launchpad.vii.electron.mattrlabs.io') + .get('/.well-known/openid-credential-issuer') + .reply(200, getMetadataResponse) + + // setup access token response + httpMock.post('/oidc/v1/auth/token').reply(200, acquireAccessTokenResponse) + + // setup credential request response + httpMock.post('/oidc/v1/auth/credential').reply(200, credentialRequestResponse) + }) + + afterAll(async () => { + cleanAll() + enableNetConnect() + }) + + it('should generate a valid authorization url', async () => { + const clientId = 'test-client' + + const redirectUri = 'https://example.com/cb' + const scope = ['TestCredential'] + const initiationUri = + 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential' + const { authorizationUrl } = await agent.modules.openId4VcClient.generateAuthorizationUrl({ + clientId, + redirectUri, + scope, + initiationUri, + }) + + const parsedUrl = new URL(authorizationUrl) + expect(authorizationUrl.startsWith('https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/authorize')).toBe( + true + ) + expect(parsedUrl.searchParams.get('response_type')).toBe('code') + expect(parsedUrl.searchParams.get('client_id')).toBe(clientId) + expect(parsedUrl.searchParams.get('code_challenge_method')).toBe('S256') + expect(parsedUrl.searchParams.get('redirect_uri')).toBe(redirectUri) + }) + it('should throw if no scope is provided', async () => { + // setup temporary redirect mock + nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { + Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', + }) + + // setup server metadata response + nock('https://launchpad.vii.electron.mattrlabs.io') + .get('/.well-known/openid-credential-issuer') + .reply(200, getMetadataResponse) + + const clientId = 'test-client' + const redirectUri = 'https://example.com/cb' + const initiationUri = + 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential' + expect( + agent.modules.openId4VcClient.generateAuthorizationUrl({ + clientId, + redirectUri, + scope: [], + initiationUri, + }) + ).rejects.toThrow() + }) + it('should successfully execute request a credential', async () => { + // setup temporary redirect mock + nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { + Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', + }) + + // setup server metadata response + nock('https://launchpad.vii.electron.mattrlabs.io') + .get('/.well-known/openid-credential-issuer') + .reply(200, getMetadataResponse) + + const did = await agent.dids.create({ + method: 'key', + options: { + keyType: KeyType.Ed25519, + }, + secret: { + privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), + }, + }) + + const keyInstance = didKeyToInstanceOfKey(did.didState.did as string) + + const kid = `${did.didState.did as string}#${keyInstance.fingerprint as string}` + + const clientId = 'test-client' + + const redirectUri = 'https://example.com/cb' + const initiationUri = + 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential' + + const scope = ['TestCredential'] + const { codeVerifier } = await agent.modules.openId4VcClient.generateAuthorizationUrl({ + clientId, + redirectUri, + scope, + initiationUri, + }) + const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialUsingAuthorizationCode({ + clientId: clientId, + authorizationCode: 'test-code', + codeVerifier: codeVerifier, + verifyRevocationState: false, + kid: kid, + issuerUri: initiationUri, + redirectUri: redirectUri, }) expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) @@ -98,6 +234,7 @@ describe('OpenId4VcClient', () => { 'OpenBadgeCredential', ]) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore expect(w3cCredentialRecord.credential.credentialSubject.id).toEqual(did.didState.did) }) diff --git a/yarn.lock b/yarn.lock index dab29c0369..03b8263561 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2322,20 +2322,20 @@ resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== -"@sphereon/openid4vci-client@^0.3.6": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@sphereon/openid4vci-client/-/openid4vci-client-0.3.6.tgz#70bd76bb888b458274007170e2bb7e784849cbbe" - integrity sha512-C336F3VPMu1LSQAv1rn1KkXOVC3JxbrUzMzOBFlJfSKfnSxfqTqGSK0NTJLnAwGNOEAQ1M4Q/2KIXbDIaTU4Ww== +"@sphereon/openid4vci-client@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@sphereon/openid4vci-client/-/openid4vci-client-0.4.0.tgz#f48c2bb42041b9eab13669de23ba917785c83b24" + integrity sha512-N9ytyV3DHAjBjd67jMowmBMmD9/4Sxkehsrpd1I9Hxg5TO1K+puUPsPXj8Zh4heIWSzT5xBsGTSqXdF0LlrDwQ== dependencies: - "@sphereon/ssi-types" "^0.8.1-next.123" + "@sphereon/ssi-types" "^0.9.0" cross-fetch "^3.1.5" debug "^4.3.4" uint8arrays "^3.1.1" -"@sphereon/ssi-types@^0.8.1-next.123": - version "0.8.1-unstable.242" - resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.8.1-unstable.242.tgz#cf958a3f6ae0cae5e2b5175616edd158170a65ac" - integrity sha512-/Dy7WuT78gWOChhe4YTbDhr9xly1rxajPnGyPaYmeKOleY8c3az2zU2pRcAiL5nqFN0DkVnT0ncYeeENr9NzOw== +"@sphereon/ssi-types@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.9.0.tgz#d140eb6abd77381926d0da7ac51b3c4b96a31b4b" + integrity sha512-umCr/syNcmvMMbQ+i/r/mwjI1Qw2aFPp9AwBTvTo1ailAVaaJjJGPkkVz1K9/2NZATNdDiQ3A8yGzdVJoKh9pA== dependencies: jwt-decode "^3.1.2" From 50e877d3a3b93f5fc3ce1d389b42b8d348dd1e10 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 27 Mar 2023 06:29:21 -0300 Subject: [PATCH 101/139] test: randomize askar wallet ids (#1405) Signed-off-by: Ariel Gentile --- packages/askar/tests/helpers.ts | 12 +++++++----- packages/core/tests/helpers.ts | 5 +++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index 3921e080ff..acae3d2d14 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -1,7 +1,7 @@ import type { AskarWalletPostgresStorageConfig } from '../src/wallet' import type { InitConfig } from '@aries-framework/core' -import { LogLevel } from '@aries-framework/core' +import { LogLevel, utils } from '@aries-framework/core' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import path from 'path' @@ -23,10 +23,11 @@ export function getPostgresAgentOptions( storageConfig: AskarWalletPostgresStorageConfig, extraConfig: Partial = {} ) { + const random = utils.uuid().slice(0, 4) const config: InitConfig = { - label: `Agent: ${name} Postgres`, + label: `PostgresAgent: ${name} - ${random}`, walletConfig: { - id: `Wallet${name}`, + id: `PostgresWallet${name}${random}`, key: `Key${name}`, storage: storageConfig, }, @@ -43,10 +44,11 @@ export function getPostgresAgentOptions( } export function getSqliteAgentOptions(name: string, extraConfig: Partial = {}) { + const random = utils.uuid().slice(0, 4) const config: InitConfig = { - label: `Agent: ${name} SQLite`, + label: `SQLiteAgent: ${name} - ${random}`, walletConfig: { - id: `Wallet${name}`, + id: `SQLiteWallet${name} - ${random}`, key: `Key${name}`, storage: { type: 'sqlite' }, }, diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 034c046aef..2da09477b7 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -94,11 +94,12 @@ export function getPostgresAgentOptions = {}, modules?: AgentModules ) { + const random = uuid().slice(0, 4) const config: InitConfig = { - label: `Agent: ${name}`, + label: `Agent: ${name} - ${random}`, walletConfig: { // NOTE: IndySDK Postgres database per wallet doesn't support special characters/spaces in the wallet name - id: `PostGresWallet${name}`, + id: `PostgresWallet${name}${random}`, key: `Key${name}`, storage: { type: 'postgres_storage', From 745950995fcc9ace7fc8e97839e9ad5a2afb5385 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Tue, 28 Mar 2023 14:53:16 +0200 Subject: [PATCH 102/139] test(migration): minor cleanup (#1403) Signed-off-by: blu3beri --- .../src/IndySdkToAskarMigrationUpdater.ts | 39 +---- .../tests/migrate.test.ts | 141 +++++++++--------- 2 files changed, 72 insertions(+), 108 deletions(-) diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index d57b506154..baf6a7ca6b 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -92,7 +92,7 @@ export class IndySdkToAskarMigrationUpdater { * This doubles checks some fields as later it might be possiblt to run this function */ private async migrate() { - const specUri = this.dbPath + const specUri = this.backupFile const kdfLevel = this.walletConfig.keyDerivationMethod ?? 'ARGON2I_MOD' const walletName = this.walletConfig.id const walletKey = this.walletConfig.key @@ -137,7 +137,7 @@ export class IndySdkToAskarMigrationUpdater { * Temporary backup location of the pre-migrated script */ private get backupFile() { - return `${this.fs.tempPath}/${this.walletConfig.id}.bak.db` + return `${this.fs.tempPath}/${this.walletConfig.id}.db` } /** @@ -161,35 +161,6 @@ export class IndySdkToAskarMigrationUpdater { } } - /** - * Reverts backed up database file to the original path, if its missing, and - * deletes the backup. We do some additional, possible redundant, exists checks - * here to be extra sure that only a happy flow occurs. - */ - private async restoreDatabase() { - // "Impossible" state. Since we do not continue if `this.backupDatabase()` - // fails, this file should always be there. If this error is thrown, we - // cannot correctly restore the state. - if (!(await this.fs.exists(this.backupFile))) { - throw new IndySdkToAskarMigrationError('Backup file could not be found while trying to restore the state') - } - - /** - * Since we used `copy` to get the file, it should still be there. We - * double-check here to be sure. - */ - if (!(await this.fs.exists(this.dbPath))) { - return - } else { - this.agent.config.logger.trace(`Moving '${this.backupFile}' back to the original path: '${this.dbPath}`) - - // Move the backedup file back to the original path - await this.fs.copyFile(this.backupFile, this.dbPath) - - this.agent.config.logger.trace(`Cleaned up the backed up file at '${this.backupFile}'`) - } - } - // Delete the backup as `this.fs.copyFile` only copies and no deletion // Since we use `tempPath` which is cleared when certain events happen, // e.g. cron-job and system restart (depending on the os) we could omit @@ -204,7 +175,7 @@ export class IndySdkToAskarMigrationUpdater { * to the `FileSystem.dataPath`. */ private async moveToNewLocation() { - const src = this.dbPath + const src = this.backupFile // New path for the database const dest = this.newWalletPath @@ -243,7 +214,7 @@ export class IndySdkToAskarMigrationUpdater { const keyMethod = this.walletConfig?.keyDerivationMethod == KeyDerivationMethod.Raw ? StoreKeyMethod.Raw : StoreKeyMethod.Kdf - this.store = await Store.open({ uri: `sqlite://${this.dbPath}`, passKey: this.walletConfig.key, keyMethod }) + this.store = await Store.open({ uri: `sqlite://${this.backupFile}`, passKey: this.walletConfig.key, keyMethod }) // Update the values to reflect the new structure await this.updateKeys() @@ -256,8 +227,6 @@ export class IndySdkToAskarMigrationUpdater { } catch (err) { this.agent.config.logger.error('Migration failed. Restoring state.') - await this.restoreDatabase() - throw new IndySdkToAskarMigrationError(`Migration failed. State has been restored. ${err.message}`, { cause: err.cause, }) diff --git a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts index a5bd7e87b2..ba78a901ec 100644 --- a/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts +++ b/packages/indy-sdk-to-askar-migration/tests/migrate.test.ts @@ -15,85 +15,52 @@ import { IndySdkToAskarMigrationError } from '../src/errors/IndySdkToAskarMigrat // FIXME: Re-include in tests when NodeJS wrapper performance is improved describeRunInNodeVersion([18], 'Indy SDK To Askar Migration', () => { - const config: InitConfig = { - label: 'test-agent', - walletConfig: { - id: `walletwallet.0-${utils.uuid()}`, - key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', - keyDerivationMethod: KeyDerivationMethod.Raw, - }, - } - - const invalidConfig: InitConfig = { - label: 'invalid-test-agent', - walletConfig: { - id: `walletwallet.1-${utils.uuid()}`, - key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', - keyDerivationMethod: KeyDerivationMethod.Raw, - }, - } - - const invalidAgent = new Agent({ - config: invalidConfig, - modules: { - indySdk: new IndySdkModule({ indySdk: indy }), - }, - dependencies: agentDependencies, - }) - - const invalidNewAgent = new Agent({ - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - config: { ...invalidConfig, walletConfig: { ...invalidConfig.walletConfig!, key: 'wrong-key' } }, - modules: { - askar: new AskarModule({ - ariesAskar, - }), - }, - dependencies: agentDependencies, - }) - - const oldAgent = new Agent({ - config, - modules: { - indySdk: new IndySdkModule({ indySdk: indy }), - }, - dependencies: agentDependencies, - }) - - const newAgent = new Agent({ - config, - modules: { - askar: new AskarModule({ - ariesAskar, - }), - }, - dependencies: agentDependencies, - }) - - const oldAgentDbPath = `${homedir()}/.indy_client/wallet/${oldAgent.config.walletConfig?.id}/sqlite.db` - const invalidAgentDbPath = `${homedir()}/.indy_client/wallet/${invalidAgent.config.walletConfig?.id}/sqlite.db` - beforeAll(() => { registerAriesAskar({ askar: ariesAskar }) }) - test('indy-sdk sqlite to aries-askar sqlite', async () => { + test('indy-sdk sqlite to aries-askar sqlite successful migration', async () => { + const indySdkAndAskarConfig: InitConfig = { + label: `indy | indy-sdk sqlite to aries-askar sqlite successful migration | ${utils.uuid()}`, + walletConfig: { + id: `indy-sdk sqlite to aries-askar sqlite successful migration | ${utils.uuid()}`, + key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', + keyDerivationMethod: KeyDerivationMethod.Raw, + }, + } + + const indySdkAgent = new Agent({ + config: indySdkAndAskarConfig, + modules: { indySdk: new IndySdkModule({ indySdk: indy }) }, + dependencies: agentDependencies, + }) + + const indySdkAgentDbPath = `${homedir()}/.indy_client/wallet/${indySdkAndAskarConfig.walletConfig?.id}/sqlite.db` + const genericRecordContent = { foo: 'bar' } - await oldAgent.initialize() + await indySdkAgent.initialize() + + const record = await indySdkAgent.genericRecords.save({ content: genericRecordContent }) - const record = await oldAgent.genericRecords.save({ content: genericRecordContent }) + await indySdkAgent.shutdown() - await oldAgent.shutdown() + const askarAgent = new Agent({ + config: indySdkAndAskarConfig, + modules: { askar: new AskarModule({ ariesAskar }) }, + dependencies: agentDependencies, + }) - const updater = await IndySdkToAskarMigrationUpdater.initialize({ dbPath: oldAgentDbPath, agent: newAgent }) + const updater = await IndySdkToAskarMigrationUpdater.initialize({ dbPath: indySdkAgentDbPath, agent: askarAgent }) await updater.update() - await newAgent.initialize() + await askarAgent.initialize() - await expect(newAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ content: genericRecordContent }) + await expect(askarAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ + content: genericRecordContent, + }) - await newAgent.shutdown() + await askarAgent.shutdown() }) /* @@ -104,24 +71,52 @@ describeRunInNodeVersion([18], 'Indy SDK To Askar Migration', () => { * - Check if the record can still be accessed */ test('indy-sdk sqlite to aries-askar sqlite fails and restores', async () => { + const indySdkAndAskarConfig: InitConfig = { + label: `indy | indy-sdk sqlite to aries-askar sqlite fails and restores | ${utils.uuid()}`, + walletConfig: { + id: `indy-sdk sqlite to aries-askar sqlite fails and restores | ${utils.uuid()}`, + key: 'GfwU1DC7gEZNs3w41tjBiZYj7BNToDoFEqKY6wZXqs1A', + keyDerivationMethod: KeyDerivationMethod.Raw, + }, + } + + const indySdkAgent = new Agent({ + config: indySdkAndAskarConfig, + modules: { indySdk: new IndySdkModule({ indySdk: indy }) }, + dependencies: agentDependencies, + }) + + const indySdkAgentDbPath = `${homedir()}/.indy_client/wallet/${indySdkAndAskarConfig.walletConfig?.id}/sqlite.db` + const genericRecordContent = { foo: 'bar' } - await invalidAgent.initialize() + await indySdkAgent.initialize() - const record = await invalidAgent.genericRecords.save({ content: genericRecordContent }) + const record = await indySdkAgent.genericRecords.save({ content: genericRecordContent }) - await invalidAgent.shutdown() + await indySdkAgent.shutdown() + + const askarAgent = new Agent({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + config: { ...indySdkAndAskarConfig, walletConfig: { ...indySdkAndAskarConfig.walletConfig!, key: 'wrong-key' } }, + modules: { + askar: new AskarModule({ + ariesAskar, + }), + }, + dependencies: agentDependencies, + }) const updater = await IndySdkToAskarMigrationUpdater.initialize({ - dbPath: invalidAgentDbPath, - agent: invalidNewAgent, + dbPath: indySdkAgentDbPath, + agent: askarAgent, }) await expect(updater.update()).rejects.toThrowError(IndySdkToAskarMigrationError) - await invalidAgent.initialize() + await indySdkAgent.initialize() - await expect(invalidAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ + await expect(indySdkAgent.genericRecords.findById(record.id)).resolves.toMatchObject({ content: genericRecordContent, }) }) From 3bff26d703f4661fb612783aedcaf5c9d92c2a37 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Wed, 29 Mar 2023 14:45:43 -0300 Subject: [PATCH 103/139] test(indy-vdr): add delay after DID creation (#1406) Signed-off-by: Ariel Gentile --- packages/indy-vdr/tests/helpers.ts | 4 ++++ .../tests/indy-vdr-did-registrar.e2e.test.ts | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index 1d1b94494e..304118e2a8 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -4,6 +4,7 @@ import type { Agent } from '@aries-framework/core' import { DidCommV1Service, DidCommV2Service, DidDocumentService, KeyType } from '@aries-framework/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' +import { sleep } from '../../core/src/utils/sleep' import { genesisTransactions } from '../../core/tests/helpers' import { IndyVdrModuleConfig } from '../src/IndyVdrModuleConfig' @@ -60,5 +61,8 @@ export async function createDidOnLedger(agent: Agent, submitterDid: string) { ) } + // Wait some time pass to let ledger settle the object + await sleep(1000) + return { did: createResult.didState.did, key } } diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index b4b66f4449..dbf68311d0 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -14,6 +14,7 @@ import { import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { convertPublicKeyToX25519, generateKeyPairFromSeed } from '@stablelib/ed25519' +import { sleep } from '../../core/src/utils/sleep' import { getAgentOptions, importExistingIndyDidFromPrivateKey } from '../../core/tests/helpers' import { IndySdkModule } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' @@ -100,6 +101,9 @@ describe('Indy VDR Indy Did Registrar', () => { const did = didRegistrationResult.didState.did if (!did) throw Error('did not defined') + // Wait some time pass to let ledger settle the object + await sleep(1000) + const didResolutionResult = await agent.dids.resolve(did) expect(JsonTransformer.toJSON(didResolutionResult)).toMatchObject({ didDocument: { @@ -149,6 +153,9 @@ describe('Indy VDR Indy Did Registrar', () => { }, }) + // Wait some time pass to let ledger settle the object + await sleep(1000) + expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -176,6 +183,9 @@ describe('Indy VDR Indy Did Registrar', () => { }, }) + // Wait some time pass to let ledger settle the object + await sleep(1000) + const didResult = await agent.dids.resolve(did) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: { @@ -313,6 +323,9 @@ describe('Indy VDR Indy Did Registrar', () => { }, }) + // Wait some time pass to let ledger settle the object + await sleep(1000) + const didResult = await agent.dids.resolve(did) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: expectedDidDocument, From bc5455f7b42612a2b85e504bc6ddd36283a42bfa Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 29 Mar 2023 22:11:34 +0200 Subject: [PATCH 104/139] feat: 0.4.0 migration script (#1392) Signed-off-by: Timo Glastra --- .../anoncreds-rs/tests/anoncreds-flow.test.ts | 6 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 6 +- packages/anoncreds/src/AnonCredsModule.ts | 11 +- .../legacy-indy-format-services.test.ts | 6 +- .../v1-connectionless-credentials.e2e.test.ts | 6 +- .../v1-credentials-auto-accept.e2e.test.ts | 16 +- .../AnonCredsCredentialDefinitionRecord.ts | 4 +- .../src/repository/AnonCredsSchemaRecord.ts | 4 +- .../__tests__/credentialDefinition.test.ts | 154 ++++ .../credentialExchangeRecord.test.ts | 160 ++++ .../0.3.1-0.4/__tests__/linkSecret.test.ts | 76 ++ .../0.3.1-0.4/__tests__/schema.test.ts | 117 +++ .../updates/0.3.1-0.4/credentialDefinition.ts | 93 ++ .../0.3.1-0.4/credentialExchangeRecord.ts | 148 ++++ .../anoncreds/src/updates/0.3.1-0.4/index.ts | 13 + .../src/updates/0.3.1-0.4/linkSecret.ts | 43 + .../anoncreds/src/updates/0.3.1-0.4/schema.ts | 62 ++ .../src/updates/__tests__/0.3.test.ts | 239 ++++++ .../holder-anoncreds-2-credentials-0.3.json | 304 +++++++ ...credential-definition-credentials-0.3.json | 431 ++++++++++ .../__tests__/__snapshots__/0.3.test.ts.snap | 812 ++++++++++++++++++ packages/anoncreds/src/utils/metadata.ts | 4 +- packages/askar/src/wallet/AskarWallet.ts | 10 + packages/core/src/error/IndySdkError.ts | 11 - packages/core/src/error/index.ts | 1 - .../v2-connectionless-credentials.e2e.test.ts | 6 +- .../v2-credentials-auto-accept.e2e.test.ts | 16 +- packages/core/src/plugins/Module.ts | 6 + .../src/storage/migration/UpdateAssistant.ts | 42 +- .../storage/migration/__tests__/0.2.test.ts | 2 +- .../storage/migration/__tests__/0.3.test.ts | 21 +- ...alice-2-sov-dids-one-cache-record-0.3.json | 87 ++ .../__tests__/__snapshots__/0.2.test.ts.snap | 6 +- .../__tests__/__snapshots__/0.3.test.ts.snap | 518 +---------- .../__snapshots__/backup-askar.test.ts.snap | 196 +++++ .../migration/__tests__/backup-askar.test.ts | 168 ++++ packages/core/src/storage/migration/index.ts | 1 + .../core/src/storage/migration/updates.ts | 6 + .../updates/0.3.1-0.4/__tests__/cache.test.ts | 53 ++ .../updates/0.3.1-0.4/__tests__/did.test.ts | 81 ++ .../migration/updates/0.3.1-0.4/cache.ts | 32 + .../migration/updates/0.3.1-0.4/did.ts | 51 ++ .../migration/updates/0.3.1-0.4/index.ts | 9 + .../src/utils/__tests__/indyError.test.ts | 52 -- packages/core/src/utils/indyError.ts | 100 --- packages/core/src/wallet/Wallet.ts | 6 + .../error/WalletExportPathExistsError.ts | 7 + packages/core/src/wallet/error/index.ts | 1 + .../indy-sdk/src/anoncreds/utils/tails.ts | 4 +- packages/indy-sdk/src/wallet/IndySdkWallet.ts | 10 + .../src/dids/IndyVdrIndyDidRegistrar.ts | 1 - 51 files changed, 3504 insertions(+), 715 deletions(-) create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialDefinition.test.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/__tests__/linkSecret.test.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/__tests__/schema.test.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/index.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts create mode 100644 packages/anoncreds/src/updates/0.3.1-0.4/schema.ts create mode 100644 packages/anoncreds/src/updates/__tests__/0.3.test.ts create mode 100644 packages/anoncreds/src/updates/__tests__/__fixtures__/holder-anoncreds-2-credentials-0.3.json create mode 100644 packages/anoncreds/src/updates/__tests__/__fixtures__/issuer-anoncreds-2-schema-credential-definition-credentials-0.3.json create mode 100644 packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap delete mode 100644 packages/core/src/error/IndySdkError.ts create mode 100644 packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-sov-dids-one-cache-record-0.3.json create mode 100644 packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap create mode 100644 packages/core/src/storage/migration/__tests__/backup-askar.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/cache.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/did.test.ts create mode 100644 packages/core/src/storage/migration/updates/0.3.1-0.4/cache.ts create mode 100644 packages/core/src/storage/migration/updates/0.3.1-0.4/did.ts create mode 100644 packages/core/src/storage/migration/updates/0.3.1-0.4/index.ts delete mode 100644 packages/core/src/utils/__tests__/indyError.test.ts delete mode 100644 packages/core/src/utils/indyError.ts create mode 100644 packages/core/src/wallet/error/WalletExportPathExistsError.ts diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts index 493d80daff..84a440213d 100644 --- a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts @@ -271,11 +271,11 @@ describeRunInNodeVersion([18], 'AnonCreds format services using anoncreds-rs', ( }) expect(holderCredentialRecord.metadata.data).toEqual({ - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }, - '_anonCreds/anonCredsCredentialRequest': { + '_anoncreds/credentialRequest': { master_secret_blinding_data: expect.any(Object), master_secret_name: expect.any(String), nonce: expect.any(String), @@ -283,7 +283,7 @@ describeRunInNodeVersion([18], 'AnonCreds format services using anoncreds-rs', ( }) expect(issuerCredentialRecord.metadata.data).toEqual({ - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }, diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index cad6411f59..00fed973f1 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -271,11 +271,11 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', }) expect(holderCredentialRecord.metadata.data).toEqual({ - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }, - '_anonCreds/anonCredsCredentialRequest': { + '_anoncreds/credentialRequest': { master_secret_blinding_data: expect.any(Object), master_secret_name: expect.any(String), nonce: expect.any(String), @@ -283,7 +283,7 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', }) expect(issuerCredentialRecord.metadata.data).toEqual({ - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: schemaState.schemaId, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, }, diff --git a/packages/anoncreds/src/AnonCredsModule.ts b/packages/anoncreds/src/AnonCredsModule.ts index 3d6eff0b74..873288348c 100644 --- a/packages/anoncreds/src/AnonCredsModule.ts +++ b/packages/anoncreds/src/AnonCredsModule.ts @@ -1,5 +1,5 @@ import type { AnonCredsModuleConfigOptions } from './AnonCredsModuleConfig' -import type { DependencyManager, Module } from '@aries-framework/core' +import type { DependencyManager, Module, Update } from '@aries-framework/core' import { AnonCredsApi } from './AnonCredsApi' import { AnonCredsModuleConfig } from './AnonCredsModuleConfig' @@ -11,6 +11,7 @@ import { import { AnonCredsCredentialDefinitionRepository } from './repository/AnonCredsCredentialDefinitionRepository' import { AnonCredsSchemaRepository } from './repository/AnonCredsSchemaRepository' import { AnonCredsRegistryService } from './services/registry/AnonCredsRegistryService' +import { updateAnonCredsModuleV0_3_1ToV0_4 } from './updates/0.3.1-0.4' /** * @public @@ -36,4 +37,12 @@ export class AnonCredsModule implements Module { dependencyManager.registerSingleton(AnonCredsKeyCorrectnessProofRepository) dependencyManager.registerSingleton(AnonCredsLinkSecretRepository) } + + public updates = [ + { + fromVersion: '0.3.1', + toVersion: '0.4', + doUpdate: updateAnonCredsModuleV0_3_1ToV0_4, + }, + ] satisfies Update[] } diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index af5686ad5d..c854914f38 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -245,11 +245,11 @@ describe('Legacy indy format services', () => { }) expect(holderCredentialRecord.metadata.data).toEqual({ - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: legacySchemaId, credentialDefinitionId: legacyCredentialDefinitionId, }, - '_anonCreds/anonCredsCredentialRequest': { + '_anoncreds/credentialRequest': { master_secret_blinding_data: expect.any(Object), master_secret_name: expect.any(String), nonce: expect.any(String), @@ -257,7 +257,7 @@ describe('Legacy indy format services', () => { }) expect(issuerCredentialRecord.metadata.data).toEqual({ - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: legacySchemaId, credentialDefinitionId: legacyCredentialDefinitionId, }, diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts index 975db00a6e..5825f0610c 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-connectionless-credentials.e2e.test.ts @@ -114,7 +114,7 @@ describe('V1 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId, credentialDefinitionId, }, @@ -136,7 +136,7 @@ describe('V1 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId, credentialDefinitionId, }, @@ -197,7 +197,7 @@ describe('V1 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts index 6c0455755c..852d0cc116 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/v1-credentials-auto-accept.e2e.test.ts @@ -89,7 +89,7 @@ describe('V1 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId: schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -128,8 +128,8 @@ describe('V1 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredentialRequest': expect.any(Object), - '_anonCreds/anonCredsCredential': { + '_anoncreds/credentialRequest': expect.any(Object), + '_anoncreds/credential': { schemaId, credentialDefinitionId, }, @@ -230,8 +230,8 @@ describe('V1 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredentialRequest': expect.any(Object), - '_anonCreds/anonCredsCredential': { + '_anoncreds/credentialRequest': expect.any(Object), + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -252,7 +252,7 @@ describe('V1 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -318,8 +318,8 @@ describe('V1 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredentialRequest': expect.any(Object), - '_anonCreds/anonCredsCredential': { + '_anoncreds/credentialRequest': expect.any(Object), + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts index f9c7df43f7..bbacfb4f73 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts @@ -25,8 +25,8 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord< public static readonly type = 'AnonCredsCredentialDefinitionRecord' public readonly type = AnonCredsCredentialDefinitionRecord.type - public readonly credentialDefinitionId!: string - public readonly credentialDefinition!: AnonCredsCredentialDefinition + public credentialDefinitionId!: string + public credentialDefinition!: AnonCredsCredentialDefinition public constructor(props: AnonCredsCredentialDefinitionRecordProps) { super() diff --git a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts index 13ad5d757c..9a5499951e 100644 --- a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts @@ -25,8 +25,8 @@ export class AnonCredsSchemaRecord extends BaseRecord< public static readonly type = 'AnonCredsSchemaRecord' public readonly type = AnonCredsSchemaRecord.type - public readonly schemaId!: string - public readonly schema!: AnonCredsSchema + public schemaId!: string + public schema!: AnonCredsSchema public constructor(props: AnonCredsSchemaRecordProps) { super() diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialDefinition.test.ts b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialDefinition.test.ts new file mode 100644 index 0000000000..65b40bcddb --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialDefinition.test.ts @@ -0,0 +1,154 @@ +import type { AnonCredsCredentialDefinition } from '../../../models' + +import { JsonTransformer } from '../../../../../core/src' +import { Agent } from '../../../../../core/src/agent/Agent' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../core/tests' +import { InMemoryAnonCredsRegistry } from '../../../../tests/InMemoryAnonCredsRegistry' +import { AnonCredsCredentialDefinitionRecord } from '../../../repository' +import { AnonCredsCredentialDefinitionRepository } from '../../../repository/AnonCredsCredentialDefinitionRepository' +import * as testModule from '../credentialDefinition' + +const agentConfig = getAgentConfig('AnonCreds Migration - Credential Exchange Record - 0.3.1-0.4.0') +const agentContext = getAgentContext() + +jest.mock('../../../repository/AnonCredsCredentialDefinitionRepository') +const AnonCredsCredentialDefinitionRepositoryMock = + AnonCredsCredentialDefinitionRepository as jest.Mock +const credentialDefinitionRepository = new AnonCredsCredentialDefinitionRepositoryMock() + +const inMemoryAnonCredsRegistry = new InMemoryAnonCredsRegistry({ + existingCredentialDefinitions: { + 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/CLAIM_DEF/104/default': { + schemaId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/credentialDefinition-name/1.0', + tag: 'default', + type: 'CL', + value: { + primary: { + master_secret: '119999 00192381', + }, + }, + issuerId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + }, + }, +}) + +const registryService = { + getRegistryForIdentifier: () => inMemoryAnonCredsRegistry, +} +jest.mock('../../../../../core/src/agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn((injectionSymbol) => + injectionSymbol === AnonCredsCredentialDefinitionRepository ? credentialDefinitionRepository : registryService + ), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.3.1-0.4.0 | AnonCreds Migration | Credential Definition Record', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('migrateAnonCredsCredentialDefinitionRecordToV0_4()', () => { + it('should fetch all records and apply the needed updates', async () => { + const records: AnonCredsCredentialDefinitionRecord[] = [ + getCredentialDefinitionRecord({ + credentialDefinition: { + id: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/CLAIM_DEF/104/default', + schemaId: '104', + tag: 'default', + type: 'CL', + value: { + primary: { + master_secret: '119999 00192381', + }, + }, + ver: '1.0', + }, + }), + ] + + mockFunction(credentialDefinitionRepository.getAll).mockResolvedValue(records) + + await testModule.migrateAnonCredsCredentialDefinitionRecordToV0_4(agent) + + expect(credentialDefinitionRepository.getAll).toHaveBeenCalledTimes(1) + expect(credentialDefinitionRepository.update).toHaveBeenCalledTimes(1) + + const [, credentialDefinitionRecord] = mockFunction(credentialDefinitionRepository.update).mock.calls[0] + expect(credentialDefinitionRecord.toJSON()).toMatchObject({ + credentialDefinitionId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/CLAIM_DEF/104/default', + credentialDefinition: { + schemaId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/credentialDefinition-name/1.0', + tag: 'default', + type: 'CL', + value: { + primary: { + master_secret: '119999 00192381', + }, + }, + issuerId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + }, + }) + }) + + it('should skip records that are already migrated to the 0.4.0 format', async () => { + const records: AnonCredsCredentialDefinitionRecord[] = [ + getCredentialDefinitionRecord({ + credentialDefinitionId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/CLAIM_DEF/104/default', + credentialDefinition: { + schemaId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/credentialDefinition-name/1.0', + tag: 'default', + type: 'CL', + value: { + primary: { + master_secret: '119999 00192381', + }, + }, + issuerId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + }, + }), + ] + + mockFunction(credentialDefinitionRepository.getAll).mockResolvedValue(records) + + await testModule.migrateAnonCredsCredentialDefinitionRecordToV0_4(agent) + + expect(credentialDefinitionRepository.getAll).toHaveBeenCalledTimes(1) + expect(credentialDefinitionRepository.update).toHaveBeenCalledTimes(0) + }) + }) +}) + +function getCredentialDefinitionRecord({ + id, + credentialDefinition, + credentialDefinitionId, +}: { + id?: string + credentialDefinition: testModule.OldCredentialDefinition | AnonCredsCredentialDefinition + credentialDefinitionId?: string +}) { + return JsonTransformer.fromJSON( + { + id: id ?? 'credentialDefinition-record-id', + credentialDefinition, + credentialDefinitionId, + }, + AnonCredsCredentialDefinitionRecord + ) +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts new file mode 100644 index 0000000000..74af775b10 --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts @@ -0,0 +1,160 @@ +import type { CredentialRecordBinding } from '../../../../../core/src' + +import { CredentialExchangeRecord, JsonTransformer } from '../../../../../core/src' +import { Agent } from '../../../../../core/src/agent/Agent' +import { CredentialRepository } from '../../../../../core/src/modules/credentials/repository/CredentialRepository' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../core/tests' +import { + migrateIndyCredentialMetadataToAnonCredsMetadata, + migrateIndyCredentialTypeToAnonCredsCredential, +} from '../credentialExchangeRecord' +import * as testModule from '../credentialExchangeRecord' + +const agentConfig = getAgentConfig('AnonCreds Migration - Credential Exchange Record - 0.3.1-0.4.0') +const agentContext = getAgentContext() + +jest.mock('../../../../../core/src/modules/credentials/repository/CredentialRepository') +const CredentialRepositoryMock = CredentialRepository as jest.Mock +const credentialRepository = new CredentialRepositoryMock() + +jest.mock('../../../../../core/src/agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn(() => credentialRepository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.3.1-0.4.0 | AnonCreds Migration | Credential Exchange Record', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + describe('migrateCredentialExchangeRecordToV0_4()', () => { + it('should fetch all records and apply the needed updates ', async () => { + const records: CredentialExchangeRecord[] = [ + getCredentialRecord({ + metadata: { + '_internal/indyCredential': { some: 'value' }, + '_internal/indyRequest': { another: 'value' }, + }, + credentials: [ + { + credentialRecordId: 'credential-id', + credentialRecordType: 'indy', + }, + { + credentialRecordId: 'credential-id2', + credentialRecordType: 'jsonld', + }, + ], + }), + ] + + mockFunction(credentialRepository.getAll).mockResolvedValue(records) + + await testModule.migrateCredentialExchangeRecordToV0_4(agent) + + expect(credentialRepository.getAll).toHaveBeenCalledTimes(1) + expect(credentialRepository.update).toHaveBeenCalledTimes(1) + + const [, credentialRecord] = mockFunction(credentialRepository.update).mock.calls[0] + expect(credentialRecord.toJSON()).toMatchObject({ + metadata: { + '_anoncreds/credential': { some: 'value' }, + '_anoncreds/credentialRequest': { another: 'value' }, + }, + credentials: [ + { + credentialRecordId: 'credential-id', + credentialRecordType: 'anoncreds', + }, + { + credentialRecordId: 'credential-id2', + credentialRecordType: 'jsonld', + }, + ], + }) + }) + }) + + describe('migrateIndyCredentialMetadataToAnonCredsMetadata()', () => { + test('updates indy metadata to anoncreds metadata', () => { + const record = getCredentialRecord({ + metadata: { + '_internal/indyCredential': { some: 'value' }, + '_internal/indyRequest': { another: 'value' }, + }, + }) + + migrateIndyCredentialMetadataToAnonCredsMetadata(agent, record) + + expect(record.toJSON()).toMatchObject({ + metadata: { + '_anoncreds/credential': { some: 'value' }, + '_anoncreds/credentialRequest': { another: 'value' }, + }, + }) + }) + }) + + describe('migrateIndyCredentialTypeToAnonCredsCredential()', () => { + test('updates indy credential record binding to anoncreds binding', () => { + const record = getCredentialRecord({ + credentials: [ + { + credentialRecordId: 'credential-id', + credentialRecordType: 'indy', + }, + { + credentialRecordId: 'credential-id2', + credentialRecordType: 'jsonld', + }, + ], + }) + + migrateIndyCredentialTypeToAnonCredsCredential(agent, record) + + expect(record.toJSON()).toMatchObject({ + credentials: [ + { + credentialRecordId: 'credential-id', + credentialRecordType: 'anoncreds', + }, + { + credentialRecordId: 'credential-id2', + credentialRecordType: 'jsonld', + }, + ], + }) + }) + }) +}) + +function getCredentialRecord({ + id, + metadata, + credentials, +}: { + id?: string + metadata?: Record + credentials?: CredentialRecordBinding[] +}) { + return JsonTransformer.fromJSON( + { + id: id ?? 'credential-id', + metadata, + credentials, + }, + CredentialExchangeRecord + ) +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/linkSecret.test.ts b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/linkSecret.test.ts new file mode 100644 index 0000000000..200e81914b --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/linkSecret.test.ts @@ -0,0 +1,76 @@ +import { Agent } from '../../../../../core/src/agent/Agent' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../core/tests' +import { AnonCredsLinkSecretRecord } from '../../../repository' +import { AnonCredsLinkSecretRepository } from '../../../repository/AnonCredsLinkSecretRepository' +import * as testModule from '../linkSecret' + +const agentConfig = getAgentConfig('AnonCreds Migration - Link Secret - 0.3.1-0.4.0') +const agentContext = getAgentContext() + +jest.mock('../../../repository/AnonCredsLinkSecretRepository') +const AnonCredsLinkSecretRepositoryMock = AnonCredsLinkSecretRepository as jest.Mock +const linkSecretRepository = new AnonCredsLinkSecretRepositoryMock() + +jest.mock('../../../../../core/src/agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + wallet: { + walletConfig: { + id: 'wallet-id', + }, + }, + dependencyManager: { + resolve: jest.fn(() => linkSecretRepository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.3.1-0.4.0 | AnonCreds Migration | Link Secret', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('migrateLinkSecretToV0_4()', () => { + test('creates default link secret record based on wallet id if no default link secret exists', async () => { + mockFunction(linkSecretRepository.findDefault).mockResolvedValue(null) + + await testModule.migrateLinkSecretToV0_4(agent) + + expect(linkSecretRepository.findDefault).toHaveBeenCalledTimes(1) + expect(linkSecretRepository.save).toHaveBeenCalledTimes(1) + + const [, linkSecretRecord] = mockFunction(linkSecretRepository.save).mock.calls[0] + expect(linkSecretRecord.toJSON()).toMatchObject({ + linkSecretId: 'wallet-id', + }) + expect(linkSecretRecord.getTags()).toMatchObject({ + isDefault: true, + }) + }) + + test('does not create default link secret record if default link secret record already exists', async () => { + mockFunction(linkSecretRepository.findDefault).mockResolvedValue( + new AnonCredsLinkSecretRecord({ + linkSecretId: 'some-link-secret-id', + }) + ) + + await testModule.migrateLinkSecretToV0_4(agent) + + expect(linkSecretRepository.findDefault).toHaveBeenCalledTimes(1) + expect(linkSecretRepository.update).toHaveBeenCalledTimes(0) + }) + }) +}) diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/schema.test.ts b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/schema.test.ts new file mode 100644 index 0000000000..0c2f2fd46e --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/schema.test.ts @@ -0,0 +1,117 @@ +import type { AnonCredsSchema } from '../../../models' + +import { JsonTransformer } from '../../../../../core/src' +import { Agent } from '../../../../../core/src/agent/Agent' +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../core/tests' +import { AnonCredsSchemaRecord } from '../../../repository' +import { AnonCredsSchemaRepository } from '../../../repository/AnonCredsSchemaRepository' +import * as testModule from '../schema' + +const agentConfig = getAgentConfig('AnonCreds Migration - Credential Exchange Record - 0.3.1-0.4.0') +const agentContext = getAgentContext() + +jest.mock('../../../repository/AnonCredsSchemaRepository') +const AnonCredsSchemaRepositoryMock = AnonCredsSchemaRepository as jest.Mock +const schemaRepository = new AnonCredsSchemaRepositoryMock() + +jest.mock('../../../../../core/src/agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn(() => schemaRepository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.3.1-0.4.0 | AnonCreds Migration | Schema Record', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('migrateAnonCredsSchemaRecordToV0_4()', () => { + it('should fetch all records and apply the needed updates', async () => { + const records: AnonCredsSchemaRecord[] = [ + getSchemaRecord({ + schema: { + attrNames: ['name', 'age'], + id: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0', + name: 'schema-name', + seqNo: 1, + version: '1.0', + ver: '1.0', + }, + }), + ] + + mockFunction(schemaRepository.getAll).mockResolvedValue(records) + + await testModule.migrateAnonCredsSchemaRecordToV0_4(agent) + + expect(schemaRepository.getAll).toHaveBeenCalledTimes(1) + expect(schemaRepository.update).toHaveBeenCalledTimes(1) + + const [, schemaRecord] = mockFunction(schemaRepository.update).mock.calls[0] + expect(schemaRecord.toJSON()).toMatchObject({ + schemaId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0', + schema: { + attrNames: ['name', 'age'], + name: 'schema-name', + version: '1.0', + issuerId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + }, + }) + }) + + it('should skip records that are already migrated to the 0.4.0 format', async () => { + const records: AnonCredsSchemaRecord[] = [ + getSchemaRecord({ + schema: { + attrNames: ['name', 'age'], + name: 'schema-name', + version: '1.0', + issuerId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + }, + schemaId: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0', + }), + ] + + mockFunction(schemaRepository.getAll).mockResolvedValue(records) + + await testModule.migrateAnonCredsSchemaRecordToV0_4(agent) + + expect(schemaRepository.getAll).toHaveBeenCalledTimes(1) + expect(schemaRepository.update).toHaveBeenCalledTimes(0) + }) + }) +}) + +function getSchemaRecord({ + id, + schema, + schemaId, +}: { + id?: string + schema: testModule.OldSchema | AnonCredsSchema + schemaId?: string +}) { + return JsonTransformer.fromJSON( + { + id: id ?? 'schema-record-id', + schema, + schemaId, + }, + AnonCredsSchemaRecord + ) +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts new file mode 100644 index 0000000000..bd0f3efd49 --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts @@ -0,0 +1,93 @@ +import type { AnonCredsCredentialDefinition } from '../../models' +import type { BaseAgent } from '@aries-framework/core' + +import { AriesFrameworkError } from '@aries-framework/core' + +import { AnonCredsCredentialDefinitionRepository } from '../../repository' +import { AnonCredsRegistryService } from '../../services/registry/AnonCredsRegistryService' + +/** + * Migrates the {@link AnonCredsCredentialDefinitionRecord} to 0.4 compatible format. It fetches all credential definition records from + * storage and updates the format based on the new ledger agnostic anoncreds models. After a record has been transformed, + * it is updated in storage and the next record will be transformed. + */ +export async function migrateAnonCredsCredentialDefinitionRecordToV0_4(agent: Agent) { + agent.config.logger.info('Migrating AnonCredsCredentialDefinitionRecord records to storage version 0.4') + const credentialDefinitionRepository = agent.dependencyManager.resolve(AnonCredsCredentialDefinitionRepository) + + agent.config.logger.debug(`Fetching all credential definition records from storage`) + const credentialDefinitionRecords = await credentialDefinitionRepository.getAll(agent.context) + + agent.config.logger.debug( + `Found a total of ${credentialDefinitionRecords.length} credential definition records to update.` + ) + + for (const credentialDefinitionRecord of credentialDefinitionRecords) { + const oldCredentialDefinition = + credentialDefinitionRecord.credentialDefinition as unknown as OldCredentialDefinition + + // If askar migration script is ran, it could be that the credential definition record is already in 0.4 format + if (oldCredentialDefinition.id === undefined) { + agent.config.logger.info( + `Credential definition record with id ${credentialDefinitionRecord.id} and credential definition id ${credentialDefinitionRecord.credentialDefinitionId} is already in storage version 0.4 format. Probably due to Indy SDK to Askar migration. Skipping...` + ) + continue + } + + agent.config.logger.debug( + `Migrating anoncreds credential definition record with id ${credentialDefinitionRecord.id} and credential definition id ${oldCredentialDefinition.id} to storage version 0.4` + ) + + // the schemaId is actually the ledger seqNo. We'll have to fetch the schema from the ledger to get the schemaId + // However, we can't just fetch the schema by it's seqNo, so we'll actually fetch the credential definition, + // which will contain the valid schemaId + const registryService = agent.dependencyManager.resolve(AnonCredsRegistryService) + const registry = registryService.getRegistryForIdentifier(agent.context, oldCredentialDefinition.id) + agent.config.logger.debug( + `Using registry with supportedIdentifier ${registry.supportedIdentifier} to resolve credential definition` + ) + + const { credentialDefinition } = await registry.getCredentialDefinition(agent.context, oldCredentialDefinition.id) + if (!credentialDefinition) { + agent.config.logger.error( + `Could not resolve credential definition with id ${oldCredentialDefinition.id} from ledger` + ) + throw new AriesFrameworkError(`Unable to resolve credential definition ${oldCredentialDefinition.id}`) + } + + agent.config.logger.debug(`Resolved credential definition with id ${oldCredentialDefinition.id} from ledger`, { + credentialDefinition, + }) + + const newCredentialDefinition = { + // Use the schemaId from the resolved credential definition so we get the qualified identifier + schemaId: credentialDefinition.schemaId, + tag: oldCredentialDefinition.tag, + type: oldCredentialDefinition.type, + value: oldCredentialDefinition.value, + issuerId: oldCredentialDefinition.id.split('/')[0], + } satisfies AnonCredsCredentialDefinition + + credentialDefinitionRecord.credentialDefinition = newCredentialDefinition + credentialDefinitionRecord.credentialDefinitionId = oldCredentialDefinition.id + + // Save updated credentialDefinition record + await credentialDefinitionRepository.update(agent.context, credentialDefinitionRecord) + + agent.config.logger.debug( + `Successfully migrated credential definition record with id ${credentialDefinitionRecord.id} to storage version 0.4` + ) + } +} + +export interface OldCredentialDefinition { + id: string + schemaId: string + type: 'CL' + tag: string + value: { + primary: Record + revocation?: unknown | undefined + } + ver: string +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts b/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts new file mode 100644 index 0000000000..526270d81f --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts @@ -0,0 +1,148 @@ +import type { BaseAgent, CredentialExchangeRecord } from '@aries-framework/core' + +import { CredentialRepository } from '@aries-framework/core' + +/** + * Migrates the {@link CredentialExchangeRecord} to 0.4 compatible format. It fetches all credential exchange records from + * storage and applies the needed updates to the records. After a record has been transformed, it is updated + * in storage and the next record will be transformed. + * + * The following transformations are applied: + * - {@link migrateIndyCredentialMetadataToAnonCredsMetadata} + * - {@link migrateIndyCredentialTypeToAnonCredsCredential} + */ +export async function migrateCredentialExchangeRecordToV0_4(agent: Agent) { + agent.config.logger.info('Migrating credential exchange records to storage version 0.4') + const credentialRepository = agent.dependencyManager.resolve(CredentialRepository) + + agent.config.logger.debug(`Fetching all credential records from storage`) + const credentialRecords = await credentialRepository.getAll(agent.context) + + agent.config.logger.debug(`Found a total of ${credentialRecords.length} credential exchange records to update.`) + for (const credentialRecord of credentialRecords) { + agent.config.logger.debug( + `Migrating credential exchange record with id ${credentialRecord.id} to storage version 0.4` + ) + + migrateIndyCredentialTypeToAnonCredsCredential(agent, credentialRecord) + migrateIndyCredentialMetadataToAnonCredsMetadata(agent, credentialRecord) + + // Save updated did record + await credentialRepository.update(agent.context, credentialRecord) + + agent.config.logger.debug( + `Successfully migrated credential exchange record with id ${credentialRecord.id} to storage version 0.4` + ) + } +} + +/** + * Migrates the indy credential record binding to anoncreds credential record binding. + * + * The following 0.3.1 credential record structure (unrelated keys omitted): + * + * ```json + * { + * "credentials": [ + * { + * "credentialRecordId": "credential-id", + * "credentialRecordType": "indy" + * }, + * { + * "credentialRecordId": "credential-id2", + * "credentialRecordType": "jsonld" + * } + * ] + * } + * ``` + * + * Wil be tranformed into the following 0.4 credential record structure (unrelated keys omitted): + * ```json + * { + * "credentials": [ + * { + * "credentialRecordId": "credential-id", + * "credentialRecordType": "anoncreds" + * }, + * { + * "credentialRecordId": "credential-id2", + * "credentialRecordType": "jsonld" + * } + * ] + * } + * ``` + */ +export function migrateIndyCredentialTypeToAnonCredsCredential( + agent: Agent, + credentialRecord: CredentialExchangeRecord +) { + agent.config.logger.debug( + `Migrating credential record with id ${credentialRecord.id} to anoncreds credential binding for version 0.4` + ) + + const INDY_CREDENTIAL_RECORD_TYPE = 'indy' + const ANONCREDS_CREDENTIAL_RECORD_TYPE = 'anoncreds' + + for (const credential of credentialRecord.credentials) { + if (credential.credentialRecordType === INDY_CREDENTIAL_RECORD_TYPE) { + agent.config.logger.debug(`Updating credential binding ${credential.credentialRecordId} to anoncreds type`) + credential.credentialRecordType = ANONCREDS_CREDENTIAL_RECORD_TYPE + } + } + + agent.config.logger.debug( + `Successfully migrated credential record with id ${credentialRecord.id} to anoncreds credential binding for version 0.4` + ) +} + +/** + * Migrates the indy credential metadata type to anoncreds credential metadata type. + * + * The following 0.3.1 credential metadata structure (unrelated keys omitted): + * + * ```json + * { + * "_internal/indyRequest": {} + * "_internal/indyCredential": {} + * } + * ``` + * + * Wil be tranformed into the following 0.4 credential metadata structure (unrelated keys omitted): + * ```json + * { + * "_anoncreds/credentialRequest": {} + * "_anoncreds/credential": {} + * } + * ``` + */ +export function migrateIndyCredentialMetadataToAnonCredsMetadata( + agent: Agent, + credentialRecord: CredentialExchangeRecord +) { + agent.config.logger.debug( + `Migrating credential record with id ${credentialRecord.id} to anoncreds metadata for version 0.4` + ) + + const indyCredentialRequestMetadataKey = '_internal/indyRequest' + const indyCredentialMetadataKey = '_internal/indyCredential' + + const ANONCREDS_CREDENTIAL_REQUEST_METADATA = '_anoncreds/credentialRequest' + const ANONCREDS_CREDENTIAL_METADATA = '_anoncreds/credential' + + const indyCredentialRequestMetadata = credentialRecord.metadata.get(indyCredentialRequestMetadataKey) + if (indyCredentialRequestMetadata) { + // TODO: we if we choose to rename master secret to link secret in anoncreds-rs we should also rename it in the request + credentialRecord.metadata.set(ANONCREDS_CREDENTIAL_REQUEST_METADATA, indyCredentialRequestMetadata) + credentialRecord.metadata.delete(indyCredentialRequestMetadataKey) + } + + const indyCredentialMetadata = credentialRecord.metadata.get(indyCredentialMetadataKey) + if (indyCredentialMetadata) { + credentialRecord.metadata.set(ANONCREDS_CREDENTIAL_METADATA, indyCredentialMetadata) + credentialRecord.metadata.delete(indyCredentialMetadataKey) + } + + agent.config.logger.debug( + `Successfully migrated credential record with id ${credentialRecord.id} to anoncreds credential metadata for version 0.4` + ) +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/index.ts b/packages/anoncreds/src/updates/0.3.1-0.4/index.ts new file mode 100644 index 0000000000..8d79e32cd1 --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/index.ts @@ -0,0 +1,13 @@ +import type { BaseAgent } from '@aries-framework/core' + +import { migrateAnonCredsCredentialDefinitionRecordToV0_4 } from './credentialDefinition' +import { migrateCredentialExchangeRecordToV0_4 } from './credentialExchangeRecord' +import { migrateLinkSecretToV0_4 } from './linkSecret' +import { migrateAnonCredsSchemaRecordToV0_4 } from './schema' + +export async function updateAnonCredsModuleV0_3_1ToV0_4(agent: Agent): Promise { + await migrateCredentialExchangeRecordToV0_4(agent) + await migrateLinkSecretToV0_4(agent) + await migrateAnonCredsCredentialDefinitionRecordToV0_4(agent) + await migrateAnonCredsSchemaRecordToV0_4(agent) +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts b/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts new file mode 100644 index 0000000000..3ad404e225 --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts @@ -0,0 +1,43 @@ +import type { BaseAgent } from '@aries-framework/core' + +import { AnonCredsLinkSecretRecord, AnonCredsLinkSecretRepository } from '../../repository' + +/** + * Creates an {@link AnonCredsLinkSecretRecord} based on the wallet id. If an {@link AnonCredsLinkSecretRecord} + * already exists (which is the case when upgraded to Askar), no link secret record will be created. + */ +export async function migrateLinkSecretToV0_4(agent: Agent) { + agent.config.logger.info('Migrating link secret to storage version 0.4') + + const linkSecretRepository = agent.dependencyManager.resolve(AnonCredsLinkSecretRepository) + + agent.config.logger.debug(`Fetching default link secret record from storage`) + const defaultLinkdSecret = await linkSecretRepository.findDefault(agent.context) + + if (!defaultLinkdSecret) { + // If no default link secret record exists, we create one based on the wallet id and set is as default + agent.config.logger.debug(`No default link secret record found. Creating one based on wallet id.`) + + if (!agent.wallet.walletConfig?.id) { + agent.config.logger.error(`Wallet id not found. Cannot create default link secret record. Skipping...`) + return + } + + // We can't store the link secret value. This is not exposed by indy-sdk. + const linkSecret = new AnonCredsLinkSecretRecord({ + linkSecretId: agent.wallet.walletConfig?.id, + }) + linkSecret.setTag('isDefault', true) + + agent.config.logger.debug( + `Saving default link secret record with record id ${linkSecret.id} and link secret id ${linkSecret.linkSecretId} to storage` + ) + await linkSecretRepository.save(agent.context, linkSecret) + } else { + agent.config.logger.debug( + `Default link secret record with record id ${defaultLinkdSecret.id} and link secret id ${defaultLinkdSecret.linkSecretId} found. Skipping...` + ) + } + + agent.config.logger.debug(`Successfully migrated link secret to version 0.4`) +} diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/schema.ts b/packages/anoncreds/src/updates/0.3.1-0.4/schema.ts new file mode 100644 index 0000000000..f0c99210ff --- /dev/null +++ b/packages/anoncreds/src/updates/0.3.1-0.4/schema.ts @@ -0,0 +1,62 @@ +import type { AnonCredsSchema } from '../../models' +import type { BaseAgent } from '@aries-framework/core' + +import { AnonCredsSchemaRepository } from '../../repository' + +/** + * Migrates the {@link AnonCredsSchemaRecord} to 0.4 compatible format. It fetches all schema records from + * storage and updates the format based on the new ledger agnostic anoncreds models. After a record has been transformed, + * it is updated in storage and the next record will be transformed. + */ +export async function migrateAnonCredsSchemaRecordToV0_4(agent: Agent) { + agent.config.logger.info('Migrating AnonCredsSchemaRecord records to storage version 0.4') + const schemaRepository = agent.dependencyManager.resolve(AnonCredsSchemaRepository) + + agent.config.logger.debug(`Fetching all schema records from storage`) + const schemaRecords = await schemaRepository.getAll(agent.context) + + agent.config.logger.debug(`Found a total of ${schemaRecords.length} schema records to update.`) + for (const schemaRecord of schemaRecords) { + const oldSchema = schemaRecord.schema as unknown as OldSchema + + // If askar migration script is ran, it could be that the credential definition record is already in 0.4 format + if (oldSchema.id === undefined) { + agent.config.logger.info( + `Schema record with id ${schemaRecord.id} and schema id ${schemaRecord.schemaId} is already in storage version 0.4 format. Probably due to Indy SDK to Askar migration. Skipping...` + ) + continue + } + + agent.config.logger.debug( + `Migrating anoncreds schema record with id ${schemaRecord.id} and schema id ${oldSchema.id} to storage version 0.4` + ) + + const newSchema = { + attrNames: oldSchema.attrNames, + name: oldSchema.name, + version: oldSchema.version, + issuerId: oldSchema.id.split('/')[0], + } satisfies AnonCredsSchema + + schemaRecord.schema = newSchema + schemaRecord.schemaId = oldSchema.id + + // schemaIssuerDid was set as tag, but is now replaced by issuerId. It was also always set + // to the value `did` as it incorrectly parsed the schemaId. + schemaRecord.setTag('schemaIssuerDid', undefined) + + // Save updated schema record + await schemaRepository.update(agent.context, schemaRecord) + + agent.config.logger.debug(`Successfully migrated schema record with id ${schemaRecord.id} to storage version 0.4`) + } +} + +export interface OldSchema { + id: string + name: string + version: string + attrNames: string[] + seqNo: number + ver: string +} diff --git a/packages/anoncreds/src/updates/__tests__/0.3.test.ts b/packages/anoncreds/src/updates/__tests__/0.3.test.ts new file mode 100644 index 0000000000..7ad829c3f9 --- /dev/null +++ b/packages/anoncreds/src/updates/__tests__/0.3.test.ts @@ -0,0 +1,239 @@ +import { DependencyManager, InjectionSymbols, Agent, UpdateAssistant, utils } from '@aries-framework/core' +import { readFileSync } from 'fs' +import path from 'path' + +import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService' +import { indySdk, agentDependencies } from '../../../../core/tests' +import { IndySdkWallet } from '../../../../indy-sdk/src' +import { IndySdkSymbol } from '../../../../indy-sdk/src/types' +import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry' +import { AnonCredsModule } from '../../AnonCredsModule' +import { + AnonCredsHolderServiceSymbol, + AnonCredsIssuerServiceSymbol, + AnonCredsVerifierServiceSymbol, +} from '../../services' + +// Backup date / time is the unique identifier for a backup, needs to be unique for every test +const backupDate = new Date('2023-03-19T22:50:20.522Z') +jest.useFakeTimers().setSystemTime(backupDate) + +describe('UpdateAssistant | AnonCreds | v0.3.1 - v0.4', () => { + it(`should correctly update the credential exchange records for holders`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(utils, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const holderRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/holder-anoncreds-2-credentials-0.3.json'), + 'utf8' + ) + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) + dependencyManager.registerInstance(AnonCredsIssuerServiceSymbol, {}) + dependencyManager.registerInstance(AnonCredsHolderServiceSymbol, {}) + dependencyManager.registerInstance(AnonCredsVerifierServiceSymbol, {}) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig: { + id: `Wallet: 0.3 Update AnonCreds - Holder`, + key: `Key: 0.3 Update AnonCreds - Holder`, + }, + }, + dependencies: agentDependencies, + modules: { + // We need to include the AnonCredsModule to run the updates + anoncreds: new AnonCredsModule({ + registries: [new InMemoryAnonCredsRegistry()], + }), + }, + }, + dependencyManager + ) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.records = JSON.parse(holderRecordsString) + + expect(await updateAssistant.isUpToDate()).toBe(false) + expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([ + { + fromVersion: '0.3.1', + toVersion: '0.4', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update() + + expect(await updateAssistant.isUpToDate()).toBe(true) + expect(await updateAssistant.getNeededUpdates()).toEqual([]) + + // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here + delete storageService.records.MEDIATOR_ROUTING_RECORD + expect(storageService.records).toMatchSnapshot() + + await agent.shutdown() + await agent.wallet.delete() + + uuidSpy.mockReset() + }) + + it(`should correctly update the schema and credential definition, and create link secret records for issuers`, async () => { + // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. + let uuidCounter = 1 + const uuidSpy = jest.spyOn(utils, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) + + const issuerRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/issuer-anoncreds-2-schema-credential-definition-credentials-0.3.json'), + 'utf8' + ) + + const dependencyManager = new DependencyManager() + const storageService = new InMemoryStorageService() + dependencyManager.registerInstance(InjectionSymbols.StorageService, storageService) + // If we register the IndySdkModule it will register the storage service, but we use in memory storage here + dependencyManager.registerContextScoped(InjectionSymbols.Wallet, IndySdkWallet) + dependencyManager.registerInstance(IndySdkSymbol, indySdk) + dependencyManager.registerInstance(AnonCredsIssuerServiceSymbol, {}) + dependencyManager.registerInstance(AnonCredsHolderServiceSymbol, {}) + dependencyManager.registerInstance(AnonCredsVerifierServiceSymbol, {}) + + const agent = new Agent( + { + config: { + label: 'Test Agent', + walletConfig: { + id: `Wallet: 0.3 Update AnonCreds - Issuer`, + key: `Key: 0.3 Update AnonCreds - Issuer`, + }, + }, + dependencies: agentDependencies, + modules: { + // We need to include the AnonCredsModule to run the updates + anoncreds: new AnonCredsModule({ + registries: [ + // We need to be able to resolve the credential definition so we can correctly + new InMemoryAnonCredsRegistry({ + existingCredentialDefinitions: { + 'did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG': { + schemaId: 'did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0', + type: 'CL', + tag: 'TAG', + value: { + primary: { + n: '92212366077388130017820454980772482128748816766820141476572599854614095851660955000471493059368591899172871902601780138917819366396362308478329294184309858890996528496805316851980442998603067852135492500241351106196875782591605768921500179261268030423733287913264566336690041275292095018304899931956463465418485815424864260174164039300668997079647515281912887296402163314193409758676035183692610399804909476026418386307889108672419432084350222061008099663029495600327790438170442656903258282723208685959709427842790363181237326817713760262728130215152068903053780106153722598661062532884431955981726066921637468626277', + s: '51390585781167888666038495435187170763184923351566453067945476469346756595806461020566734704158200027078692575370502193819960413516290740555746465017482403889478846290536023708403164732218491843776868132606601025003681747438312581577370961516850128243993069117644352618102176047630881347535103984514944899145266563740618494984195198066875837169587608421653434298405108448043919659694417868161307274719186874014050768478275366248108923366328095899343801270111152240906954275776825865228792303252410200003812030838965966766135547588341334766187306815530098180130152857685278588510653805870629396608258594629734808653690', + r: { + master_secret: + '61760181601132349837705650289020474131050187135887129471275844481815813236212130783118399756778708344638568886652376797607377320325668612002653752234977886335615451602379984880071434500085608574636210148262041392898193694256008614118948399335181637372037261847305940365423773073896368876304671332779131812342778821167205383614143093932646167069176375555949468490333033638790088487176980785886865670928635382374747549737473235069853277820515331625504955674335885563904945632728269515723913822149934246500994026445014344664596837782532383727917670585587931554459150014400148586199456993200824425072825041491149065115358', + name: '26931653629593338073547610164492146524581067674323312766422801723649824593245481234130445257275008372300577748467390938672361842062002005882497002927312107798057743381013725196864084323240188855871993429346248168719358184490582297236588103100736704037766893167139178159330117766371806271005063205199099350905918805615139883380562348264630567225617537443345104841331985857206740142310735949731954114795552226430346325242557801443933408634628778255674180716568613268278944764455783252702248656985033565125477742417595184280107251126994232013125430027211388949790163391384834400043466265407965987657397646084753620067162', + age: '12830581846716232289919923091802380953776468678758115385731032778424701987000173171859986490394782070339145726689704906636521504338663443469452098276346339448054923530423862972901740020260863939784049655599141309168321131841197392728580317478651190091260391159517458959241170623799027865010022955890184958710784660242539198197998462816406524943537217991903198815091955260278449922637325465043293444707204707128649276474679898162587929569212222042385297095967670138838722149998051089657830225229881876437390119475653879155105350339634203813849831587911926503279160004910687478611349149984784835918594248713746244647783', + }, + rctxt: + '49138795132156579347604024288478735151511429635862925688354411685205551763173458098934068417340097826251030547752551543780926866551808708614689637810970695962341030571486307177314332719168625736959985286432056963760600243473038903885347227651607234887915878119362501367507071709125019506105125043394599512754034429977523734855754182754166158276654375145600716372728023694171066421047665189687655246390105632221713801254689564447819382923248801463300558408016868673087319876644152902663657524012266707505607127264589517707325298805787788577090696580253467312664036297509153665682462337661380935241888630672980409135218', + z: '60039858321231958911193979301402644724013798961769784342413248136534681852773598059805490735235936787666273383388316713664379360735859198156203333524277752965063504355175962212112042368638829236003950022345790744597825843498279654720032726822247321101635671237626308268641767351508666548662103083107416168951088459343716911392807952489009684909391952363633692353090657169830487309162716174148340837088238136793727262599036868196525437496909391247737814314203700293659965465494637540937762691328712617352605531361117679740841379808332881579693119257467828678864789270752346248637901288389165259844857126172669320275054', + }, + }, + issuerId: 'did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H', + }, + 'did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222': { + schemaId: 'did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12', + type: 'CL', + tag: 'TAG2222', + value: { + primary: { + n: '92672464557302826159958381706610232890780336783477671819498833000372263812875113518039840314305532823865676182383425212337361529127538393953888294696727490398569250059424479369124987018050461872589017845243006613503064725987487445193151580781503573638936354603906684667833347097853363102011613363551325414493877438329911648643160153986822516630519571283817404410939266429143144325310144873915523634615108054232698216677813083664813055224968248142239446186423096615162232894052206134565411335121805318762068246410255953720296084525738290155785653879950387998340378428740625858243516259978797729172915362664095388670853', + s: '14126994029068124564262196574803727042317991235159231485233854758856355239996741822278406673337232628669751727662479515044513565209261235580848666630891738643990084502393352476512637677170660741636200618878417433799077613673205726221908822955109963272016538705991333626487531499501561952303907487494079241110050020874027756313402672435051524680914533743665605349121374703526870439925807395782970618162620991315112088226807823652545755186406850860290372739405126488851340032404507898084409367889215777693868794728141508635105180827151292046483128114528214465463152927678575672993454367871685772245405671312263615738674', + r: { + master_secret: + '26619502892062275386286102324954654427871501074061444846499515284182097331967223335934051936866595058991987589854477281430063143491959604612779394547177027208671151839864660333634457188140162529133121090987235146837242477233778516233683361556079466930407338673047472758762971774183683006400366713364299999136369605402942210978218705656266115751492424192940375368169431001551131077280268253962541139755004287154221749191778445668471756569604156885298127934116907544590473960073154419342138695278066485640775060389330807300193554886282756714343171543381166744147102049996134009291163457413551838522312496539196521595692', + age: '66774168049579501626527407565561158517617240253618394664527561632035323705337586053746273530704030779131642005263474574499533256973752287111528352278167213322154697290967283640418150957238004730763043665983334023181560033670971095508406493073727137576662898702804435263291473328275724172150330235410304531103984478435316648590218258879268883696376276091511367418038567366131461327869666106899795056026111553656932251156588986604454718398629113510266779047268855074155849278155719183039926867214509122089958991364786653941718444527779068428328047815224843863247382688134945397530917090461254004883032104714157971400208', + name: '86741028136853574348723360731891313985090403925160846711944073250686426070668157504590860843944722066104971819518996745252253900749842002049747953678564857190954502037349272982356665401492886602390599170831356482930058593126740772109115907363756874709445041702269262783286817223011097284796236690595266721670997137095592005971209969288260603902458413116126663192645410011918509026240763669966445865557485752253073758758805818980495379553872266089697405986128733558878942127067722757597848458411141451957344742184798866278323991155218917859626726262257431337439505881892995617030558234045945209395337282759265659447047', + height: + '36770374391380149834988196363447736840005566975684817148359676140020826239618728242171844190597784913998189387814084045750250841733745991085876913508447852492274928778550079342017977247125002133117906534740912461625630470754160325262589990928728689070499835994964192507742581994860212500470412940278375419595406129858839275229421691764136274418279944569154327695608011398611897919792595046386574831604431186160019573221025054141054966299987505071844770166968281403659227192031982497703452822527121064221030191938050276126255137769594174387744686048921264418842943478063585931864099188919773279516048122408000535396365', + }, + rctxt: + '71013751275772779114070724661642241189015436101735233481124050655632421295506098157799226697991094582116557937036881377025107827713675564553986787961039221830812177248435167562891351835998258222703796710987072076518659197627933717399137564619646356496210281862112127733957003638837075816198062819168957810762822613691407808469027306413697001991060047213339777833838291591976754857934071589843434238025803790508552421154902537027548698271140571140256835534208651964449214890690159171682094521879102663244464066621388809286987873635426369915309596945084951678722672915158041830248278889303704844284468270547467324686757', + z: '90415953543044389740703639345387867170174070770040351538453902580989033567810029650534915348296084212079064544906463014824475317557221991571331212308335167828473551776349999211544897426382305096215624787217055491736755052175278235595298571339706430785816901931128536495808042995635624112114867111658850659510246291844949806165980806847525704751270260070165853067310918184720602183083989806069386048683955313982129380729637761521928446431397104973906871109766946008926113644488012281655650467201044142029022180536946134328567554182760495139058952910079169456941591963348364521142012653606596379566245637724637892435425', + }, + revocation: { + g: '1 1864FF219549D1BC1E492955305FC5EED27C114580F206532D2F5D983A1DD3BD 1 0414758D7B6B254A9CA81E1084721A97CA312497C21BB9B16096636C59F9D105 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + g_dash: + '1 2327DA248E721E3935D81C5579DD3707882FFB962B518D37FB1112D96CC63611 1 164989452135CF5D840A20EE354DBF26BEEC74DE7FD53672E55224BEE0228128 1 0634D5E85C210319BFD2535AFD8F7F79590B2F5CC61AF794218CC50B43FBB8C6 1 0A63F1C0FC2C4540156C7A2E2A2DF1DDF99879C25B4F622933707DD6074A0F1B 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + h: '1 0A031B1932CDFEE76C448CA0B13A7DDC81615036DA17B81DB2E5DFC7D1F6CD6F 1 06F46C9CC7D32A11C7D2A308D4C71BEE42B3BD9DD54141284D92D64D3AC2CE04 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h0: '1 1C88CA353EF878B74E7F515C88E2CBF11FDC3047E3C5057B34ECC2635B4F8FA5 1 1D645261FBC6164EC493BB700B5D8D5C8BF876FD9BA034B107753C79A53B0321 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h1: '1 16AC82FE7769689173EABA532E7A489DF87F81AE891C1FDA90FE9813F6761D71 1 147E45451C76CD3A9B0649B12E27EA0BF4E85E632D1B2BEC3EC9FFFA51780ACE 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h2: '1 2522C4FAA35392EE9B35DAC9CD8E270364598A5ED019CB34695E9C01D43C16DC 1 21D353FB299C9E39C976055BF4555198C63F912DBE3471E930185EF5A20470E5 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + htilde: + '1 24D87DBC6283534AE2AA38C45E52D83CC1E70BD589C813F412CC68563F52A2CA 1 05189BC1AAEE8E2A6CB92F65A8C0A18E4125EE61E5CEF1809EF68B388844D1B1 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + h_cap: + '1 1E3272ABDFD9BF05DB5A7667335A48B9026C9EA2C8DB9FA6E59323BBEB955FE2 1 031BD12497C5BBD68BEA2D0D41713CDFFDCBE462D603C54E9CA5F50DE792E1AB 1 05A917EBAA7D4B321E34F37ADC0C3212CE297E67C7D7FEC4E28AD4CE863B7516 1 16780B2C5BF22F7868BF7F442987AF1382F6465A581F6824245EFB90D4BB8B62 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + u: '1 1F654067166C73E14C4600C2349F0756763653A0B66F8872D99F9642F3BD2013 1 24B074FFB3EE1E5E7A17A06F4BCB4082478224BD4711619286266B59E3110777 1 001B07BEE5A1E36C0BBC31E56E039B39BB0A1BA2F491C2F674EC5CB89150FC2F 1 0F4F1E71A11EB1215DE5A081B7651E1E22C30FCCC5566E13F0A8062DB67B9E32 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + pk: '1 0A165BF9A5546F44298356622C58CA29D2C8D194402CAFCAF5944BE65239474E 1 24BA0620893059732B89897F601F37EF92F9F29B4526E094DA9DC612EB5A90CD 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8', + y: '1 020240A177435C7D5B1DBDB78A5F0A34A353447991E670BA09E69CCD03FA6800 1 1501D3C784703A097EDDE368B27B85229030C2942C4874CB913C7AAB8C3EF61A 1 109DB12EF355D8A477E353970300E8C0AC2E48793D3DC13416BFF75145BAD753 1 079C6F242737A5D97AC34CDE4FDE4BEC057A399E73E4EF87E7024048163A005F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000', + }, + }, + issuerId: 'did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H', + }, + }, + }), + ], + }), + }, + }, + dependencyManager + ) + + const updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'doNotChange', + }, + }) + + await updateAssistant.initialize() + + // Set storage after initialization. This mimics as if this wallet + // is opened as an existing wallet instead of a new wallet + storageService.records = JSON.parse(issuerRecordsString) + + expect(await updateAssistant.isUpToDate()).toBe(false) + expect(await updateAssistant.getNeededUpdates()).toEqual([ + { + fromVersion: '0.3.1', + toVersion: '0.4', + doUpdate: expect.any(Function), + }, + ]) + + await updateAssistant.update() + + expect(await updateAssistant.isUpToDate()).toBe(true) + expect(await updateAssistant.getNeededUpdates()).toEqual([]) + + // MEDIATOR_ROUTING_RECORD recipientKeys will be different every time, and is not what we're testing here + delete storageService.records.MEDIATOR_ROUTING_RECORD + expect(storageService.records).toMatchSnapshot() + + await agent.shutdown() + await agent.wallet.delete() + + uuidSpy.mockReset() + }) +}) diff --git a/packages/anoncreds/src/updates/__tests__/__fixtures__/holder-anoncreds-2-credentials-0.3.json b/packages/anoncreds/src/updates/__tests__/__fixtures__/holder-anoncreds-2-credentials-0.3.json new file mode 100644 index 0000000000..0bd19082fc --- /dev/null +++ b/packages/anoncreds/src/updates/__tests__/__fixtures__/holder-anoncreds-2-credentials-0.3.json @@ -0,0 +1,304 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2023-03-18T18:53:44.041Z", + "storageVersion": "0.3.1", + "updatedAt": "2023-03-18T18:53:44.041Z" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "8788182f-1397-4265-9cea-10831b55f2df": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "8788182f-1397-4265-9cea-10831b55f2df", + "createdAt": "2023-03-18T18:54:00.025Z", + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "role": "receiver", + "message": { + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "@id": "c5fc78be-b355-4411-86f3-3d97482b9841", + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "John" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "99" + } + ] + }, + "offers~attach": [ + { + "@id": "libindy-cred-offer-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiODUxODAxMDMyNzEzNDg5NzYxOTg5MzAzNjMzMDkzOTEyOTExMDUxNjI0OTQ0OTYzMTgzNzM2MDY3NDkwOTc2MDYxODEwMDgxODkxMzQiLCJ4el9jYXAiOiI4NDk0NDg4MjQzNTk2NTkwOTc2MjQzMjc0NDg4ODk2Mjc1NTcyODAyMTQ1ODE5NDQzNTE0NzQxMzk1NDI1NjM5MzQwMTczMDIzMTQ5NzI3MDY5NzMzMzQwODgzMTU4MzQ1NTYzOTA5OTcxNDMzNTg1MjMwODAxNTYyMTM0NjczNjM1ODg5NTA3Njg5ODQwOTgyODU5Mzg1NjA1MTc1NTkxNDYxOTkyMDExNzU2Mzg1MTI3MTQ3ODgxNDMwODEzNjYxNzY0MDU5MzE0ODk4MTc2NzQzMTQ5MjYzMDMwMDQ1NzMwMDMzMzI2NzgyMzg1OTY0NjcxMzg1ODQ2MzcxNjQ5MzQxMTg2MDM5NjE4MTQwOTIwMDUxMzg1MDAwNTYxMDcyMTc5NTEyMzc5Nzk0OTU4NjE1ODIyODI2OTExNzIwNTQyNTE0MTQ1NDc5MTAxOTUyMzM4MDMwMDY1MDk5NjcxOTU2OTMxMzE2NjE5MjM0NTQ0NTE5NTQ1ODQ1MzA4MzgxMjQyNTM0NDcyOTc3NjY0MjAwMjc2MTMyOTgxODE1ODAzNTIxOTExMzk4ODkxMjE0NjE1NzA1MDM2ODM2ODU1NDU1NzY4ODg4MTUxNDgzODAyNDcyODQyMzczNzE0MTI0NTYwMzIyNTI3NDE4MTEwNzYyMjgyNzY4NzMyNTIzMDQyMDA3MDY2OTk2ODIxMTQwMzE1NDg0NzI4NTM2NzIwNDI3MDg5MTI2NDk1NTAzMjc0ODQ4MDM3MjUzOTM3NjI3MDU2ODUzMTQ4NjE5NDA4NDYxOTI5NzEzMjM4MjEwNDc4MjcyMTIxNTUwNjQzODc4ODM1NDYwMzY1OTIwMjE3NTk5NDYyNDUzMDMyNDQ4MjYyMTM3NjE5ODY0OTU4MzA1MDE3MjA4OTYwNDc1MTQxODgwMTMiLCJ4cl9jYXAiOltbIm5hbWUiLCI1MDcyNzU2NDE2NDA2ODIxNzU1OTc0MzUxMTg0NjE1NjA4NDY2NTk3Mzk0NzA2MTY1NDg2ODAzMjc3MjMyMzQyOTk4MDA0MzY0OTU0MTczMzc0NDIwOTc5NTkwMDcyODgxNDgxNDA0MTg2OTExODg5NzQ4MTgzMzQ1OTk5NzQ0NzgxMTQ1MTMwNzEyNDIzODY0Nzc1MzQzNjAzNTk2NDM3Mzg4OTgzNTExNDAzODA0NjEyNjU1MDE5NzQ4MTI5NDk3ODY2NTcwMDQyMjcwNDQxNDQ5MjYwODY0NzgyMzI5MjAxNDEzMTc5ODU3NzA0MjM5OTMyMTg4NTc4NzE3MDczNzM3NjUyNzY5MzY5NDg4OTgxNzg2NDQwNTExODAzMjMzNDMxNzA4NDk4MTU2NTA0OTUzNzkzNjU2NjQ2NzMyNTU4MzQwNDI2MDI1MjA3NTk0OTIwMDY4OTc2OTQ4Nzg2OTUxNzM3MDIwNDQ0NTA5NzYyMDQ2MzIzNzA0MDQ3MjU1ODU3NDE5ODE3MDc5NTI3NDgzNTE1NDY2NTAyMDkzOTY1NDMzMzk3MjQ1MzA4MjQ5MDgyMTQ4Mjc4NDA1MzI5Njg1Mjc0MDYwNjk0MzI0MTI2ODgxMjkyMDIyMjY1ODczMjk5MDU0NDU1OTA5NzkyNjUwNjAyMTk0NjUzMjYxMDk0ODYwOTc2NzA4ODE1ODgwMjExMTY0MTkwMDM0NjY0MzI2MDc3NjcwNzkyMDE4NTE2MzMzNDI3NjkwODYwMjIxODEwMzk5MDgxMjc5NjAwNTYzMjk3MjI0NjM0MDM0NjcxNTIwODE5MzU3NzQ0Njk2NzU1Njg1NDI2NjIzMzAwMjQ3MDUwODE4NTQ2MDM2NjA0NjMxNjcyNzE5MjI0NDA4NTE2NDM4NTgxMDM5Njk4NzI0MSJdLFsibWFzdGVyX3NlY3JldCIsIjU2MzYzNTgyMDQ5Mjg4OTY1OTg1MDA4NzgyMzU0NjgyNjMwNDkxMzQ3MTM1NDIxNTAyMDEyMTIwMzI4MDI4ODIyMjUyMzg4NjgwNTMwNTgxMTcwMTgwNDU1MTcyNTc3ODkyMTEyMTY1OTM0Mjk5NjUyNzAxNDExMzUyNDkzMzkyODU0ODI4NzMyMDQzMDI0MDI0MzM0MzMzNzc0NjEyOTEzOTUyMjAzNjM1NDk2MDQ0ODMzMDI5NDE2NjUwOTU5NjE0ODgzNTUwOTMxNzgzNTA5MzE1Nzg4MDEyODQ0MzAwMDQwMDE5MTY5MTc3NTI1OTgxMTU3OTkwNjQzMDcyMjQyNzcxMjU0MTYyNzMxOTU4NzI2Nzc1NjYwMjkxODIzMDcyNDk1Mzg0NzM5MTcwODc4ODMxNzkxMjQzMjEzMjU5MzA5ODQxNjU3MjUwOTg1NzMxMjEyNzE2MDM2MDY3MDUxNjM2NzA0MjA1NDEzMDk2MDU3MTA2NTM2MTI2ODUyNDU0NzcwMzQzMTMwMTczMjAwNjEzMDIxOTE4MzgzMDQxOTU4MTkwOTE2NzQ0NjU4NTI0ODA1NjM4Mzk2OTY3OTA3MzIwNjY1MDU1MzcwMjY0NjAxMDczMjc5NDMyNjM5MjM3Njc1NTA0OTg1NzQyNTI4NjYwMTAyMDEzNzIxMzA2MTE4MTg0NDk1MTEyNDQ2NDYyNDc2NTkwMjYxODkxMjA0OTQxOTA4MjMyNzMzNDA3MTg4MDA3NzE2NTA2OTUzMDY0Nzc5NDk5ODExNzI0ODI5NjcyNjY2NzIyNjIzOTAxMTc1OTk0NTIyNjkwMjk1ODI0MDgyNzY5NjQ0NDYxOTAxMDk2NzI3MTE5NzAzMjUzNzI4NjY3MTU1MzA5MDYzNDUyNDY2MDY3NzU5NzIwOTgyNDA3MiJdLFsiYWdlIiwiMTM2NTQxMjE0MjM5MTcyNDQxNzQ1MjU3MjcyMDI3MTA4NDYwMzU0MjgxMTA2OTA2MzYwNDIwMDE0NjUyMDIxMDgyNDEzODM2ODEyMjk3NjY3ODk2MTYzNDkzMjM4NDIxNDI4NjMyNTMxODE0ODk4NzkwMDg4OTg2NjgyMTE2OTAyMzc4NDgwNTE4OTUxNDExNzg1OTk3NTk5MDMyNDYxNjExNjIyMDUyNjMzMDQ5ODYxMzc5MTQzNzI4MTM5MTUyMDkyMzI0ODc3MjMxMTYwNTgzNzA5NjE0MzA1NzQ1MjA5MjQwNjU2MDU4NjY3OTMwODEzNzYyNDY5MDc2ODc5MTk1Nzg0Nzg4NTE2NjI3MjgxMDY0NjE3MzgzMDc4Njc5MTkwODIwMzQwNTgwNDY2MjU3ODU3NjA1MTc2MTg4NTI3OTMxMDI4MTMzNTY5Njc0Mzg2ODAwMTA2MDE2MDg1Nzc0OTcyMzI1NTAyNDA2MTY0OTY0MjU2OTUxNDI3ODAxMTQzNTQxMzUzMzI0Nzg0MzA5OTY4MjIyOTU1NDk4Njk3NTAwMDUwMzc0MDg0NjIwNzQ4MTk0NzIyMTI2NjE2OTY3OTY3Mzc1NTM3Nzc5NTc4NTMwMDIxODExNTA2MTIxNjcxMDUwNDgzNTM2MjA3Njc3MTg5NDQwNjEwNzk0NTcyNzI5MTgzMzAyMjM1MDkxMDg4NTU2ODc5NTg3OTE3MDMzMzQyODcyMzg2NDQ5MTQ0NzgwMDYyNjc4MzA3NzE4MzU1MjQ5MTUxNjc5MDA1MzkxNDA5NDE4OTQxMjEzNDkxMjQyMjg2NTAwODcyMzQxNDI3Nzk1MjQ1ODYzODE2MDY2NDY3NDkxOTg4OTU3MDEwNDIxNDA3NDkyMDUxOTc0NTMwNjIxOTk1ODU0ODczNTM5Mjk3MyJdXX0sIm5vbmNlIjoiNzk3NjAzMjE3NzA5MzM1MzAwMTcwODI4In0=" + } + } + ], + "~service": { + "recipientKeys": ["GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3000" + } + }, + "updatedAt": "2023-03-18T18:54:00.025Z" + }, + "id": "8788182f-1397-4265-9cea-10831b55f2df", + "type": "DidCommMessageRecord", + "tags": { + "role": "receiver", + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolName": "issue-credential", + "messageName": "offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "messageId": "c5fc78be-b355-4411-86f3-3d97482b9841" + } + }, + "2c250bf3-da8b-46ac-999d-509e4e6daafa": { + "value": { + "metadata": { + "_internal/indyRequest": { + "master_secret_blinding_data": { + "v_prime": "6088566065720309491695644944398283228337587174153857313170975821102428665682789111613194763354086540665993822078019981371868225077833338619179176775427438467982451441607103798898879602785159234518625137830139620180247716943526165654371269235270542103763086097868993123576876140373079243750364373248313759006451117374448224809216784667062369066076812328680472952148248732117690061334364498707450807760707599232005951883007442927332478453073050250159545354197772368724822531644722135760544102661829321297308144745035201971564171469931191452967102169235498946760810509797149446495254099095221645804379785022515460071863075055785600423275733199", + "vr_prime": null + }, + "nonce": "131502096406868204437821", + "master_secret_name": "walletId28c602347-3f6e-429f-93cd-d5aa7856ef3f" + }, + "_internal/indyCredential": { + "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG", + "schemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0" + } + }, + "credentials": [ + { + "credentialRecordType": "indy", + "credentialRecordId": "f54d231b-ef4f-4da5-adad-b10a1edaeb18" + } + ], + "id": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "createdAt": "2023-03-18T18:54:00.023Z", + "state": "done", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolVersion": "v1", + "credentialAttributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "John" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "99" + } + ], + "updatedAt": "2023-03-18T18:54:01.370Z" + }, + "id": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "type": "CredentialRecord", + "tags": { + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "state": "done", + "credentialIds": ["f54d231b-ef4f-4da5-adad-b10a1edaeb18"] + } + }, + "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64": { + "value": { + "metadata": {}, + "id": "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64", + "createdAt": "2023-03-18T18:54:01.098Z", + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "role": "sender", + "message": { + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "@id": "2a6a3dad-8838-489b-aeea-deef649b0dc1", + "requests~attach": [ + { + "@id": "libindy-cred-request-0", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm92ZXJfZGlkIjoiY3F4ZW8ybzVIWmVjYWc3VHo5aTJXcTRqejQxTml4THJjZW5HV0s2QmJKVyIsImNyZWRfZGVmX2lkIjoiQTRDWVBBU0pZUlpSdDk4WVdyYWMzSDozOkNMOjcyODI2NTpUQUciLCJibGluZGVkX21zIjp7InUiOiI3OTE4NzUwNzgzMzQxMjU3ODU3OTUzMjc2OTU5MjcxOTcyMDQwMjQxMTU1MzcyODEwOTQ2NTEwMjk5MDA1ODEyMTcwNjkwMjIzMTQ2ODU0NDk1MTI1NjQ1MTg3ODQxNzk0NjA3OTUwOTQ1OTM3MDYxMjk1ODgwMjIxMzE2NTg1MTIyNDY1Mzk1MjAwNDQ3MDIwNzAxNDA0NjEwMDc4MzkyMjA4NzkxMjk5NzYwMzM4OTIxNDMzMDc1Njk2ODU0NTY3MTIzNjYxNTYwNDMwNDE3NzQwMzc5MzA4NDQzODcyOTU1NzAwNTk1MTg2NzcxODY3MzM5NzQ5NDgzODYxNTQ2NTE2MTU4NTM5MjkyNDQzNTQ3OTg3MDUwMzE4OTAyOTI5OTgyNzMzMDk1ODk4MDIyMjg2OTk1OTQwMjkzMTg3NTg5NDgwNTgwNTAwNjM0NzAyNjQxNDM0MTgxNjIwMTU4OTU3MzUyNTE1OTA4NDE2MjI4MDQ0NDA2OTU4MTk1MDg4Mjc0ODI4Njc3OTQxMDgxOTczOTg3NjU1MDEzNDUxMzA4ODQyMjYyMzY4MTQzOTExNjIxMTE0NzYyNTk3Nzg1MDczMTM4MDg3NTQ2MDIyMzc1NjQxODQ5ODI2OTg2MjYwMDE5NTAzNzE3OTk0NjM3MzIyNDExNTgzNTY0MTQ1NjcwMTM5OTQ1MjQxOTU2Njk2MDQ3MTQzNjA4NjQ0MDM5OTg2NTYwODUyMzA1MTczMjUxMTUyMDIzODI5NjI3MzQyODM2NDI3MjkwNDQ5NTA3OTY0Nzk4MTQ2NjkzOTUxMDkwNzUwOTAyNiIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6Ijk4MjIzNzMzMDcwMDk1NjM0MzU5MTE2MDEyOTgzMDcyMjc5MjcxMzk1MTg0NjA0NDcxNDQ5NzA1Nzg0MTEyNDAyODMwNzIyMTUxMzIxIiwidl9kYXNoX2NhcCI6IjU5ODA0MTY4ODAxODk1NDAzMjkwMzczMTA3NzA4MzUwODc1NjY4MDcyNDY3ODAyOTcyOTIyNjUzNDE5ODU2MjMyNTIzNDI4OTUxODQ4NjEyNDE1MjM5Nzk3Mjk5ODY2MzIxNjU5NDQ1MTM1NzQ4NzU2MDY1NjgyOTY5MjY4ODI5MTYyMDA0NjQ4NzYwMzg4NTg4NDkyNjg1NDI1MTg1MDU2OTAxOTkxMjcwMzYwMDk3MDc5NjEyNTYxMzY4NzU1OTcwMjY5MjI4MDYzMjMyODU0NzI0NDkyOTA5Njg5MDMyOTg4NjYyMjk5Mzg3MDU2NDEwNjU5MDU3MDUwNjE0MDQ2NzE1NjA0NTgyMzM2NTg4MjMxMjI3MTEzMDEzMDQxMTA0NTU2NzM1MDE3ODUwNzUzODcwNjc2ODYxNDA4MjA0NzkzMDIzNTYwMDEwNTEzODAwNzA4MjAyNjAyNjQ0Mjg2NzI4NjAyOTk5MzU5MDcwODQxMTQ5MTAzMjA5MzY0ODkyMzMzMDYwMDgzMTA5NDIzOTQ5NzE4NTk5NjEzMzk2NjIyMjc4MTExMzk5ODU0MTcyMjMyNTQzOTk1Njc5NDk3Mjk1Nzc1NzA0MjA0MTQxOTU2NDI1MDc4NjYzMzgwMDA1Nzc2ODY2MTcxNTY4OTU1NjE4NjAwMTA2NzkxMjIyNDkyODA2NzI1ODU1NDY2Nzk4OTEzMTc2NDcxMDY3MTk5ODQ2ODEwNDI5MDIzMDc3ODI3OTc1OTIzMDIzNjU3MTg0NjkwNzE0MjkxNDk0MDc5MTM1NzYyOTUxNTc0MjMzNjMwMjExNDQ1Njc3NzE1Mzg3Mzc1NjkyMjAzODE3NDczNDk5NDExNzE5MzIwMjczNzExOTIzMzM3MzYzMTAyOTkwMDcyMjE2MjYzMzUxMzMxNTk4ODk1OTU3MzU1MDc1NTEzODE0NTUwNzkyMCIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiNTY0MTY1NjA2NjM0MTUwODgzMjg2MjM1NDM1MjUyODQ3MjAxMTk4NTQyNDAxMjYzNTY2MDQ2MTA3OTU3NzI4NTQ0MTMwMjgzNjUyNjQzOTI0NDc2NTU2NTIzNzg0ODI1OTgyMzMwMzc4NDI4OTM0MDkxNDcwNDM0OTAwMTM3NzkwNDkxMjM4NTA4ODk2ODQxMzkwNjQ4MTA1NzY5ODYzMzI1OTAzODI1Mjk1MjU2OTQ0NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMzE1MDIwOTY0MDY4NjgyMDQ0Mzc4MjEifQ==" + } + } + ], + "~thread": { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841" + }, + "~service": { + "recipientKeys": ["cqxeo2o5HZecag7Tz9i2Wq4jz41NixLrcenGWK6BbJW"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001" + } + }, + "updatedAt": "2023-03-18T18:54:01.099Z" + }, + "id": "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64", + "type": "DidCommMessageRecord", + "tags": { + "role": "sender", + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolName": "issue-credential", + "messageName": "request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "messageId": "2a6a3dad-8838-489b-aeea-deef649b0dc1" + } + }, + "669093c0-b1f6-437a-b285-9cef598bb748": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "669093c0-b1f6-437a-b285-9cef598bb748", + "createdAt": "2023-03-18T18:54:01.134Z", + "associatedRecordId": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "role": "receiver", + "message": { + "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", + "@id": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", + "formats": [ + { + "attach_id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "format": "hlindy/cred-abstract@v2.0" + } + ], + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", + "attributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "John" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "99" + }, + { + "mime-type": "text/plain", + "name": "height", + "value": "180" + } + ] + }, + "offers~attach": [ + { + "@id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6QW5vdGhlclNjaGVtYTo1LjEyIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY2OlRBRzIyMjIiLCJrZXlfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjQwMTQ0MTA0NDg3MjM0NDU2MTc1NzYwMDc1NzMxNjUyNjg1MTk0MjE5MTk5NDk3NDczNTM4NjU4ODM3OTIyODMzNTEzMDg0Nzk2MDQ5IiwieHpfY2FwIjoiMzgxOTQyMjM1Mzc3MzYwODEyNjY0MDA4MjYzNDQxMDg2MDMwOTMyMjAxNzgzMjM3ODQxODQ5NDg3ODk2ODg1MTYwODY2MTY1MDM3NzI2MTIxNjU0MjcwOTg5NDY3NjAzNDExOTAzODk4MzUwMDAzNDIwODg3MzI4NTUwMTY2MTI1ODMyMjAxOTQzMTkwNzAxMDU4NTAwMDE5ODM1NjA1ODczNDYzOTkwODg3NzQ0NjY3MzU0MjM2Njc3MzcyODg0ODQyNjE5NTEwMTUwOTA2MjI1OTMzMjc1ODEyNjg2NDg3NTg5NjY3ODI3MjAwODcwOTQ0OTIyMjk5MzI3OTI4MDQ1MTk1OTIwMDI3NTc0MDQwNDA4ODU5MzAwMzY1MDYwODc3Nzg2ODkwOTE1MDU5NTA2ODc1OTI0NzE2OTI1MDM2MTc4Njg2NDE5NTYyMzcwODI4MTMzODY2Nzg3NzkyMDcwNjAyNDQzNTkzMTk2NzEzNzcyNDM2NTYzODI0MzkwMDIyNzg4MjU2MzA4NjU4OTc0OTEzMTk1ODYxODUwMTQ3ODE1Mjg5NzQwOTA4NDk1MjQ3NTAyNjYyNDc3NzQ2NTI5ODA3Mzg0OTgxODI5MDc3NTQ4OTI2NzExMDkzNzQ5MjM1ODU4NjUwNDc5NzE5NDI4MzUwMzAwNzUyNjQ0OTg1MTQ5MTMxNjA1NjUzMDIxMDYxNzkwMjY3MzQyNTY4NTkyNTY2MTQ0MDM5NzY4OTg0NTMyNDMzNzk0MzUzNjQ2Nzg1MjA3NDgzOTk2ODQ0OTcxNTgzNzY3NDQ5ODYyODgxMjMxMjI1MzM4MzAzMTQ4NzA0ODczMDEzNDM3MDgyNzY1MTk4OTY2MzE5NTM0OTkyNjk4MzMzMDQ0MDI3MjIyNTYyNTIzNzk3ODk5Mjk2MTQ1NDU5IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiOTE5MTc5NzQ4MTE5NTg5MTY3Njc5MjQxODk5NzY0ODIwNTk0MDc5OTQxNzIzOTgzOTYyNzQ1MTczODM0NDQxMDQ3MjU4MDcyOTE4OTUzNjIzOTQ4MDMyNzI2NDgyNzI2MTEwOTk2Mjk3MDU3NTYwNjcwNzAxOTU1MTkxNDc0NjM0MzQ0ODMxMzg3NTk4NzI2MzMxMjc0NjI4NDU3Njk5NzczMDA1NDMwMDIxNzMwMzg4MzcwMTEyMjc3MzI2MzU4OTgwMTA3ODIzNzUzODc3MTU0NjIwMDkzMjE5MjYyNjAxNDM2NzMyNTgzNDI4Nzc4NDA4OTc0NTQyNzkzMDk0NTQ5MTczOTA3MzQ3OTUxNTc1NjM5NzU2NDg5MTA0Mzk0MTY3NzExMzY1MjM3OTI1MjAwNjk4OTg5NTI5MTQ3OTIzNTYzNDMyODgyMzgwMTg0NzU0NzkzODMwMTE3MTQ1MDAwMTI0NDYxNjkzOTcxMDQ5MjgzNDk1NTE4MDQxMDc5ODUyMzAwMjk0NDM1MjYzOTIwNDU0NTU3MzUxNDQ1MDM3NDI4MDg3OTk2Mzg2NjY3NjU3Nzk5OTYyNzQzNzIyNzA3NzczOTEzMzc0NzIxODUyNTQ3MjkwMTY5MjI5NTAzMTQxOTMwODYzNTk4NTExNjc4NDEyMDE0MzE2MDM2MzYxMzczNDcwOTQwMDEyODcwMDgwMDA2MzE0NzYxNzYzNzUyNzYwODk5MTQ3NzA1MTA0NzQyNjAxNjkxMzMxNjkzMDIwMjg2MjA2NzQ2NzE0MzI3NjU2MjA2NTMzMjk3NDg4MjU2NTM2NTQ3MzY4MjM2OTQ2MDM5NzAzMzc0OTMzNTE0NTc2NDg2NjQyNTY4MjgyNTY2MjMyNDU1NTU5MDY4MzE3NzU5NDM0ODU4NTI3MDg2NjQ0Il0sWyJoZWlnaHQiLCI5MjMwMzkyNDc1NjI4ODc1MjA4OTM0NjM0NzE4MjYzNzA4MDIzOTI1MDU0NjY2NDgzMzgxMzIyMzc3MDg1MjMxMjU4MTM4MzgwOTU1NTk3NDQxNTEyOTYwNDA2MjI3MjUwODgyNjA3NjExMDkwODk3MTM1NDcxNzAwMDIzNDcwOTM2ODg4MDE3NDY5Nzk0ODYzNDk4NzUyNTI3Njc3MjMwMTEwNzg0ODQzNzI0NDUyNTUzODYyOTA2MzM5MDc0OTIzNDU4NTQ3NDYzODcwNzU3OTg5MzMxNzk4OTI2MjM4MjUxMTM2NTYzNjM2MjIyOTQwNDkwMzY3MjQ2OTg0OTU2NTE5MTAzODcwNDE0MDM5NzM2MDE2MDY5MzA2NjQ0NjQzODI4OTgxMTE3OTM3NzYyNDAzODY1Mjc1MDU5MjEyOTY2NzIxOTU3MzM0MTM2ODEyMDI0OTE0MzA4MzAxMzk5MzM4NzMyOTIzNTA0MjA5MDM5ODMxMTc5NjU1NTkyNjg0MjMyMTIzMTI2Mjc4ODQzNDMyOTUwMTk1Mjg3MzE4ODI3NTM2MTMwNDQ3NzM3MTgwMjk3MDE0ODEzNDg3NDQyOTg2NjQ1NzQyNjEyMzE5NzQxNDY2MDMyNTg5OTU0NzYwNjE4MDU0MDUxMjAzMTE1NTAxNDcxNDExMzg3NzU0NDk5MzAwNTU4MTc5NjM5NDAxOTM0NTAzMTMyMDEzMjAzOTg2NzkyMTEzMDAzNTkwODg1NTc3NjgyMzU2NDY3MjA5NTUwNjQxODQxMDYyNTkzNDYyODIwODg3NzgxNDYyODM3ODkzODcxNDM4MzM3Mjc5MTcwMTExMTQ5MTU4NDMzNDE0ODI1NTkyNjcyODU2MzM5OTM4NTgyODg2NzM3OTIwMjc1MzI0MjEwMTUzMjE5MjI2OTYiXSxbImFnZSIsIjkxNTg1ODk3NDkwNzE0ODA3OTY2MDYzOTg5MjE1NTMxNDkyOTQwMDI5NDcyMTM4MjgwNjcxNjcyMjQ0NjY5MDc5NzIyNTQyMDU0NTU3NjY0MTcxMDI1NzM1NjQ4NTIwMTM4ODQ4ODAxNzIyMTc4MTcxMTA5NTc0MTMyNTExMzM1MDEwNTc5NzExMzcyODM5MjI3MDExOTg4MTUyMTEwMzI4MTE5MjkyMjI4NjM3MDU4MDQ3NzYwODYwOTQ0NTY3MzQxMjY4MTY4Mjk3NjE5MDM2ODEwMjYwODM2NDI1NDkwMzU3NjE4NzM4NTYxNTY2MTUxODQ3MzIxNzM1MjQ5ODk1MDU5NTY2OTQxODI5MjE0Nzc0MTA0NzYyNTQwMjcyMjk2NjE1NTE3NjUwMDcyNDQyMTI0NjY5MDEzMTc1ODAyMDk5MDQxMzk3MzE5ODQ0OTA2MDgwOTYxNTcyMTcwNjg2NzgzNDM1Mjg2MDUyMzE5ODY3ODExMDE5MjAxMDYwODM2OTM3Mzc0MzM0NDM5MTQxMDAzMTI3NTcyNjgzNTgwODI0OTkwOTg3MjE5MzU4NzkzOTM2NTU4Nzk3MjI0MDQzNTM1ODA5NzMyNzgxMjE1NzEwNjI1MjQzODYwNTk4MTk0MjU2MjAwODkwOTA3ODAzMDcyMTAzNzc3MzkwODk4MDczOTgyNjY3Njc1ODg0MjI3MjU0Mzc2OTI5Mjg3ODQyNDE0MTE0MjcwNDQwMTEzNDUxNjk4NzE5Nzc5NjQyNTI4MDA4NDM3Mzk5NjI0NTE3OTM4Nzg5MDc3ODE5ODA0MDY5MzcxOTM0NzExMTIyNTQyODU0OTg4MDA0Mjc4NDkwMjAxNTk2NjE0MjUwODc3NDYxMDczNjc3NTUzNzYxMTMyMTA5Nzg3NTQ2ODE1ODk5Njc2NCJdLFsibmFtZSIsIjYyNzgwNTIwMTM3MzI3NTUzMDc3MDg4NTE4NDg1NDYyMTA0NjEzMjEyNzY3ODUwMzYwNTc3NDQ4MDUxNTk5MTMxMTM1NTI2NzQ3Nzc2NzMzMDg1MDMwODcyMDE1OTM2MTI2NzE0MTIxMDgxMzg3ODU2MTkwMTkzMzI3ODY3OTE0NTEzODM2NTQ1OTY4Mjg1NTc5ODEyODMxMDI4ODc2Nzg1NzI3OTQ2MTEwNzg5Mzc0MjcyODgzMzkyOTgwNDkwODk3NDkwMTc5MDQ0ODM0NTgwMzQ2ODY4NDI2ODc0ODU4NTY1OTg4NTUyMDcwNjI1NDczNjM4MDM3Njc5NTU1NTk2MzE5MTc3Nzc5OTcxMTIxMjQzMjgyMTIyOTQ2NjY0ODMxOTgxMTg3MzQ3MzcyMjkxMjYwOTM3MzkzNDA1ODk5OTI0NjM4MzE3ODI5MDczODMxMjI4ODc1Njg5MTcyMTg4NjIyMDI5NzcxNzM5MTQ5NDY2Mzg3NTM5NjkyNDQ5NDU5MjczNDI5NzM5MjMzNjkyNTEzNDA5OTkyNDgxNTQ4ODk0NjAzNjM3MTYzNjA4MTM0MTAzMTk3Nzc3NTM4OTYwMDcyMjcyMzYyNzM4NDM1MTM3MDcyNzIxMjExMDYxNTg4MDE3ODczODg3MTEwNDA2OTk1NDQ4ODIwMDEzMDA5MjgyMzk0OTczMDMwMDI5MTY3NjQ5NzY1OTI1MTUxMzY4NTg5OTkyNzMyMDE1ODAwNjAzNzYxOTI3MTg3MDM4MDkxNDY3MDE1MjA3MzIwNDczMDM0NDA3MDIyNDA0NjQ4MTI0NTk2NjQwNjU1NjY1MTIzMzY5Njc0ODI2NDE3MjE2ODUxNTM4Njc1NTM3NzAwOTg4MTQzNzE1NTE3NzMwMTM4NjA4NzkxMjcyMzM0MDUyMzY4OCJdXX0sIm5vbmNlIjoiNDE0MzQ4Njg0NDk2OTAxNjkyMjI2OTY0In0=" + } + } + ], + "~thread": { + "thid": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b" + }, + "~service": { + "recipientKeys": ["DXubCT3ahg6N7aASVFVei1GNUTecne8m3iRWjVNiAw31"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3000" + } + }, + "updatedAt": "2023-03-18T18:54:01.134Z" + }, + "id": "669093c0-b1f6-437a-b285-9cef598bb748", + "type": "DidCommMessageRecord", + "tags": { + "role": "receiver", + "associatedRecordId": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "protocolName": "issue-credential", + "messageName": "offer-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/2.0/offer-credential", + "messageId": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7" + } + }, + "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3": { + "value": { + "_tags": {}, + "metadata": {}, + "credentials": [], + "id": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "createdAt": "2023-03-18T18:54:01.133Z", + "state": "offer-received", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "protocolVersion": "v2", + "updatedAt": "2023-03-18T18:54:01.136Z" + }, + "id": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "type": "CredentialRecord", + "tags": { + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "state": "offer-received", + "credentialIds": [] + } + }, + "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3", + "createdAt": "2023-03-18T18:54:01.369Z", + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "role": "receiver", + "message": { + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "@id": "578fc144-1e01-418c-b564-1523eb1e95b8", + "credentials~attach": [ + { + "@id": "libindy-cred-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJhZ2UiOnsicmF3IjoiOTkiLCJlbmNvZGVkIjoiOTkifSwibmFtZSI6eyJyYXciOiJKb2huIiwiZW5jb2RlZCI6Ijc2MzU1NzEzOTAzNTYxODY1ODY2NzQxMjkyOTg4NzQ2MTkxOTcyNTIzMDE1MDk4Nzg5NDU4MjQwMDc3NDc4ODI2NTEzMTE0NzQzMjU4In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjMyMTIwMDQ1ODc4MzIxMjcyMzA1ODI5MTc3NzMzMTIwNzE1OTY5NDEyNjkwNjUyNDQ4OTc0MTA4NzEzNjU0ODc3NTg2MzIzMTI3ODk2IiwiYSI6IjIyMjY0MTYwNjIwODcwNDUyNTExMjcyMzE1MzMzMDA0MjQzMzY3NTM2NzM3NDMwNjM1NjExMjcwMDkwOTE4NDMwNzc0ODEzMjAzNjQwNjMxMjIyNDczMzk0MjQ4MTgzMDIzMjIyNzExNDUwMzQxMDcxOTQyNDQwMDgwMjY2Nzk1Mzg5Mzg5Njc1NjYwOTUzNTQyMDE4OTA3NjQ3NzI4OTQ4NjY1MzA2Njg0NjExNDU1NTI5NzM5OTY1NDcyMjQ2NDQxMzE1NzAxMzM1ODc1MDY3MjExMDk3NzcyOTgwMjU1NDIxMDMzMTI1MjAyMTQzNDk3NjMyOTAyMjM1NDAyMzU5OTA1MzY5MzE4MjI1NTc4MjUxNjY4NTYzNzc1NTY0MDM2MjUxNzE0Mzk3MTEzNjQ3OTg0MjcxMTE5MTU2NDQ3NjI1OTk1NjE5MjAwMDk4MTgzNzY1NjkzMTg1ODEzNjA1NDU3OTQwMzE0MDU2MDkzMTI2MzQ3OTU5MzYwODIyMzg0OTEzODg3Mjg3ODI2NjkyNDIyNDMyNDUwMDA5OTYxNjQ2MjMzNTE3MjY3NDU1OTkyMjA3MTE3Mzk5NzU1NjY3MTA3MzM1NTQ0MzEwNDQwNDE1NDE5NTk5NTA1OTgxMzkwMjk5NDUxNzQyODg4NDg0MTc0NTU5MDA5NDgwNjU5MDk2Nzg2ODI2MDgxNzc3MzcwNTk1MTU3OTg5NjQ1MDYxNDI2OTA2ODM2MDk5NTU5MDQ0MDI4ODM2MzYwOTM2MDkwOTkxNjA1OTU0NTM2OTQxMjgxODQwNzk2MjkxODc0ODk2NDEzNTM5IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDExMzE1MTE0OTUxODg0MDQ5ODkyNjAwNTY3NTgzMTc4ODkwMyIsInYiOiI2NTE5ODc4MzYyODQ5NzExNDY4NDkyNDM1OTM3MDU4NzMzOTYxMTkxNjA3NjI4MzUzNjkxMDg1MzM5MDMwNDU0OTkyODc0ODYyODkyNDg4ODYzNTA3MDQ1MTM1MzA4ODI1NDA2NzYwMTQwNDQzNzM0NDYzODE5NTM2MzE0NzcxMTQ3MDk4MjU2ODMzNTc2MjIwNDI5ODQyNjc3NzMwMzQwODYwNjE2NTcxNzc5NjU4OTIxNDY4Mjc0NTUwOTc5NjYyMDkxNzEwNDU5MDk2MDgzMzYzNTc1Mjc0MjQzNzIyMzIzOTIxMjY5MDYyMjE0NjQyNzQyMTI0MzQ4MTY0MDUxNzE3MTk5MTkzODY3NTM3NTEzNjYzOTY1ODQzMDI5MjAxODA0OTE2MTEzNzMxODYzOTUzNjQ5MDkwNDgzNzMyMTkxNTQ2MTEwMjAxNTg0NzMxODg4NTE5NjA2MjE1OTkyNTgxNzk2MDg2NzUzOTE5NzUxMjkwMDI3MDI4NzczMTAwOTc5ODI5MzQ5NzA0MTUyMDEzNjg2MzU1MzM1MjIyNjU5MDY2NzE0NDQ2NDc4NzY3MTE5NDE4MjY3OTg5NTAyNzc4MjMzNzM3MjM4MjU1MTQxNzQyMjk4NTU3MDY2NzA2MTM0NzYwMjQwMzY3OTMzMzc5NzYzMTc5MTI1NTI4MDQwMzkxNjQwNTIyNTM5NjE5NTU0NTE0NTk4OTUxNTg0NjA3MjYwNzk1NzE1MDMyMjM4NTQ3ODMyMzA0NTY2MzQ4NjYzMTc0NzQwMDE2MzQ2NTU2MTM1ODc4MzgxNTYzODQ2NzU0MzQzMjk0NTIzNjc0NDI3NjQxNjAxNjAwNjE2NzI3NjEyMzc0MzI2NzY4ODA5NjAyNTE5MTAzOTk3NDY4OTg1NTg3Nzg4MjI3Njc5MzQ4NTgwNzk1OTkyOTkxNzMzMDg5MTUyMTg2MDg4OTU2MTg2MTQ0OTkyMDI5OTI2OTUxOTU0OTQyNjYwMDUxOTM0MDc5NzkxODI1NzA2MTExNzg0MDU2NDM2OTA2MDgxMDQ2MDQ5ODI0ODE1NDE0MTc5NTMzMDA2ODE4NzQ3NzgwNTQ5In0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjEwMTUyMDI0OTk1MTUxMzcyOTgwNzI5NDM1MTQ5MzgyMzQxMDQ3MDIzNjI2NjA4MDc3ODg1NzQ2Mjg4MTE3MjI2OTAyNzM4OTY5OTEwMTU0NjgzNzM2MzI4MzQ2MDg0MjcwMDA3MTY2NTg3ODI5MjE2MzEzNDc4MDk3Njc0MTI0ODU2ODIyMjA4NzI0Nzk1NTE0ODgzOTYwMzE5NDc5OTg4NzAzNDUyNjI4NjYxMDc3MTg3OTIyMzA1NDc5MDE2NzQzOTk0NzYwMzE5NzI1OTExODk0MjM2NDMxMDkxMTIyNTUxNTU0NzgwODg0NjQ2MjE0MTUzMDUzMTM2NDMwMTk4OTA5MTM0OTk4OTM2NjY3MzI4ODI2MDcwNzEzMzk0NDg0NDI0ODUxNjkxMzUxNDc0NjAxMjIwODk2NTIyMDYzNDA5NzA4NDA1Njk2MzY5MjA0MzU0NzE1MDkxMzk2Mzc4Mzc3MzA0ODk3MzMwOTM0Mjc2NTQyNjE2NjAxNTk1ODI5NzgxOTg3NTMyNzkxMzIyNTgzOTE1Njk1OTY2MjM3MTc4Njg1NTMzNTE3MTQxNTAyNDE3MzQxMDIzMTA1MTczMjMwMTcwNzUzODYwMjgxNDAxODk4MDE5OTQwNjA2MzczOTYwMzYxNjA3NTE2NjgyMDg4MTc1NzU4ODA0Mzg4MTM5MTQ0MDkwMjg5MzI5NzMzNTQ1NDg4MjUyNjczNDIyODkzMzc1MzE5ODQ2OTMwOTIyNjIwNzAzMTEwMDgwODU5OTE4ODQ0MzgyOTQ3ODczMjAwNzA4MTY2MzA0NDk4ODk0MDA4NTMyIiwiYyI6IjIyNDQyNTM5MzYwMzYzNjQyODI1ODkxNTc5ODgzMDE5Mjc3Mjk0NTQ2MjUwMDEzNTM3MzI2OTY2NzM3MzE0NTUxMjEwMjU3MjU2NDU5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9" + } + } + ], + "~thread": { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841" + }, + "~please_ack": { + "on": ["RECEIPT"] + }, + "~service": { + "recipientKeys": ["GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3000" + } + }, + "updatedAt": "2023-03-18T18:54:01.369Z" + }, + "id": "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3", + "type": "DidCommMessageRecord", + "tags": { + "role": "receiver", + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolName": "issue-credential", + "messageName": "issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "messageId": "578fc144-1e01-418c-b564-1523eb1e95b8" + } + } +} diff --git a/packages/anoncreds/src/updates/__tests__/__fixtures__/issuer-anoncreds-2-schema-credential-definition-credentials-0.3.json b/packages/anoncreds/src/updates/__tests__/__fixtures__/issuer-anoncreds-2-schema-credential-definition-credentials-0.3.json new file mode 100644 index 0000000000..b4bd2e2d08 --- /dev/null +++ b/packages/anoncreds/src/updates/__tests__/__fixtures__/issuer-anoncreds-2-schema-credential-definition-credentials-0.3.json @@ -0,0 +1,431 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2023-03-18T18:53:43.140Z", + "storageVersion": "0.3.1", + "updatedAt": "2023-03-18T18:53:43.140Z" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "fcdba9cd-3132-4e46-9677-f78c5a146cf0": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "fcdba9cd-3132-4e46-9677-f78c5a146cf0", + "schema": { + "ver": "1.0", + "id": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", + "name": "Test Schema", + "version": "5.0", + "attrNames": ["name", "age"], + "seqNo": 728265 + }, + "updatedAt": "2023-03-18T18:53:45.521Z" + }, + "id": "fcdba9cd-3132-4e46-9677-f78c5a146cf0", + "type": "AnonCredsSchemaRecord", + "tags": { + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", + "schemaIssuerDid": "did", + "schemaName": "Test Schema", + "schemaVersion": "5.0" + } + }, + "de4c170b-b277-4220-b9dc-7e645ff4f041": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "de4c170b-b277-4220-b9dc-7e645ff4f041", + "schema": { + "ver": "1.0", + "id": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", + "name": "AnotherSchema", + "version": "5.12", + "attrNames": ["name", "height", "age"], + "seqNo": 728266 + }, + "updatedAt": "2023-03-18T18:53:48.938Z" + }, + "id": "de4c170b-b277-4220-b9dc-7e645ff4f041", + "type": "AnonCredsSchemaRecord", + "tags": { + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", + "schemaIssuerDid": "did", + "schemaName": "AnotherSchema", + "schemaVersion": "5.12" + } + }, + "6ef35f59-a732-42f0-9c5e-4540cd3a672f": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "6ef35f59-a732-42f0-9c5e-4540cd3a672f", + "credentialDefinition": { + "ver": "1.0", + "id": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG", + "schemaId": "728265", + "type": "CL", + "tag": "TAG", + "value": { + "primary": { + "n": "92212366077388130017820454980772482128748816766820141476572599854614095851660955000471493059368591899172871902601780138917819366396362308478329294184309858890996528496805316851980442998603067852135492500241351106196875782591605768921500179261268030423733287913264566336690041275292095018304899931956463465418485815424864260174164039300668997079647515281912887296402163314193409758676035183692610399804909476026418386307889108672419432084350222061008099663029495600327790438170442656903258282723208685959709427842790363181237326817713760262728130215152068903053780106153722598661062532884431955981726066921637468626277", + "s": "51390585781167888666038495435187170763184923351566453067945476469346756595806461020566734704158200027078692575370502193819960413516290740555746465017482403889478846290536023708403164732218491843776868132606601025003681747438312581577370961516850128243993069117644352618102176047630881347535103984514944899145266563740618494984195198066875837169587608421653434298405108448043919659694417868161307274719186874014050768478275366248108923366328095899343801270111152240906954275776825865228792303252410200003812030838965966766135547588341334766187306815530098180130152857685278588510653805870629396608258594629734808653690", + "r": { + "master_secret": "61760181601132349837705650289020474131050187135887129471275844481815813236212130783118399756778708344638568886652376797607377320325668612002653752234977886335615451602379984880071434500085608574636210148262041392898193694256008614118948399335181637372037261847305940365423773073896368876304671332779131812342778821167205383614143093932646167069176375555949468490333033638790088487176980785886865670928635382374747549737473235069853277820515331625504955674335885563904945632728269515723913822149934246500994026445014344664596837782532383727917670585587931554459150014400148586199456993200824425072825041491149065115358", + "name": "26931653629593338073547610164492146524581067674323312766422801723649824593245481234130445257275008372300577748467390938672361842062002005882497002927312107798057743381013725196864084323240188855871993429346248168719358184490582297236588103100736704037766893167139178159330117766371806271005063205199099350905918805615139883380562348264630567225617537443345104841331985857206740142310735949731954114795552226430346325242557801443933408634628778255674180716568613268278944764455783252702248656985033565125477742417595184280107251126994232013125430027211388949790163391384834400043466265407965987657397646084753620067162", + "age": "12830581846716232289919923091802380953776468678758115385731032778424701987000173171859986490394782070339145726689704906636521504338663443469452098276346339448054923530423862972901740020260863939784049655599141309168321131841197392728580317478651190091260391159517458959241170623799027865010022955890184958710784660242539198197998462816406524943537217991903198815091955260278449922637325465043293444707204707128649276474679898162587929569212222042385297095967670138838722149998051089657830225229881876437390119475653879155105350339634203813849831587911926503279160004910687478611349149984784835918594248713746244647783" + }, + "rctxt": "49138795132156579347604024288478735151511429635862925688354411685205551763173458098934068417340097826251030547752551543780926866551808708614689637810970695962341030571486307177314332719168625736959985286432056963760600243473038903885347227651607234887915878119362501367507071709125019506105125043394599512754034429977523734855754182754166158276654375145600716372728023694171066421047665189687655246390105632221713801254689564447819382923248801463300558408016868673087319876644152902663657524012266707505607127264589517707325298805787788577090696580253467312664036297509153665682462337661380935241888630672980409135218", + "z": "60039858321231958911193979301402644724013798961769784342413248136534681852773598059805490735235936787666273383388316713664379360735859198156203333524277752965063504355175962212112042368638829236003950022345790744597825843498279654720032726822247321101635671237626308268641767351508666548662103083107416168951088459343716911392807952489009684909391952363633692353090657169830487309162716174148340837088238136793727262599036868196525437496909391247737814314203700293659965465494637540937762691328712617352605531361117679740841379808332881579693119257467828678864789270752346248637901288389165259844857126172669320275054" + } + } + }, + "updatedAt": "2023-03-18T18:53:55.036Z" + }, + "id": "6ef35f59-a732-42f0-9c5e-4540cd3a672f", + "type": "AnonCredsCredentialDefinitionRecord", + "tags": { + "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG" + } + }, + "1545e17d-fc88-4020-a1f7-e6dbcf1e5266": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "1545e17d-fc88-4020-a1f7-e6dbcf1e5266", + "credentialDefinition": { + "ver": "1.0", + "id": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222", + "schemaId": "728266", + "type": "CL", + "tag": "TAG2222", + "value": { + "primary": { + "n": "92672464557302826159958381706610232890780336783477671819498833000372263812875113518039840314305532823865676182383425212337361529127538393953888294696727490398569250059424479369124987018050461872589017845243006613503064725987487445193151580781503573638936354603906684667833347097853363102011613363551325414493877438329911648643160153986822516630519571283817404410939266429143144325310144873915523634615108054232698216677813083664813055224968248142239446186423096615162232894052206134565411335121805318762068246410255953720296084525738290155785653879950387998340378428740625858243516259978797729172915362664095388670853", + "s": "14126994029068124564262196574803727042317991235159231485233854758856355239996741822278406673337232628669751727662479515044513565209261235580848666630891738643990084502393352476512637677170660741636200618878417433799077613673205726221908822955109963272016538705991333626487531499501561952303907487494079241110050020874027756313402672435051524680914533743665605349121374703526870439925807395782970618162620991315112088226807823652545755186406850860290372739405126488851340032404507898084409367889215777693868794728141508635105180827151292046483128114528214465463152927678575672993454367871685772245405671312263615738674", + "r": { + "master_secret": "26619502892062275386286102324954654427871501074061444846499515284182097331967223335934051936866595058991987589854477281430063143491959604612779394547177027208671151839864660333634457188140162529133121090987235146837242477233778516233683361556079466930407338673047472758762971774183683006400366713364299999136369605402942210978218705656266115751492424192940375368169431001551131077280268253962541139755004287154221749191778445668471756569604156885298127934116907544590473960073154419342138695278066485640775060389330807300193554886282756714343171543381166744147102049996134009291163457413551838522312496539196521595692", + "age": "66774168049579501626527407565561158517617240253618394664527561632035323705337586053746273530704030779131642005263474574499533256973752287111528352278167213322154697290967283640418150957238004730763043665983334023181560033670971095508406493073727137576662898702804435263291473328275724172150330235410304531103984478435316648590218258879268883696376276091511367418038567366131461327869666106899795056026111553656932251156588986604454718398629113510266779047268855074155849278155719183039926867214509122089958991364786653941718444527779068428328047815224843863247382688134945397530917090461254004883032104714157971400208", + "name": "86741028136853574348723360731891313985090403925160846711944073250686426070668157504590860843944722066104971819518996745252253900749842002049747953678564857190954502037349272982356665401492886602390599170831356482930058593126740772109115907363756874709445041702269262783286817223011097284796236690595266721670997137095592005971209969288260603902458413116126663192645410011918509026240763669966445865557485752253073758758805818980495379553872266089697405986128733558878942127067722757597848458411141451957344742184798866278323991155218917859626726262257431337439505881892995617030558234045945209395337282759265659447047", + "height": "36770374391380149834988196363447736840005566975684817148359676140020826239618728242171844190597784913998189387814084045750250841733745991085876913508447852492274928778550079342017977247125002133117906534740912461625630470754160325262589990928728689070499835994964192507742581994860212500470412940278375419595406129858839275229421691764136274418279944569154327695608011398611897919792595046386574831604431186160019573221025054141054966299987505071844770166968281403659227192031982497703452822527121064221030191938050276126255137769594174387744686048921264418842943478063585931864099188919773279516048122408000535396365" + }, + "rctxt": "71013751275772779114070724661642241189015436101735233481124050655632421295506098157799226697991094582116557937036881377025107827713675564553986787961039221830812177248435167562891351835998258222703796710987072076518659197627933717399137564619646356496210281862112127733957003638837075816198062819168957810762822613691407808469027306413697001991060047213339777833838291591976754857934071589843434238025803790508552421154902537027548698271140571140256835534208651964449214890690159171682094521879102663244464066621388809286987873635426369915309596945084951678722672915158041830248278889303704844284468270547467324686757", + "z": "90415953543044389740703639345387867170174070770040351538453902580989033567810029650534915348296084212079064544906463014824475317557221991571331212308335167828473551776349999211544897426382305096215624787217055491736755052175278235595298571339706430785816901931128536495808042995635624112114867111658850659510246291844949806165980806847525704751270260070165853067310918184720602183083989806069386048683955313982129380729637761521928446431397104973906871109766946008926113644488012281655650467201044142029022180536946134328567554182760495139058952910079169456941591963348364521142012653606596379566245637724637892435425" + }, + "revocation": { + "g": "1 1864FF219549D1BC1E492955305FC5EED27C114580F206532D2F5D983A1DD3BD 1 0414758D7B6B254A9CA81E1084721A97CA312497C21BB9B16096636C59F9D105 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "g_dash": "1 2327DA248E721E3935D81C5579DD3707882FFB962B518D37FB1112D96CC63611 1 164989452135CF5D840A20EE354DBF26BEEC74DE7FD53672E55224BEE0228128 1 0634D5E85C210319BFD2535AFD8F7F79590B2F5CC61AF794218CC50B43FBB8C6 1 0A63F1C0FC2C4540156C7A2E2A2DF1DDF99879C25B4F622933707DD6074A0F1B 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + "h": "1 0A031B1932CDFEE76C448CA0B13A7DDC81615036DA17B81DB2E5DFC7D1F6CD6F 1 06F46C9CC7D32A11C7D2A308D4C71BEE42B3BD9DD54141284D92D64D3AC2CE04 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h0": "1 1C88CA353EF878B74E7F515C88E2CBF11FDC3047E3C5057B34ECC2635B4F8FA5 1 1D645261FBC6164EC493BB700B5D8D5C8BF876FD9BA034B107753C79A53B0321 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h1": "1 16AC82FE7769689173EABA532E7A489DF87F81AE891C1FDA90FE9813F6761D71 1 147E45451C76CD3A9B0649B12E27EA0BF4E85E632D1B2BEC3EC9FFFA51780ACE 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h2": "1 2522C4FAA35392EE9B35DAC9CD8E270364598A5ED019CB34695E9C01D43C16DC 1 21D353FB299C9E39C976055BF4555198C63F912DBE3471E930185EF5A20470E5 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "htilde": "1 24D87DBC6283534AE2AA38C45E52D83CC1E70BD589C813F412CC68563F52A2CA 1 05189BC1AAEE8E2A6CB92F65A8C0A18E4125EE61E5CEF1809EF68B388844D1B1 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h_cap": "1 1E3272ABDFD9BF05DB5A7667335A48B9026C9EA2C8DB9FA6E59323BBEB955FE2 1 031BD12497C5BBD68BEA2D0D41713CDFFDCBE462D603C54E9CA5F50DE792E1AB 1 05A917EBAA7D4B321E34F37ADC0C3212CE297E67C7D7FEC4E28AD4CE863B7516 1 16780B2C5BF22F7868BF7F442987AF1382F6465A581F6824245EFB90D4BB8B62 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + "u": "1 1F654067166C73E14C4600C2349F0756763653A0B66F8872D99F9642F3BD2013 1 24B074FFB3EE1E5E7A17A06F4BCB4082478224BD4711619286266B59E3110777 1 001B07BEE5A1E36C0BBC31E56E039B39BB0A1BA2F491C2F674EC5CB89150FC2F 1 0F4F1E71A11EB1215DE5A081B7651E1E22C30FCCC5566E13F0A8062DB67B9E32 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + "pk": "1 0A165BF9A5546F44298356622C58CA29D2C8D194402CAFCAF5944BE65239474E 1 24BA0620893059732B89897F601F37EF92F9F29B4526E094DA9DC612EB5A90CD 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "y": "1 020240A177435C7D5B1DBDB78A5F0A34A353447991E670BA09E69CCD03FA6800 1 1501D3C784703A097EDDE368B27B85229030C2942C4874CB913C7AAB8C3EF61A 1 109DB12EF355D8A477E353970300E8C0AC2E48793D3DC13416BFF75145BAD753 1 079C6F242737A5D97AC34CDE4FDE4BEC057A399E73E4EF87E7024048163A005F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000" + } + } + }, + "updatedAt": "2023-03-18T18:53:59.067Z" + }, + "id": "1545e17d-fc88-4020-a1f7-e6dbcf1e5266", + "type": "AnonCredsCredentialDefinitionRecord", + "tags": { + "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222" + } + }, + "d7353d4a-24fc-405f-9bf5-f99fae726349": { + "value": { + "metadata": {}, + "id": "d7353d4a-24fc-405f-9bf5-f99fae726349", + "createdAt": "2023-03-18T18:53:59.857Z", + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "role": "sender", + "message": { + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "@id": "c5fc78be-b355-4411-86f3-3d97482b9841", + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": [ + { + "name": "name", + "value": "John" + }, + { + "name": "age", + "value": "99" + } + ] + }, + "offers~attach": [ + { + "@id": "libindy-cred-offer-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiODUxODAxMDMyNzEzNDg5NzYxOTg5MzAzNjMzMDkzOTEyOTExMDUxNjI0OTQ0OTYzMTgzNzM2MDY3NDkwOTc2MDYxODEwMDgxODkxMzQiLCJ4el9jYXAiOiI4NDk0NDg4MjQzNTk2NTkwOTc2MjQzMjc0NDg4ODk2Mjc1NTcyODAyMTQ1ODE5NDQzNTE0NzQxMzk1NDI1NjM5MzQwMTczMDIzMTQ5NzI3MDY5NzMzMzQwODgzMTU4MzQ1NTYzOTA5OTcxNDMzNTg1MjMwODAxNTYyMTM0NjczNjM1ODg5NTA3Njg5ODQwOTgyODU5Mzg1NjA1MTc1NTkxNDYxOTkyMDExNzU2Mzg1MTI3MTQ3ODgxNDMwODEzNjYxNzY0MDU5MzE0ODk4MTc2NzQzMTQ5MjYzMDMwMDQ1NzMwMDMzMzI2NzgyMzg1OTY0NjcxMzg1ODQ2MzcxNjQ5MzQxMTg2MDM5NjE4MTQwOTIwMDUxMzg1MDAwNTYxMDcyMTc5NTEyMzc5Nzk0OTU4NjE1ODIyODI2OTExNzIwNTQyNTE0MTQ1NDc5MTAxOTUyMzM4MDMwMDY1MDk5NjcxOTU2OTMxMzE2NjE5MjM0NTQ0NTE5NTQ1ODQ1MzA4MzgxMjQyNTM0NDcyOTc3NjY0MjAwMjc2MTMyOTgxODE1ODAzNTIxOTExMzk4ODkxMjE0NjE1NzA1MDM2ODM2ODU1NDU1NzY4ODg4MTUxNDgzODAyNDcyODQyMzczNzE0MTI0NTYwMzIyNTI3NDE4MTEwNzYyMjgyNzY4NzMyNTIzMDQyMDA3MDY2OTk2ODIxMTQwMzE1NDg0NzI4NTM2NzIwNDI3MDg5MTI2NDk1NTAzMjc0ODQ4MDM3MjUzOTM3NjI3MDU2ODUzMTQ4NjE5NDA4NDYxOTI5NzEzMjM4MjEwNDc4MjcyMTIxNTUwNjQzODc4ODM1NDYwMzY1OTIwMjE3NTk5NDYyNDUzMDMyNDQ4MjYyMTM3NjE5ODY0OTU4MzA1MDE3MjA4OTYwNDc1MTQxODgwMTMiLCJ4cl9jYXAiOltbIm5hbWUiLCI1MDcyNzU2NDE2NDA2ODIxNzU1OTc0MzUxMTg0NjE1NjA4NDY2NTk3Mzk0NzA2MTY1NDg2ODAzMjc3MjMyMzQyOTk4MDA0MzY0OTU0MTczMzc0NDIwOTc5NTkwMDcyODgxNDgxNDA0MTg2OTExODg5NzQ4MTgzMzQ1OTk5NzQ0NzgxMTQ1MTMwNzEyNDIzODY0Nzc1MzQzNjAzNTk2NDM3Mzg4OTgzNTExNDAzODA0NjEyNjU1MDE5NzQ4MTI5NDk3ODY2NTcwMDQyMjcwNDQxNDQ5MjYwODY0NzgyMzI5MjAxNDEzMTc5ODU3NzA0MjM5OTMyMTg4NTc4NzE3MDczNzM3NjUyNzY5MzY5NDg4OTgxNzg2NDQwNTExODAzMjMzNDMxNzA4NDk4MTU2NTA0OTUzNzkzNjU2NjQ2NzMyNTU4MzQwNDI2MDI1MjA3NTk0OTIwMDY4OTc2OTQ4Nzg2OTUxNzM3MDIwNDQ0NTA5NzYyMDQ2MzIzNzA0MDQ3MjU1ODU3NDE5ODE3MDc5NTI3NDgzNTE1NDY2NTAyMDkzOTY1NDMzMzk3MjQ1MzA4MjQ5MDgyMTQ4Mjc4NDA1MzI5Njg1Mjc0MDYwNjk0MzI0MTI2ODgxMjkyMDIyMjY1ODczMjk5MDU0NDU1OTA5NzkyNjUwNjAyMTk0NjUzMjYxMDk0ODYwOTc2NzA4ODE1ODgwMjExMTY0MTkwMDM0NjY0MzI2MDc3NjcwNzkyMDE4NTE2MzMzNDI3NjkwODYwMjIxODEwMzk5MDgxMjc5NjAwNTYzMjk3MjI0NjM0MDM0NjcxNTIwODE5MzU3NzQ0Njk2NzU1Njg1NDI2NjIzMzAwMjQ3MDUwODE4NTQ2MDM2NjA0NjMxNjcyNzE5MjI0NDA4NTE2NDM4NTgxMDM5Njk4NzI0MSJdLFsibWFzdGVyX3NlY3JldCIsIjU2MzYzNTgyMDQ5Mjg4OTY1OTg1MDA4NzgyMzU0NjgyNjMwNDkxMzQ3MTM1NDIxNTAyMDEyMTIwMzI4MDI4ODIyMjUyMzg4NjgwNTMwNTgxMTcwMTgwNDU1MTcyNTc3ODkyMTEyMTY1OTM0Mjk5NjUyNzAxNDExMzUyNDkzMzkyODU0ODI4NzMyMDQzMDI0MDI0MzM0MzMzNzc0NjEyOTEzOTUyMjAzNjM1NDk2MDQ0ODMzMDI5NDE2NjUwOTU5NjE0ODgzNTUwOTMxNzgzNTA5MzE1Nzg4MDEyODQ0MzAwMDQwMDE5MTY5MTc3NTI1OTgxMTU3OTkwNjQzMDcyMjQyNzcxMjU0MTYyNzMxOTU4NzI2Nzc1NjYwMjkxODIzMDcyNDk1Mzg0NzM5MTcwODc4ODMxNzkxMjQzMjEzMjU5MzA5ODQxNjU3MjUwOTg1NzMxMjEyNzE2MDM2MDY3MDUxNjM2NzA0MjA1NDEzMDk2MDU3MTA2NTM2MTI2ODUyNDU0NzcwMzQzMTMwMTczMjAwNjEzMDIxOTE4MzgzMDQxOTU4MTkwOTE2NzQ0NjU4NTI0ODA1NjM4Mzk2OTY3OTA3MzIwNjY1MDU1MzcwMjY0NjAxMDczMjc5NDMyNjM5MjM3Njc1NTA0OTg1NzQyNTI4NjYwMTAyMDEzNzIxMzA2MTE4MTg0NDk1MTEyNDQ2NDYyNDc2NTkwMjYxODkxMjA0OTQxOTA4MjMyNzMzNDA3MTg4MDA3NzE2NTA2OTUzMDY0Nzc5NDk5ODExNzI0ODI5NjcyNjY2NzIyNjIzOTAxMTc1OTk0NTIyNjkwMjk1ODI0MDgyNzY5NjQ0NDYxOTAxMDk2NzI3MTE5NzAzMjUzNzI4NjY3MTU1MzA5MDYzNDUyNDY2MDY3NzU5NzIwOTgyNDA3MiJdLFsiYWdlIiwiMTM2NTQxMjE0MjM5MTcyNDQxNzQ1MjU3MjcyMDI3MTA4NDYwMzU0MjgxMTA2OTA2MzYwNDIwMDE0NjUyMDIxMDgyNDEzODM2ODEyMjk3NjY3ODk2MTYzNDkzMjM4NDIxNDI4NjMyNTMxODE0ODk4NzkwMDg4OTg2NjgyMTE2OTAyMzc4NDgwNTE4OTUxNDExNzg1OTk3NTk5MDMyNDYxNjExNjIyMDUyNjMzMDQ5ODYxMzc5MTQzNzI4MTM5MTUyMDkyMzI0ODc3MjMxMTYwNTgzNzA5NjE0MzA1NzQ1MjA5MjQwNjU2MDU4NjY3OTMwODEzNzYyNDY5MDc2ODc5MTk1Nzg0Nzg4NTE2NjI3MjgxMDY0NjE3MzgzMDc4Njc5MTkwODIwMzQwNTgwNDY2MjU3ODU3NjA1MTc2MTg4NTI3OTMxMDI4MTMzNTY5Njc0Mzg2ODAwMTA2MDE2MDg1Nzc0OTcyMzI1NTAyNDA2MTY0OTY0MjU2OTUxNDI3ODAxMTQzNTQxMzUzMzI0Nzg0MzA5OTY4MjIyOTU1NDk4Njk3NTAwMDUwMzc0MDg0NjIwNzQ4MTk0NzIyMTI2NjE2OTY3OTY3Mzc1NTM3Nzc5NTc4NTMwMDIxODExNTA2MTIxNjcxMDUwNDgzNTM2MjA3Njc3MTg5NDQwNjEwNzk0NTcyNzI5MTgzMzAyMjM1MDkxMDg4NTU2ODc5NTg3OTE3MDMzMzQyODcyMzg2NDQ5MTQ0NzgwMDYyNjc4MzA3NzE4MzU1MjQ5MTUxNjc5MDA1MzkxNDA5NDE4OTQxMjEzNDkxMjQyMjg2NTAwODcyMzQxNDI3Nzk1MjQ1ODYzODE2MDY2NDY3NDkxOTg4OTU3MDEwNDIxNDA3NDkyMDUxOTc0NTMwNjIxOTk1ODU0ODczNTM5Mjk3MyJdXX0sIm5vbmNlIjoiNzk3NjAzMjE3NzA5MzM1MzAwMTcwODI4In0=" + } + } + ], + "~service": { + "recipientKeys": ["GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3000" + } + }, + "updatedAt": "2023-03-18T18:54:00.011Z" + }, + "id": "d7353d4a-24fc-405f-9bf5-f99fae726349", + "type": "DidCommMessageRecord", + "tags": { + "role": "sender", + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolName": "issue-credential", + "messageName": "offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "messageId": "c5fc78be-b355-4411-86f3-3d97482b9841" + } + }, + "be76cfbf-111b-4332-b1fe-7a1fea272188": { + "value": { + "metadata": { + "_internal/indyCredential": { + "schemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0", + "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG" + } + }, + "credentials": [], + "id": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "createdAt": "2023-03-18T18:53:59.068Z", + "state": "done", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolVersion": "v1", + "credentialAttributes": [ + { + "mime-type": "text/plain", + "name": "name", + "value": "John" + }, + { + "mime-type": "text/plain", + "name": "age", + "value": "99" + } + ], + "updatedAt": "2023-03-18T18:54:01.378Z" + }, + "id": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "type": "CredentialRecord", + "tags": { + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "state": "done", + "credentialIds": [] + } + }, + "e531476a-8147-44db-9e3f-2c8f97fa8f94": { + "value": { + "metadata": {}, + "id": "e531476a-8147-44db-9e3f-2c8f97fa8f94", + "createdAt": "2023-03-18T18:54:00.005Z", + "associatedRecordId": "a56d83c5-2427-4f06-9a90-585623cf854a", + "role": "sender", + "message": { + "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", + "@id": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", + "formats": [ + { + "attach_id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "format": "hlindy/cred-abstract@v2.0" + } + ], + "credential_preview": { + "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", + "attributes": [ + { + "name": "name", + "value": "John" + }, + { + "name": "age", + "value": "99" + }, + { + "name": "height", + "value": "180" + } + ] + }, + "offers~attach": [ + { + "@id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6QW5vdGhlclNjaGVtYTo1LjEyIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY2OlRBRzIyMjIiLCJrZXlfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjQwMTQ0MTA0NDg3MjM0NDU2MTc1NzYwMDc1NzMxNjUyNjg1MTk0MjE5MTk5NDk3NDczNTM4NjU4ODM3OTIyODMzNTEzMDg0Nzk2MDQ5IiwieHpfY2FwIjoiMzgxOTQyMjM1Mzc3MzYwODEyNjY0MDA4MjYzNDQxMDg2MDMwOTMyMjAxNzgzMjM3ODQxODQ5NDg3ODk2ODg1MTYwODY2MTY1MDM3NzI2MTIxNjU0MjcwOTg5NDY3NjAzNDExOTAzODk4MzUwMDAzNDIwODg3MzI4NTUwMTY2MTI1ODMyMjAxOTQzMTkwNzAxMDU4NTAwMDE5ODM1NjA1ODczNDYzOTkwODg3NzQ0NjY3MzU0MjM2Njc3MzcyODg0ODQyNjE5NTEwMTUwOTA2MjI1OTMzMjc1ODEyNjg2NDg3NTg5NjY3ODI3MjAwODcwOTQ0OTIyMjk5MzI3OTI4MDQ1MTk1OTIwMDI3NTc0MDQwNDA4ODU5MzAwMzY1MDYwODc3Nzg2ODkwOTE1MDU5NTA2ODc1OTI0NzE2OTI1MDM2MTc4Njg2NDE5NTYyMzcwODI4MTMzODY2Nzg3NzkyMDcwNjAyNDQzNTkzMTk2NzEzNzcyNDM2NTYzODI0MzkwMDIyNzg4MjU2MzA4NjU4OTc0OTEzMTk1ODYxODUwMTQ3ODE1Mjg5NzQwOTA4NDk1MjQ3NTAyNjYyNDc3NzQ2NTI5ODA3Mzg0OTgxODI5MDc3NTQ4OTI2NzExMDkzNzQ5MjM1ODU4NjUwNDc5NzE5NDI4MzUwMzAwNzUyNjQ0OTg1MTQ5MTMxNjA1NjUzMDIxMDYxNzkwMjY3MzQyNTY4NTkyNTY2MTQ0MDM5NzY4OTg0NTMyNDMzNzk0MzUzNjQ2Nzg1MjA3NDgzOTk2ODQ0OTcxNTgzNzY3NDQ5ODYyODgxMjMxMjI1MzM4MzAzMTQ4NzA0ODczMDEzNDM3MDgyNzY1MTk4OTY2MzE5NTM0OTkyNjk4MzMzMDQ0MDI3MjIyNTYyNTIzNzk3ODk5Mjk2MTQ1NDU5IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiOTE5MTc5NzQ4MTE5NTg5MTY3Njc5MjQxODk5NzY0ODIwNTk0MDc5OTQxNzIzOTgzOTYyNzQ1MTczODM0NDQxMDQ3MjU4MDcyOTE4OTUzNjIzOTQ4MDMyNzI2NDgyNzI2MTEwOTk2Mjk3MDU3NTYwNjcwNzAxOTU1MTkxNDc0NjM0MzQ0ODMxMzg3NTk4NzI2MzMxMjc0NjI4NDU3Njk5NzczMDA1NDMwMDIxNzMwMzg4MzcwMTEyMjc3MzI2MzU4OTgwMTA3ODIzNzUzODc3MTU0NjIwMDkzMjE5MjYyNjAxNDM2NzMyNTgzNDI4Nzc4NDA4OTc0NTQyNzkzMDk0NTQ5MTczOTA3MzQ3OTUxNTc1NjM5NzU2NDg5MTA0Mzk0MTY3NzExMzY1MjM3OTI1MjAwNjk4OTg5NTI5MTQ3OTIzNTYzNDMyODgyMzgwMTg0NzU0NzkzODMwMTE3MTQ1MDAwMTI0NDYxNjkzOTcxMDQ5MjgzNDk1NTE4MDQxMDc5ODUyMzAwMjk0NDM1MjYzOTIwNDU0NTU3MzUxNDQ1MDM3NDI4MDg3OTk2Mzg2NjY3NjU3Nzk5OTYyNzQzNzIyNzA3NzczOTEzMzc0NzIxODUyNTQ3MjkwMTY5MjI5NTAzMTQxOTMwODYzNTk4NTExNjc4NDEyMDE0MzE2MDM2MzYxMzczNDcwOTQwMDEyODcwMDgwMDA2MzE0NzYxNzYzNzUyNzYwODk5MTQ3NzA1MTA0NzQyNjAxNjkxMzMxNjkzMDIwMjg2MjA2NzQ2NzE0MzI3NjU2MjA2NTMzMjk3NDg4MjU2NTM2NTQ3MzY4MjM2OTQ2MDM5NzAzMzc0OTMzNTE0NTc2NDg2NjQyNTY4MjgyNTY2MjMyNDU1NTU5MDY4MzE3NzU5NDM0ODU4NTI3MDg2NjQ0Il0sWyJoZWlnaHQiLCI5MjMwMzkyNDc1NjI4ODc1MjA4OTM0NjM0NzE4MjYzNzA4MDIzOTI1MDU0NjY2NDgzMzgxMzIyMzc3MDg1MjMxMjU4MTM4MzgwOTU1NTk3NDQxNTEyOTYwNDA2MjI3MjUwODgyNjA3NjExMDkwODk3MTM1NDcxNzAwMDIzNDcwOTM2ODg4MDE3NDY5Nzk0ODYzNDk4NzUyNTI3Njc3MjMwMTEwNzg0ODQzNzI0NDUyNTUzODYyOTA2MzM5MDc0OTIzNDU4NTQ3NDYzODcwNzU3OTg5MzMxNzk4OTI2MjM4MjUxMTM2NTYzNjM2MjIyOTQwNDkwMzY3MjQ2OTg0OTU2NTE5MTAzODcwNDE0MDM5NzM2MDE2MDY5MzA2NjQ0NjQzODI4OTgxMTE3OTM3NzYyNDAzODY1Mjc1MDU5MjEyOTY2NzIxOTU3MzM0MTM2ODEyMDI0OTE0MzA4MzAxMzk5MzM4NzMyOTIzNTA0MjA5MDM5ODMxMTc5NjU1NTkyNjg0MjMyMTIzMTI2Mjc4ODQzNDMyOTUwMTk1Mjg3MzE4ODI3NTM2MTMwNDQ3NzM3MTgwMjk3MDE0ODEzNDg3NDQyOTg2NjQ1NzQyNjEyMzE5NzQxNDY2MDMyNTg5OTU0NzYwNjE4MDU0MDUxMjAzMTE1NTAxNDcxNDExMzg3NzU0NDk5MzAwNTU4MTc5NjM5NDAxOTM0NTAzMTMyMDEzMjAzOTg2NzkyMTEzMDAzNTkwODg1NTc3NjgyMzU2NDY3MjA5NTUwNjQxODQxMDYyNTkzNDYyODIwODg3NzgxNDYyODM3ODkzODcxNDM4MzM3Mjc5MTcwMTExMTQ5MTU4NDMzNDE0ODI1NTkyNjcyODU2MzM5OTM4NTgyODg2NzM3OTIwMjc1MzI0MjEwMTUzMjE5MjI2OTYiXSxbImFnZSIsIjkxNTg1ODk3NDkwNzE0ODA3OTY2MDYzOTg5MjE1NTMxNDkyOTQwMDI5NDcyMTM4MjgwNjcxNjcyMjQ0NjY5MDc5NzIyNTQyMDU0NTU3NjY0MTcxMDI1NzM1NjQ4NTIwMTM4ODQ4ODAxNzIyMTc4MTcxMTA5NTc0MTMyNTExMzM1MDEwNTc5NzExMzcyODM5MjI3MDExOTg4MTUyMTEwMzI4MTE5MjkyMjI4NjM3MDU4MDQ3NzYwODYwOTQ0NTY3MzQxMjY4MTY4Mjk3NjE5MDM2ODEwMjYwODM2NDI1NDkwMzU3NjE4NzM4NTYxNTY2MTUxODQ3MzIxNzM1MjQ5ODk1MDU5NTY2OTQxODI5MjE0Nzc0MTA0NzYyNTQwMjcyMjk2NjE1NTE3NjUwMDcyNDQyMTI0NjY5MDEzMTc1ODAyMDk5MDQxMzk3MzE5ODQ0OTA2MDgwOTYxNTcyMTcwNjg2NzgzNDM1Mjg2MDUyMzE5ODY3ODExMDE5MjAxMDYwODM2OTM3Mzc0MzM0NDM5MTQxMDAzMTI3NTcyNjgzNTgwODI0OTkwOTg3MjE5MzU4NzkzOTM2NTU4Nzk3MjI0MDQzNTM1ODA5NzMyNzgxMjE1NzEwNjI1MjQzODYwNTk4MTk0MjU2MjAwODkwOTA3ODAzMDcyMTAzNzc3MzkwODk4MDczOTgyNjY3Njc1ODg0MjI3MjU0Mzc2OTI5Mjg3ODQyNDE0MTE0MjcwNDQwMTEzNDUxNjk4NzE5Nzc5NjQyNTI4MDA4NDM3Mzk5NjI0NTE3OTM4Nzg5MDc3ODE5ODA0MDY5MzcxOTM0NzExMTIyNTQyODU0OTg4MDA0Mjc4NDkwMjAxNTk2NjE0MjUwODc3NDYxMDczNjc3NTUzNzYxMTMyMTA5Nzg3NTQ2ODE1ODk5Njc2NCJdLFsibmFtZSIsIjYyNzgwNTIwMTM3MzI3NTUzMDc3MDg4NTE4NDg1NDYyMTA0NjEzMjEyNzY3ODUwMzYwNTc3NDQ4MDUxNTk5MTMxMTM1NTI2NzQ3Nzc2NzMzMDg1MDMwODcyMDE1OTM2MTI2NzE0MTIxMDgxMzg3ODU2MTkwMTkzMzI3ODY3OTE0NTEzODM2NTQ1OTY4Mjg1NTc5ODEyODMxMDI4ODc2Nzg1NzI3OTQ2MTEwNzg5Mzc0MjcyODgzMzkyOTgwNDkwODk3NDkwMTc5MDQ0ODM0NTgwMzQ2ODY4NDI2ODc0ODU4NTY1OTg4NTUyMDcwNjI1NDczNjM4MDM3Njc5NTU1NTk2MzE5MTc3Nzc5OTcxMTIxMjQzMjgyMTIyOTQ2NjY0ODMxOTgxMTg3MzQ3MzcyMjkxMjYwOTM3MzkzNDA1ODk5OTI0NjM4MzE3ODI5MDczODMxMjI4ODc1Njg5MTcyMTg4NjIyMDI5NzcxNzM5MTQ5NDY2Mzg3NTM5NjkyNDQ5NDU5MjczNDI5NzM5MjMzNjkyNTEzNDA5OTkyNDgxNTQ4ODk0NjAzNjM3MTYzNjA4MTM0MTAzMTk3Nzc3NTM4OTYwMDcyMjcyMzYyNzM4NDM1MTM3MDcyNzIxMjExMDYxNTg4MDE3ODczODg3MTEwNDA2OTk1NDQ4ODIwMDEzMDA5MjgyMzk0OTczMDMwMDI5MTY3NjQ5NzY1OTI1MTUxMzY4NTg5OTkyNzMyMDE1ODAwNjAzNzYxOTI3MTg3MDM4MDkxNDY3MDE1MjA3MzIwNDczMDM0NDA3MDIyNDA0NjQ4MTI0NTk2NjQwNjU1NjY1MTIzMzY5Njc0ODI2NDE3MjE2ODUxNTM4Njc1NTM3NzAwOTg4MTQzNzE1NTE3NzMwMTM4NjA4NzkxMjcyMzM0MDUyMzY4OCJdXX0sIm5vbmNlIjoiNDE0MzQ4Njg0NDk2OTAxNjkyMjI2OTY0In0=" + } + } + ], + "~thread": { + "thid": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b" + }, + "~service": { + "recipientKeys": ["DXubCT3ahg6N7aASVFVei1GNUTecne8m3iRWjVNiAw31"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3000" + } + }, + "updatedAt": "2023-03-18T18:54:00.014Z" + }, + "id": "e531476a-8147-44db-9e3f-2c8f97fa8f94", + "type": "DidCommMessageRecord", + "tags": { + "role": "sender", + "associatedRecordId": "a56d83c5-2427-4f06-9a90-585623cf854a", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "protocolName": "issue-credential", + "messageName": "offer-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/2.0/offer-credential", + "messageId": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7" + } + }, + "a56d83c5-2427-4f06-9a90-585623cf854a": { + "value": { + "_tags": {}, + "metadata": { + "_internal/indyCredential": { + "schemaId": "A4CYPASJYRZRt98YWrac3H:2:AnotherSchema:5.12", + "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728266:TAG2222" + } + }, + "credentials": [], + "id": "a56d83c5-2427-4f06-9a90-585623cf854a", + "createdAt": "2023-03-18T18:53:59.859Z", + "state": "offer-sent", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "protocolVersion": "v2", + "credentialAttributes": [ + { + "name": "name", + "value": "John" + }, + { + "name": "age", + "value": "99" + }, + { + "name": "height", + "value": "180" + } + ], + "updatedAt": "2023-03-18T18:54:00.007Z" + }, + "id": "a56d83c5-2427-4f06-9a90-585623cf854a", + "type": "CredentialRecord", + "tags": { + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "state": "offer-sent", + "credentialIds": [] + } + }, + "598dbcc3-5272-4503-9c67-b0cb69a9d3d6": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "598dbcc3-5272-4503-9c67-b0cb69a9d3d6", + "createdAt": "2023-03-18T18:54:01.126Z", + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "role": "receiver", + "message": { + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "@id": "2a6a3dad-8838-489b-aeea-deef649b0dc1", + "requests~attach": [ + { + "@id": "libindy-cred-request-0", + "mime-type": "application/json", + "data": { + "base64": "eyJwcm92ZXJfZGlkIjoiY3F4ZW8ybzVIWmVjYWc3VHo5aTJXcTRqejQxTml4THJjZW5HV0s2QmJKVyIsImNyZWRfZGVmX2lkIjoiQTRDWVBBU0pZUlpSdDk4WVdyYWMzSDozOkNMOjcyODI2NTpUQUciLCJibGluZGVkX21zIjp7InUiOiI3OTE4NzUwNzgzMzQxMjU3ODU3OTUzMjc2OTU5MjcxOTcyMDQwMjQxMTU1MzcyODEwOTQ2NTEwMjk5MDA1ODEyMTcwNjkwMjIzMTQ2ODU0NDk1MTI1NjQ1MTg3ODQxNzk0NjA3OTUwOTQ1OTM3MDYxMjk1ODgwMjIxMzE2NTg1MTIyNDY1Mzk1MjAwNDQ3MDIwNzAxNDA0NjEwMDc4MzkyMjA4NzkxMjk5NzYwMzM4OTIxNDMzMDc1Njk2ODU0NTY3MTIzNjYxNTYwNDMwNDE3NzQwMzc5MzA4NDQzODcyOTU1NzAwNTk1MTg2NzcxODY3MzM5NzQ5NDgzODYxNTQ2NTE2MTU4NTM5MjkyNDQzNTQ3OTg3MDUwMzE4OTAyOTI5OTgyNzMzMDk1ODk4MDIyMjg2OTk1OTQwMjkzMTg3NTg5NDgwNTgwNTAwNjM0NzAyNjQxNDM0MTgxNjIwMTU4OTU3MzUyNTE1OTA4NDE2MjI4MDQ0NDA2OTU4MTk1MDg4Mjc0ODI4Njc3OTQxMDgxOTczOTg3NjU1MDEzNDUxMzA4ODQyMjYyMzY4MTQzOTExNjIxMTE0NzYyNTk3Nzg1MDczMTM4MDg3NTQ2MDIyMzc1NjQxODQ5ODI2OTg2MjYwMDE5NTAzNzE3OTk0NjM3MzIyNDExNTgzNTY0MTQ1NjcwMTM5OTQ1MjQxOTU2Njk2MDQ3MTQzNjA4NjQ0MDM5OTg2NTYwODUyMzA1MTczMjUxMTUyMDIzODI5NjI3MzQyODM2NDI3MjkwNDQ5NTA3OTY0Nzk4MTQ2NjkzOTUxMDkwNzUwOTAyNiIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6Ijk4MjIzNzMzMDcwMDk1NjM0MzU5MTE2MDEyOTgzMDcyMjc5MjcxMzk1MTg0NjA0NDcxNDQ5NzA1Nzg0MTEyNDAyODMwNzIyMTUxMzIxIiwidl9kYXNoX2NhcCI6IjU5ODA0MTY4ODAxODk1NDAzMjkwMzczMTA3NzA4MzUwODc1NjY4MDcyNDY3ODAyOTcyOTIyNjUzNDE5ODU2MjMyNTIzNDI4OTUxODQ4NjEyNDE1MjM5Nzk3Mjk5ODY2MzIxNjU5NDQ1MTM1NzQ4NzU2MDY1NjgyOTY5MjY4ODI5MTYyMDA0NjQ4NzYwMzg4NTg4NDkyNjg1NDI1MTg1MDU2OTAxOTkxMjcwMzYwMDk3MDc5NjEyNTYxMzY4NzU1OTcwMjY5MjI4MDYzMjMyODU0NzI0NDkyOTA5Njg5MDMyOTg4NjYyMjk5Mzg3MDU2NDEwNjU5MDU3MDUwNjE0MDQ2NzE1NjA0NTgyMzM2NTg4MjMxMjI3MTEzMDEzMDQxMTA0NTU2NzM1MDE3ODUwNzUzODcwNjc2ODYxNDA4MjA0NzkzMDIzNTYwMDEwNTEzODAwNzA4MjAyNjAyNjQ0Mjg2NzI4NjAyOTk5MzU5MDcwODQxMTQ5MTAzMjA5MzY0ODkyMzMzMDYwMDgzMTA5NDIzOTQ5NzE4NTk5NjEzMzk2NjIyMjc4MTExMzk5ODU0MTcyMjMyNTQzOTk1Njc5NDk3Mjk1Nzc1NzA0MjA0MTQxOTU2NDI1MDc4NjYzMzgwMDA1Nzc2ODY2MTcxNTY4OTU1NjE4NjAwMTA2NzkxMjIyNDkyODA2NzI1ODU1NDY2Nzk4OTEzMTc2NDcxMDY3MTk5ODQ2ODEwNDI5MDIzMDc3ODI3OTc1OTIzMDIzNjU3MTg0NjkwNzE0MjkxNDk0MDc5MTM1NzYyOTUxNTc0MjMzNjMwMjExNDQ1Njc3NzE1Mzg3Mzc1NjkyMjAzODE3NDczNDk5NDExNzE5MzIwMjczNzExOTIzMzM3MzYzMTAyOTkwMDcyMjE2MjYzMzUxMzMxNTk4ODk1OTU3MzU1MDc1NTEzODE0NTUwNzkyMCIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiNTY0MTY1NjA2NjM0MTUwODgzMjg2MjM1NDM1MjUyODQ3MjAxMTk4NTQyNDAxMjYzNTY2MDQ2MTA3OTU3NzI4NTQ0MTMwMjgzNjUyNjQzOTI0NDc2NTU2NTIzNzg0ODI1OTgyMzMwMzc4NDI4OTM0MDkxNDcwNDM0OTAwMTM3NzkwNDkxMjM4NTA4ODk2ODQxMzkwNjQ4MTA1NzY5ODYzMzI1OTAzODI1Mjk1MjU2OTQ0NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMzE1MDIwOTY0MDY4NjgyMDQ0Mzc4MjEifQ==" + } + } + ], + "~thread": { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841" + }, + "~service": { + "recipientKeys": ["cqxeo2o5HZecag7Tz9i2Wq4jz41NixLrcenGWK6BbJW"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3001" + } + }, + "updatedAt": "2023-03-18T18:54:01.126Z" + }, + "id": "598dbcc3-5272-4503-9c67-b0cb69a9d3d6", + "type": "DidCommMessageRecord", + "tags": { + "role": "receiver", + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolName": "issue-credential", + "messageName": "request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "messageId": "2a6a3dad-8838-489b-aeea-deef649b0dc1" + } + }, + "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf": { + "value": { + "metadata": {}, + "id": "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf", + "createdAt": "2023-03-18T18:54:01.192Z", + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "role": "sender", + "message": { + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "@id": "578fc144-1e01-418c-b564-1523eb1e95b8", + "credentials~attach": [ + { + "@id": "libindy-cred-0", + "mime-type": "application/json", + "data": { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJhZ2UiOnsicmF3IjoiOTkiLCJlbmNvZGVkIjoiOTkifSwibmFtZSI6eyJyYXciOiJKb2huIiwiZW5jb2RlZCI6Ijc2MzU1NzEzOTAzNTYxODY1ODY2NzQxMjkyOTg4NzQ2MTkxOTcyNTIzMDE1MDk4Nzg5NDU4MjQwMDc3NDc4ODI2NTEzMTE0NzQzMjU4In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjMyMTIwMDQ1ODc4MzIxMjcyMzA1ODI5MTc3NzMzMTIwNzE1OTY5NDEyNjkwNjUyNDQ4OTc0MTA4NzEzNjU0ODc3NTg2MzIzMTI3ODk2IiwiYSI6IjIyMjY0MTYwNjIwODcwNDUyNTExMjcyMzE1MzMzMDA0MjQzMzY3NTM2NzM3NDMwNjM1NjExMjcwMDkwOTE4NDMwNzc0ODEzMjAzNjQwNjMxMjIyNDczMzk0MjQ4MTgzMDIzMjIyNzExNDUwMzQxMDcxOTQyNDQwMDgwMjY2Nzk1Mzg5Mzg5Njc1NjYwOTUzNTQyMDE4OTA3NjQ3NzI4OTQ4NjY1MzA2Njg0NjExNDU1NTI5NzM5OTY1NDcyMjQ2NDQxMzE1NzAxMzM1ODc1MDY3MjExMDk3NzcyOTgwMjU1NDIxMDMzMTI1MjAyMTQzNDk3NjMyOTAyMjM1NDAyMzU5OTA1MzY5MzE4MjI1NTc4MjUxNjY4NTYzNzc1NTY0MDM2MjUxNzE0Mzk3MTEzNjQ3OTg0MjcxMTE5MTU2NDQ3NjI1OTk1NjE5MjAwMDk4MTgzNzY1NjkzMTg1ODEzNjA1NDU3OTQwMzE0MDU2MDkzMTI2MzQ3OTU5MzYwODIyMzg0OTEzODg3Mjg3ODI2NjkyNDIyNDMyNDUwMDA5OTYxNjQ2MjMzNTE3MjY3NDU1OTkyMjA3MTE3Mzk5NzU1NjY3MTA3MzM1NTQ0MzEwNDQwNDE1NDE5NTk5NTA1OTgxMzkwMjk5NDUxNzQyODg4NDg0MTc0NTU5MDA5NDgwNjU5MDk2Nzg2ODI2MDgxNzc3MzcwNTk1MTU3OTg5NjQ1MDYxNDI2OTA2ODM2MDk5NTU5MDQ0MDI4ODM2MzYwOTM2MDkwOTkxNjA1OTU0NTM2OTQxMjgxODQwNzk2MjkxODc0ODk2NDEzNTM5IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDExMzE1MTE0OTUxODg0MDQ5ODkyNjAwNTY3NTgzMTc4ODkwMyIsInYiOiI2NTE5ODc4MzYyODQ5NzExNDY4NDkyNDM1OTM3MDU4NzMzOTYxMTkxNjA3NjI4MzUzNjkxMDg1MzM5MDMwNDU0OTkyODc0ODYyODkyNDg4ODYzNTA3MDQ1MTM1MzA4ODI1NDA2NzYwMTQwNDQzNzM0NDYzODE5NTM2MzE0NzcxMTQ3MDk4MjU2ODMzNTc2MjIwNDI5ODQyNjc3NzMwMzQwODYwNjE2NTcxNzc5NjU4OTIxNDY4Mjc0NTUwOTc5NjYyMDkxNzEwNDU5MDk2MDgzMzYzNTc1Mjc0MjQzNzIyMzIzOTIxMjY5MDYyMjE0NjQyNzQyMTI0MzQ4MTY0MDUxNzE3MTk5MTkzODY3NTM3NTEzNjYzOTY1ODQzMDI5MjAxODA0OTE2MTEzNzMxODYzOTUzNjQ5MDkwNDgzNzMyMTkxNTQ2MTEwMjAxNTg0NzMxODg4NTE5NjA2MjE1OTkyNTgxNzk2MDg2NzUzOTE5NzUxMjkwMDI3MDI4NzczMTAwOTc5ODI5MzQ5NzA0MTUyMDEzNjg2MzU1MzM1MjIyNjU5MDY2NzE0NDQ2NDc4NzY3MTE5NDE4MjY3OTg5NTAyNzc4MjMzNzM3MjM4MjU1MTQxNzQyMjk4NTU3MDY2NzA2MTM0NzYwMjQwMzY3OTMzMzc5NzYzMTc5MTI1NTI4MDQwMzkxNjQwNTIyNTM5NjE5NTU0NTE0NTk4OTUxNTg0NjA3MjYwNzk1NzE1MDMyMjM4NTQ3ODMyMzA0NTY2MzQ4NjYzMTc0NzQwMDE2MzQ2NTU2MTM1ODc4MzgxNTYzODQ2NzU0MzQzMjk0NTIzNjc0NDI3NjQxNjAxNjAwNjE2NzI3NjEyMzc0MzI2NzY4ODA5NjAyNTE5MTAzOTk3NDY4OTg1NTg3Nzg4MjI3Njc5MzQ4NTgwNzk1OTkyOTkxNzMzMDg5MTUyMTg2MDg4OTU2MTg2MTQ0OTkyMDI5OTI2OTUxOTU0OTQyNjYwMDUxOTM0MDc5NzkxODI1NzA2MTExNzg0MDU2NDM2OTA2MDgxMDQ2MDQ5ODI0ODE1NDE0MTc5NTMzMDA2ODE4NzQ3NzgwNTQ5In0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjEwMTUyMDI0OTk1MTUxMzcyOTgwNzI5NDM1MTQ5MzgyMzQxMDQ3MDIzNjI2NjA4MDc3ODg1NzQ2Mjg4MTE3MjI2OTAyNzM4OTY5OTEwMTU0NjgzNzM2MzI4MzQ2MDg0MjcwMDA3MTY2NTg3ODI5MjE2MzEzNDc4MDk3Njc0MTI0ODU2ODIyMjA4NzI0Nzk1NTE0ODgzOTYwMzE5NDc5OTg4NzAzNDUyNjI4NjYxMDc3MTg3OTIyMzA1NDc5MDE2NzQzOTk0NzYwMzE5NzI1OTExODk0MjM2NDMxMDkxMTIyNTUxNTU0NzgwODg0NjQ2MjE0MTUzMDUzMTM2NDMwMTk4OTA5MTM0OTk4OTM2NjY3MzI4ODI2MDcwNzEzMzk0NDg0NDI0ODUxNjkxMzUxNDc0NjAxMjIwODk2NTIyMDYzNDA5NzA4NDA1Njk2MzY5MjA0MzU0NzE1MDkxMzk2Mzc4Mzc3MzA0ODk3MzMwOTM0Mjc2NTQyNjE2NjAxNTk1ODI5NzgxOTg3NTMyNzkxMzIyNTgzOTE1Njk1OTY2MjM3MTc4Njg1NTMzNTE3MTQxNTAyNDE3MzQxMDIzMTA1MTczMjMwMTcwNzUzODYwMjgxNDAxODk4MDE5OTQwNjA2MzczOTYwMzYxNjA3NTE2NjgyMDg4MTc1NzU4ODA0Mzg4MTM5MTQ0MDkwMjg5MzI5NzMzNTQ1NDg4MjUyNjczNDIyODkzMzc1MzE5ODQ2OTMwOTIyNjIwNzAzMTEwMDgwODU5OTE4ODQ0MzgyOTQ3ODczMjAwNzA4MTY2MzA0NDk4ODk0MDA4NTMyIiwiYyI6IjIyNDQyNTM5MzYwMzYzNjQyODI1ODkxNTc5ODgzMDE5Mjc3Mjk0NTQ2MjUwMDEzNTM3MzI2OTY2NzM3MzE0NTUxMjEwMjU3MjU2NDU5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9" + } + } + ], + "~thread": { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841" + }, + "~please_ack": { + "on": ["RECEIPT"] + }, + "~service": { + "recipientKeys": ["GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW"], + "routingKeys": [], + "serviceEndpoint": "http://localhost:3000" + } + }, + "updatedAt": "2023-03-18T18:54:01.192Z" + }, + "id": "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf", + "type": "DidCommMessageRecord", + "tags": { + "role": "sender", + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "protocolName": "issue-credential", + "messageName": "issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "messageId": "578fc144-1e01-418c-b564-1523eb1e95b8" + } + } +} diff --git a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap new file mode 100644 index 0000000000..2bffe080ab --- /dev/null +++ b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap @@ -0,0 +1,812 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the credential exchange records for holders 1`] = ` +Object { + "1-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "isDefault": true, + "linkSecretId": "Wallet: 0.3 Update AnonCreds - Holder", + }, + "type": "AnonCredsLinkSecretRecord", + "value": Object { + "_tags": Object { + "isDefault": true, + }, + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "linkSecretId": "Wallet: 0.3 Update AnonCreds - Holder", + "metadata": Object {}, + "updatedAt": "2023-03-19T22:50:20.522Z", + "value": undefined, + }, + }, + "2c250bf3-da8b-46ac-999d-509e4e6daafa": Object { + "id": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "tags": Object { + "connectionId": undefined, + "credentialIds": Array [ + "f54d231b-ef4f-4da5-adad-b10a1edaeb18", + ], + "state": "done", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "CredentialRecord", + "value": Object { + "createdAt": "2023-03-18T18:54:00.023Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "John", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "99", + }, + ], + "credentials": Array [ + Object { + "credentialRecordId": "f54d231b-ef4f-4da5-adad-b10a1edaeb18", + "credentialRecordType": "anoncreds", + }, + ], + "id": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "metadata": Object { + "_anoncreds/credential": Object { + "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG", + "schemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0", + }, + "_anoncreds/credentialRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "6088566065720309491695644944398283228337587174153857313170975821102428665682789111613194763354086540665993822078019981371868225077833338619179176775427438467982451441607103798898879602785159234518625137830139620180247716943526165654371269235270542103763086097868993123576876140373079243750364373248313759006451117374448224809216784667062369066076812328680472952148248732117690061334364498707450807760707599232005951883007442927332478453073050250159545354197772368724822531644722135760544102661829321297308144745035201971564171469931191452967102169235498946760810509797149446495254099095221645804379785022515460071863075055785600423275733199", + "vr_prime": null, + }, + "master_secret_name": "walletId28c602347-3f6e-429f-93cd-d5aa7856ef3f", + "nonce": "131502096406868204437821", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "669093c0-b1f6-437a-b285-9cef598bb748": Object { + "id": "669093c0-b1f6-437a-b285-9cef598bb748", + "tags": Object { + "associatedRecordId": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "messageId": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/offer-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "createdAt": "2023-03-18T18:54:01.134Z", + "id": "669093c0-b1f6-437a-b285-9cef598bb748", + "message": Object { + "@id": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", + "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "John", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "99", + }, + Object { + "mime-type": "text/plain", + "name": "height", + "value": "180", + }, + ], + }, + "formats": Array [ + Object { + "attach_id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "format": "hlindy/cred-abstract@v2.0", + }, + ], + "offers~attach": Array [ + Object { + "@id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6QW5vdGhlclNjaGVtYTo1LjEyIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY2OlRBRzIyMjIiLCJrZXlfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjQwMTQ0MTA0NDg3MjM0NDU2MTc1NzYwMDc1NzMxNjUyNjg1MTk0MjE5MTk5NDk3NDczNTM4NjU4ODM3OTIyODMzNTEzMDg0Nzk2MDQ5IiwieHpfY2FwIjoiMzgxOTQyMjM1Mzc3MzYwODEyNjY0MDA4MjYzNDQxMDg2MDMwOTMyMjAxNzgzMjM3ODQxODQ5NDg3ODk2ODg1MTYwODY2MTY1MDM3NzI2MTIxNjU0MjcwOTg5NDY3NjAzNDExOTAzODk4MzUwMDAzNDIwODg3MzI4NTUwMTY2MTI1ODMyMjAxOTQzMTkwNzAxMDU4NTAwMDE5ODM1NjA1ODczNDYzOTkwODg3NzQ0NjY3MzU0MjM2Njc3MzcyODg0ODQyNjE5NTEwMTUwOTA2MjI1OTMzMjc1ODEyNjg2NDg3NTg5NjY3ODI3MjAwODcwOTQ0OTIyMjk5MzI3OTI4MDQ1MTk1OTIwMDI3NTc0MDQwNDA4ODU5MzAwMzY1MDYwODc3Nzg2ODkwOTE1MDU5NTA2ODc1OTI0NzE2OTI1MDM2MTc4Njg2NDE5NTYyMzcwODI4MTMzODY2Nzg3NzkyMDcwNjAyNDQzNTkzMTk2NzEzNzcyNDM2NTYzODI0MzkwMDIyNzg4MjU2MzA4NjU4OTc0OTEzMTk1ODYxODUwMTQ3ODE1Mjg5NzQwOTA4NDk1MjQ3NTAyNjYyNDc3NzQ2NTI5ODA3Mzg0OTgxODI5MDc3NTQ4OTI2NzExMDkzNzQ5MjM1ODU4NjUwNDc5NzE5NDI4MzUwMzAwNzUyNjQ0OTg1MTQ5MTMxNjA1NjUzMDIxMDYxNzkwMjY3MzQyNTY4NTkyNTY2MTQ0MDM5NzY4OTg0NTMyNDMzNzk0MzUzNjQ2Nzg1MjA3NDgzOTk2ODQ0OTcxNTgzNzY3NDQ5ODYyODgxMjMxMjI1MzM4MzAzMTQ4NzA0ODczMDEzNDM3MDgyNzY1MTk4OTY2MzE5NTM0OTkyNjk4MzMzMDQ0MDI3MjIyNTYyNTIzNzk3ODk5Mjk2MTQ1NDU5IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiOTE5MTc5NzQ4MTE5NTg5MTY3Njc5MjQxODk5NzY0ODIwNTk0MDc5OTQxNzIzOTgzOTYyNzQ1MTczODM0NDQxMDQ3MjU4MDcyOTE4OTUzNjIzOTQ4MDMyNzI2NDgyNzI2MTEwOTk2Mjk3MDU3NTYwNjcwNzAxOTU1MTkxNDc0NjM0MzQ0ODMxMzg3NTk4NzI2MzMxMjc0NjI4NDU3Njk5NzczMDA1NDMwMDIxNzMwMzg4MzcwMTEyMjc3MzI2MzU4OTgwMTA3ODIzNzUzODc3MTU0NjIwMDkzMjE5MjYyNjAxNDM2NzMyNTgzNDI4Nzc4NDA4OTc0NTQyNzkzMDk0NTQ5MTczOTA3MzQ3OTUxNTc1NjM5NzU2NDg5MTA0Mzk0MTY3NzExMzY1MjM3OTI1MjAwNjk4OTg5NTI5MTQ3OTIzNTYzNDMyODgyMzgwMTg0NzU0NzkzODMwMTE3MTQ1MDAwMTI0NDYxNjkzOTcxMDQ5MjgzNDk1NTE4MDQxMDc5ODUyMzAwMjk0NDM1MjYzOTIwNDU0NTU3MzUxNDQ1MDM3NDI4MDg3OTk2Mzg2NjY3NjU3Nzk5OTYyNzQzNzIyNzA3NzczOTEzMzc0NzIxODUyNTQ3MjkwMTY5MjI5NTAzMTQxOTMwODYzNTk4NTExNjc4NDEyMDE0MzE2MDM2MzYxMzczNDcwOTQwMDEyODcwMDgwMDA2MzE0NzYxNzYzNzUyNzYwODk5MTQ3NzA1MTA0NzQyNjAxNjkxMzMxNjkzMDIwMjg2MjA2NzQ2NzE0MzI3NjU2MjA2NTMzMjk3NDg4MjU2NTM2NTQ3MzY4MjM2OTQ2MDM5NzAzMzc0OTMzNTE0NTc2NDg2NjQyNTY4MjgyNTY2MjMyNDU1NTU5MDY4MzE3NzU5NDM0ODU4NTI3MDg2NjQ0Il0sWyJoZWlnaHQiLCI5MjMwMzkyNDc1NjI4ODc1MjA4OTM0NjM0NzE4MjYzNzA4MDIzOTI1MDU0NjY2NDgzMzgxMzIyMzc3MDg1MjMxMjU4MTM4MzgwOTU1NTk3NDQxNTEyOTYwNDA2MjI3MjUwODgyNjA3NjExMDkwODk3MTM1NDcxNzAwMDIzNDcwOTM2ODg4MDE3NDY5Nzk0ODYzNDk4NzUyNTI3Njc3MjMwMTEwNzg0ODQzNzI0NDUyNTUzODYyOTA2MzM5MDc0OTIzNDU4NTQ3NDYzODcwNzU3OTg5MzMxNzk4OTI2MjM4MjUxMTM2NTYzNjM2MjIyOTQwNDkwMzY3MjQ2OTg0OTU2NTE5MTAzODcwNDE0MDM5NzM2MDE2MDY5MzA2NjQ0NjQzODI4OTgxMTE3OTM3NzYyNDAzODY1Mjc1MDU5MjEyOTY2NzIxOTU3MzM0MTM2ODEyMDI0OTE0MzA4MzAxMzk5MzM4NzMyOTIzNTA0MjA5MDM5ODMxMTc5NjU1NTkyNjg0MjMyMTIzMTI2Mjc4ODQzNDMyOTUwMTk1Mjg3MzE4ODI3NTM2MTMwNDQ3NzM3MTgwMjk3MDE0ODEzNDg3NDQyOTg2NjQ1NzQyNjEyMzE5NzQxNDY2MDMyNTg5OTU0NzYwNjE4MDU0MDUxMjAzMTE1NTAxNDcxNDExMzg3NzU0NDk5MzAwNTU4MTc5NjM5NDAxOTM0NTAzMTMyMDEzMjAzOTg2NzkyMTEzMDAzNTkwODg1NTc3NjgyMzU2NDY3MjA5NTUwNjQxODQxMDYyNTkzNDYyODIwODg3NzgxNDYyODM3ODkzODcxNDM4MzM3Mjc5MTcwMTExMTQ5MTU4NDMzNDE0ODI1NTkyNjcyODU2MzM5OTM4NTgyODg2NzM3OTIwMjc1MzI0MjEwMTUzMjE5MjI2OTYiXSxbImFnZSIsIjkxNTg1ODk3NDkwNzE0ODA3OTY2MDYzOTg5MjE1NTMxNDkyOTQwMDI5NDcyMTM4MjgwNjcxNjcyMjQ0NjY5MDc5NzIyNTQyMDU0NTU3NjY0MTcxMDI1NzM1NjQ4NTIwMTM4ODQ4ODAxNzIyMTc4MTcxMTA5NTc0MTMyNTExMzM1MDEwNTc5NzExMzcyODM5MjI3MDExOTg4MTUyMTEwMzI4MTE5MjkyMjI4NjM3MDU4MDQ3NzYwODYwOTQ0NTY3MzQxMjY4MTY4Mjk3NjE5MDM2ODEwMjYwODM2NDI1NDkwMzU3NjE4NzM4NTYxNTY2MTUxODQ3MzIxNzM1MjQ5ODk1MDU5NTY2OTQxODI5MjE0Nzc0MTA0NzYyNTQwMjcyMjk2NjE1NTE3NjUwMDcyNDQyMTI0NjY5MDEzMTc1ODAyMDk5MDQxMzk3MzE5ODQ0OTA2MDgwOTYxNTcyMTcwNjg2NzgzNDM1Mjg2MDUyMzE5ODY3ODExMDE5MjAxMDYwODM2OTM3Mzc0MzM0NDM5MTQxMDAzMTI3NTcyNjgzNTgwODI0OTkwOTg3MjE5MzU4NzkzOTM2NTU4Nzk3MjI0MDQzNTM1ODA5NzMyNzgxMjE1NzEwNjI1MjQzODYwNTk4MTk0MjU2MjAwODkwOTA3ODAzMDcyMTAzNzc3MzkwODk4MDczOTgyNjY3Njc1ODg0MjI3MjU0Mzc2OTI5Mjg3ODQyNDE0MTE0MjcwNDQwMTEzNDUxNjk4NzE5Nzc5NjQyNTI4MDA4NDM3Mzk5NjI0NTE3OTM4Nzg5MDc3ODE5ODA0MDY5MzcxOTM0NzExMTIyNTQyODU0OTg4MDA0Mjc4NDkwMjAxNTk2NjE0MjUwODc3NDYxMDczNjc3NTUzNzYxMTMyMTA5Nzg3NTQ2ODE1ODk5Njc2NCJdLFsibmFtZSIsIjYyNzgwNTIwMTM3MzI3NTUzMDc3MDg4NTE4NDg1NDYyMTA0NjEzMjEyNzY3ODUwMzYwNTc3NDQ4MDUxNTk5MTMxMTM1NTI2NzQ3Nzc2NzMzMDg1MDMwODcyMDE1OTM2MTI2NzE0MTIxMDgxMzg3ODU2MTkwMTkzMzI3ODY3OTE0NTEzODM2NTQ1OTY4Mjg1NTc5ODEyODMxMDI4ODc2Nzg1NzI3OTQ2MTEwNzg5Mzc0MjcyODgzMzkyOTgwNDkwODk3NDkwMTc5MDQ0ODM0NTgwMzQ2ODY4NDI2ODc0ODU4NTY1OTg4NTUyMDcwNjI1NDczNjM4MDM3Njc5NTU1NTk2MzE5MTc3Nzc5OTcxMTIxMjQzMjgyMTIyOTQ2NjY0ODMxOTgxMTg3MzQ3MzcyMjkxMjYwOTM3MzkzNDA1ODk5OTI0NjM4MzE3ODI5MDczODMxMjI4ODc1Njg5MTcyMTg4NjIyMDI5NzcxNzM5MTQ5NDY2Mzg3NTM5NjkyNDQ5NDU5MjczNDI5NzM5MjMzNjkyNTEzNDA5OTkyNDgxNTQ4ODk0NjAzNjM3MTYzNjA4MTM0MTAzMTk3Nzc3NTM4OTYwMDcyMjcyMzYyNzM4NDM1MTM3MDcyNzIxMjExMDYxNTg4MDE3ODczODg3MTEwNDA2OTk1NDQ4ODIwMDEzMDA5MjgyMzk0OTczMDMwMDI5MTY3NjQ5NzY1OTI1MTUxMzY4NTg5OTkyNzMyMDE1ODAwNjAzNzYxOTI3MTg3MDM4MDkxNDY3MDE1MjA3MzIwNDczMDM0NDA3MDIyNDA0NjQ4MTI0NTk2NjQwNjU1NjY1MTIzMzY5Njc0ODI2NDE3MjE2ODUxNTM4Njc1NTM3NzAwOTg4MTQzNzE1NTE3NzMwMTM4NjA4NzkxMjcyMzM0MDUyMzY4OCJdXX0sIm5vbmNlIjoiNDE0MzQ4Njg0NDk2OTAxNjkyMjI2OTY0In0=", + }, + "mime-type": "application/json", + }, + ], + "~service": Object { + "recipientKeys": Array [ + "DXubCT3ahg6N7aASVFVei1GNUTecne8m3iRWjVNiAw31", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3000", + }, + "~thread": Object { + "thid": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + }, + }, + "metadata": Object {}, + "role": "receiver", + "updatedAt": "2023-03-18T18:54:01.134Z", + }, + }, + "8788182f-1397-4265-9cea-10831b55f2df": Object { + "id": "8788182f-1397-4265-9cea-10831b55f2df", + "tags": Object { + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "messageId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "createdAt": "2023-03-18T18:54:00.025Z", + "id": "8788182f-1397-4265-9cea-10831b55f2df", + "message": Object { + "@id": "c5fc78be-b355-4411-86f3-3d97482b9841", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "John", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "99", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiODUxODAxMDMyNzEzNDg5NzYxOTg5MzAzNjMzMDkzOTEyOTExMDUxNjI0OTQ0OTYzMTgzNzM2MDY3NDkwOTc2MDYxODEwMDgxODkxMzQiLCJ4el9jYXAiOiI4NDk0NDg4MjQzNTk2NTkwOTc2MjQzMjc0NDg4ODk2Mjc1NTcyODAyMTQ1ODE5NDQzNTE0NzQxMzk1NDI1NjM5MzQwMTczMDIzMTQ5NzI3MDY5NzMzMzQwODgzMTU4MzQ1NTYzOTA5OTcxNDMzNTg1MjMwODAxNTYyMTM0NjczNjM1ODg5NTA3Njg5ODQwOTgyODU5Mzg1NjA1MTc1NTkxNDYxOTkyMDExNzU2Mzg1MTI3MTQ3ODgxNDMwODEzNjYxNzY0MDU5MzE0ODk4MTc2NzQzMTQ5MjYzMDMwMDQ1NzMwMDMzMzI2NzgyMzg1OTY0NjcxMzg1ODQ2MzcxNjQ5MzQxMTg2MDM5NjE4MTQwOTIwMDUxMzg1MDAwNTYxMDcyMTc5NTEyMzc5Nzk0OTU4NjE1ODIyODI2OTExNzIwNTQyNTE0MTQ1NDc5MTAxOTUyMzM4MDMwMDY1MDk5NjcxOTU2OTMxMzE2NjE5MjM0NTQ0NTE5NTQ1ODQ1MzA4MzgxMjQyNTM0NDcyOTc3NjY0MjAwMjc2MTMyOTgxODE1ODAzNTIxOTExMzk4ODkxMjE0NjE1NzA1MDM2ODM2ODU1NDU1NzY4ODg4MTUxNDgzODAyNDcyODQyMzczNzE0MTI0NTYwMzIyNTI3NDE4MTEwNzYyMjgyNzY4NzMyNTIzMDQyMDA3MDY2OTk2ODIxMTQwMzE1NDg0NzI4NTM2NzIwNDI3MDg5MTI2NDk1NTAzMjc0ODQ4MDM3MjUzOTM3NjI3MDU2ODUzMTQ4NjE5NDA4NDYxOTI5NzEzMjM4MjEwNDc4MjcyMTIxNTUwNjQzODc4ODM1NDYwMzY1OTIwMjE3NTk5NDYyNDUzMDMyNDQ4MjYyMTM3NjE5ODY0OTU4MzA1MDE3MjA4OTYwNDc1MTQxODgwMTMiLCJ4cl9jYXAiOltbIm5hbWUiLCI1MDcyNzU2NDE2NDA2ODIxNzU1OTc0MzUxMTg0NjE1NjA4NDY2NTk3Mzk0NzA2MTY1NDg2ODAzMjc3MjMyMzQyOTk4MDA0MzY0OTU0MTczMzc0NDIwOTc5NTkwMDcyODgxNDgxNDA0MTg2OTExODg5NzQ4MTgzMzQ1OTk5NzQ0NzgxMTQ1MTMwNzEyNDIzODY0Nzc1MzQzNjAzNTk2NDM3Mzg4OTgzNTExNDAzODA0NjEyNjU1MDE5NzQ4MTI5NDk3ODY2NTcwMDQyMjcwNDQxNDQ5MjYwODY0NzgyMzI5MjAxNDEzMTc5ODU3NzA0MjM5OTMyMTg4NTc4NzE3MDczNzM3NjUyNzY5MzY5NDg4OTgxNzg2NDQwNTExODAzMjMzNDMxNzA4NDk4MTU2NTA0OTUzNzkzNjU2NjQ2NzMyNTU4MzQwNDI2MDI1MjA3NTk0OTIwMDY4OTc2OTQ4Nzg2OTUxNzM3MDIwNDQ0NTA5NzYyMDQ2MzIzNzA0MDQ3MjU1ODU3NDE5ODE3MDc5NTI3NDgzNTE1NDY2NTAyMDkzOTY1NDMzMzk3MjQ1MzA4MjQ5MDgyMTQ4Mjc4NDA1MzI5Njg1Mjc0MDYwNjk0MzI0MTI2ODgxMjkyMDIyMjY1ODczMjk5MDU0NDU1OTA5NzkyNjUwNjAyMTk0NjUzMjYxMDk0ODYwOTc2NzA4ODE1ODgwMjExMTY0MTkwMDM0NjY0MzI2MDc3NjcwNzkyMDE4NTE2MzMzNDI3NjkwODYwMjIxODEwMzk5MDgxMjc5NjAwNTYzMjk3MjI0NjM0MDM0NjcxNTIwODE5MzU3NzQ0Njk2NzU1Njg1NDI2NjIzMzAwMjQ3MDUwODE4NTQ2MDM2NjA0NjMxNjcyNzE5MjI0NDA4NTE2NDM4NTgxMDM5Njk4NzI0MSJdLFsibWFzdGVyX3NlY3JldCIsIjU2MzYzNTgyMDQ5Mjg4OTY1OTg1MDA4NzgyMzU0NjgyNjMwNDkxMzQ3MTM1NDIxNTAyMDEyMTIwMzI4MDI4ODIyMjUyMzg4NjgwNTMwNTgxMTcwMTgwNDU1MTcyNTc3ODkyMTEyMTY1OTM0Mjk5NjUyNzAxNDExMzUyNDkzMzkyODU0ODI4NzMyMDQzMDI0MDI0MzM0MzMzNzc0NjEyOTEzOTUyMjAzNjM1NDk2MDQ0ODMzMDI5NDE2NjUwOTU5NjE0ODgzNTUwOTMxNzgzNTA5MzE1Nzg4MDEyODQ0MzAwMDQwMDE5MTY5MTc3NTI1OTgxMTU3OTkwNjQzMDcyMjQyNzcxMjU0MTYyNzMxOTU4NzI2Nzc1NjYwMjkxODIzMDcyNDk1Mzg0NzM5MTcwODc4ODMxNzkxMjQzMjEzMjU5MzA5ODQxNjU3MjUwOTg1NzMxMjEyNzE2MDM2MDY3MDUxNjM2NzA0MjA1NDEzMDk2MDU3MTA2NTM2MTI2ODUyNDU0NzcwMzQzMTMwMTczMjAwNjEzMDIxOTE4MzgzMDQxOTU4MTkwOTE2NzQ0NjU4NTI0ODA1NjM4Mzk2OTY3OTA3MzIwNjY1MDU1MzcwMjY0NjAxMDczMjc5NDMyNjM5MjM3Njc1NTA0OTg1NzQyNTI4NjYwMTAyMDEzNzIxMzA2MTE4MTg0NDk1MTEyNDQ2NDYyNDc2NTkwMjYxODkxMjA0OTQxOTA4MjMyNzMzNDA3MTg4MDA3NzE2NTA2OTUzMDY0Nzc5NDk5ODExNzI0ODI5NjcyNjY2NzIyNjIzOTAxMTc1OTk0NTIyNjkwMjk1ODI0MDgyNzY5NjQ0NDYxOTAxMDk2NzI3MTE5NzAzMjUzNzI4NjY3MTU1MzA5MDYzNDUyNDY2MDY3NzU5NzIwOTgyNDA3MiJdLFsiYWdlIiwiMTM2NTQxMjE0MjM5MTcyNDQxNzQ1MjU3MjcyMDI3MTA4NDYwMzU0MjgxMTA2OTA2MzYwNDIwMDE0NjUyMDIxMDgyNDEzODM2ODEyMjk3NjY3ODk2MTYzNDkzMjM4NDIxNDI4NjMyNTMxODE0ODk4NzkwMDg4OTg2NjgyMTE2OTAyMzc4NDgwNTE4OTUxNDExNzg1OTk3NTk5MDMyNDYxNjExNjIyMDUyNjMzMDQ5ODYxMzc5MTQzNzI4MTM5MTUyMDkyMzI0ODc3MjMxMTYwNTgzNzA5NjE0MzA1NzQ1MjA5MjQwNjU2MDU4NjY3OTMwODEzNzYyNDY5MDc2ODc5MTk1Nzg0Nzg4NTE2NjI3MjgxMDY0NjE3MzgzMDc4Njc5MTkwODIwMzQwNTgwNDY2MjU3ODU3NjA1MTc2MTg4NTI3OTMxMDI4MTMzNTY5Njc0Mzg2ODAwMTA2MDE2MDg1Nzc0OTcyMzI1NTAyNDA2MTY0OTY0MjU2OTUxNDI3ODAxMTQzNTQxMzUzMzI0Nzg0MzA5OTY4MjIyOTU1NDk4Njk3NTAwMDUwMzc0MDg0NjIwNzQ4MTk0NzIyMTI2NjE2OTY3OTY3Mzc1NTM3Nzc5NTc4NTMwMDIxODExNTA2MTIxNjcxMDUwNDgzNTM2MjA3Njc3MTg5NDQwNjEwNzk0NTcyNzI5MTgzMzAyMjM1MDkxMDg4NTU2ODc5NTg3OTE3MDMzMzQyODcyMzg2NDQ5MTQ0NzgwMDYyNjc4MzA3NzE4MzU1MjQ5MTUxNjc5MDA1MzkxNDA5NDE4OTQxMjEzNDkxMjQyMjg2NTAwODcyMzQxNDI3Nzk1MjQ1ODYzODE2MDY2NDY3NDkxOTg4OTU3MDEwNDIxNDA3NDkyMDUxOTc0NTMwNjIxOTk1ODU0ODczNTM5Mjk3MyJdXX0sIm5vbmNlIjoiNzk3NjAzMjE3NzA5MzM1MzAwMTcwODI4In0=", + }, + "mime-type": "application/json", + }, + ], + "~service": Object { + "recipientKeys": Array [ + "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3000", + }, + }, + "metadata": Object {}, + "role": "receiver", + "updatedAt": "2023-03-18T18:54:00.025Z", + }, + }, + "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3": Object { + "id": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "tags": Object { + "connectionId": undefined, + "credentialIds": Array [], + "state": "offer-received", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + }, + "type": "CredentialRecord", + "value": Object { + "createdAt": "2023-03-18T18:54:01.133Z", + "credentials": Array [], + "id": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", + "metadata": Object {}, + "protocolVersion": "v2", + "state": "offer-received", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2023-03-18T18:53:44.041Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.4", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3": Object { + "id": "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3", + "tags": Object { + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "messageId": "578fc144-1e01-418c-b564-1523eb1e95b8", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "createdAt": "2023-03-18T18:54:01.369Z", + "id": "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3", + "message": Object { + "@id": "578fc144-1e01-418c-b564-1523eb1e95b8", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJhZ2UiOnsicmF3IjoiOTkiLCJlbmNvZGVkIjoiOTkifSwibmFtZSI6eyJyYXciOiJKb2huIiwiZW5jb2RlZCI6Ijc2MzU1NzEzOTAzNTYxODY1ODY2NzQxMjkyOTg4NzQ2MTkxOTcyNTIzMDE1MDk4Nzg5NDU4MjQwMDc3NDc4ODI2NTEzMTE0NzQzMjU4In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjMyMTIwMDQ1ODc4MzIxMjcyMzA1ODI5MTc3NzMzMTIwNzE1OTY5NDEyNjkwNjUyNDQ4OTc0MTA4NzEzNjU0ODc3NTg2MzIzMTI3ODk2IiwiYSI6IjIyMjY0MTYwNjIwODcwNDUyNTExMjcyMzE1MzMzMDA0MjQzMzY3NTM2NzM3NDMwNjM1NjExMjcwMDkwOTE4NDMwNzc0ODEzMjAzNjQwNjMxMjIyNDczMzk0MjQ4MTgzMDIzMjIyNzExNDUwMzQxMDcxOTQyNDQwMDgwMjY2Nzk1Mzg5Mzg5Njc1NjYwOTUzNTQyMDE4OTA3NjQ3NzI4OTQ4NjY1MzA2Njg0NjExNDU1NTI5NzM5OTY1NDcyMjQ2NDQxMzE1NzAxMzM1ODc1MDY3MjExMDk3NzcyOTgwMjU1NDIxMDMzMTI1MjAyMTQzNDk3NjMyOTAyMjM1NDAyMzU5OTA1MzY5MzE4MjI1NTc4MjUxNjY4NTYzNzc1NTY0MDM2MjUxNzE0Mzk3MTEzNjQ3OTg0MjcxMTE5MTU2NDQ3NjI1OTk1NjE5MjAwMDk4MTgzNzY1NjkzMTg1ODEzNjA1NDU3OTQwMzE0MDU2MDkzMTI2MzQ3OTU5MzYwODIyMzg0OTEzODg3Mjg3ODI2NjkyNDIyNDMyNDUwMDA5OTYxNjQ2MjMzNTE3MjY3NDU1OTkyMjA3MTE3Mzk5NzU1NjY3MTA3MzM1NTQ0MzEwNDQwNDE1NDE5NTk5NTA1OTgxMzkwMjk5NDUxNzQyODg4NDg0MTc0NTU5MDA5NDgwNjU5MDk2Nzg2ODI2MDgxNzc3MzcwNTk1MTU3OTg5NjQ1MDYxNDI2OTA2ODM2MDk5NTU5MDQ0MDI4ODM2MzYwOTM2MDkwOTkxNjA1OTU0NTM2OTQxMjgxODQwNzk2MjkxODc0ODk2NDEzNTM5IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDExMzE1MTE0OTUxODg0MDQ5ODkyNjAwNTY3NTgzMTc4ODkwMyIsInYiOiI2NTE5ODc4MzYyODQ5NzExNDY4NDkyNDM1OTM3MDU4NzMzOTYxMTkxNjA3NjI4MzUzNjkxMDg1MzM5MDMwNDU0OTkyODc0ODYyODkyNDg4ODYzNTA3MDQ1MTM1MzA4ODI1NDA2NzYwMTQwNDQzNzM0NDYzODE5NTM2MzE0NzcxMTQ3MDk4MjU2ODMzNTc2MjIwNDI5ODQyNjc3NzMwMzQwODYwNjE2NTcxNzc5NjU4OTIxNDY4Mjc0NTUwOTc5NjYyMDkxNzEwNDU5MDk2MDgzMzYzNTc1Mjc0MjQzNzIyMzIzOTIxMjY5MDYyMjE0NjQyNzQyMTI0MzQ4MTY0MDUxNzE3MTk5MTkzODY3NTM3NTEzNjYzOTY1ODQzMDI5MjAxODA0OTE2MTEzNzMxODYzOTUzNjQ5MDkwNDgzNzMyMTkxNTQ2MTEwMjAxNTg0NzMxODg4NTE5NjA2MjE1OTkyNTgxNzk2MDg2NzUzOTE5NzUxMjkwMDI3MDI4NzczMTAwOTc5ODI5MzQ5NzA0MTUyMDEzNjg2MzU1MzM1MjIyNjU5MDY2NzE0NDQ2NDc4NzY3MTE5NDE4MjY3OTg5NTAyNzc4MjMzNzM3MjM4MjU1MTQxNzQyMjk4NTU3MDY2NzA2MTM0NzYwMjQwMzY3OTMzMzc5NzYzMTc5MTI1NTI4MDQwMzkxNjQwNTIyNTM5NjE5NTU0NTE0NTk4OTUxNTg0NjA3MjYwNzk1NzE1MDMyMjM4NTQ3ODMyMzA0NTY2MzQ4NjYzMTc0NzQwMDE2MzQ2NTU2MTM1ODc4MzgxNTYzODQ2NzU0MzQzMjk0NTIzNjc0NDI3NjQxNjAxNjAwNjE2NzI3NjEyMzc0MzI2NzY4ODA5NjAyNTE5MTAzOTk3NDY4OTg1NTg3Nzg4MjI3Njc5MzQ4NTgwNzk1OTkyOTkxNzMzMDg5MTUyMTg2MDg4OTU2MTg2MTQ0OTkyMDI5OTI2OTUxOTU0OTQyNjYwMDUxOTM0MDc5NzkxODI1NzA2MTExNzg0MDU2NDM2OTA2MDgxMDQ2MDQ5ODI0ODE1NDE0MTc5NTMzMDA2ODE4NzQ3NzgwNTQ5In0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjEwMTUyMDI0OTk1MTUxMzcyOTgwNzI5NDM1MTQ5MzgyMzQxMDQ3MDIzNjI2NjA4MDc3ODg1NzQ2Mjg4MTE3MjI2OTAyNzM4OTY5OTEwMTU0NjgzNzM2MzI4MzQ2MDg0MjcwMDA3MTY2NTg3ODI5MjE2MzEzNDc4MDk3Njc0MTI0ODU2ODIyMjA4NzI0Nzk1NTE0ODgzOTYwMzE5NDc5OTg4NzAzNDUyNjI4NjYxMDc3MTg3OTIyMzA1NDc5MDE2NzQzOTk0NzYwMzE5NzI1OTExODk0MjM2NDMxMDkxMTIyNTUxNTU0NzgwODg0NjQ2MjE0MTUzMDUzMTM2NDMwMTk4OTA5MTM0OTk4OTM2NjY3MzI4ODI2MDcwNzEzMzk0NDg0NDI0ODUxNjkxMzUxNDc0NjAxMjIwODk2NTIyMDYzNDA5NzA4NDA1Njk2MzY5MjA0MzU0NzE1MDkxMzk2Mzc4Mzc3MzA0ODk3MzMwOTM0Mjc2NTQyNjE2NjAxNTk1ODI5NzgxOTg3NTMyNzkxMzIyNTgzOTE1Njk1OTY2MjM3MTc4Njg1NTMzNTE3MTQxNTAyNDE3MzQxMDIzMTA1MTczMjMwMTcwNzUzODYwMjgxNDAxODk4MDE5OTQwNjA2MzczOTYwMzYxNjA3NTE2NjgyMDg4MTc1NzU4ODA0Mzg4MTM5MTQ0MDkwMjg5MzI5NzMzNTQ1NDg4MjUyNjczNDIyODkzMzc1MzE5ODQ2OTMwOTIyNjIwNzAzMTEwMDgwODU5OTE4ODQ0MzgyOTQ3ODczMjAwNzA4MTY2MzA0NDk4ODk0MDA4NTMyIiwiYyI6IjIyNDQyNTM5MzYwMzYzNjQyODI1ODkxNTc5ODgzMDE5Mjc3Mjk0NTQ2MjUwMDEzNTM3MzI2OTY2NzM3MzE0NTUxMjEwMjU3MjU2NDU5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", + }, + "mime-type": "application/json", + }, + ], + "~please_ack": Object { + "on": Array [ + "RECEIPT", + ], + }, + "~service": Object { + "recipientKeys": Array [ + "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3000", + }, + "~thread": Object { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + }, + "metadata": Object {}, + "role": "receiver", + "updatedAt": "2023-03-18T18:54:01.369Z", + }, + }, + "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64": Object { + "id": "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64", + "tags": Object { + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "messageId": "2a6a3dad-8838-489b-aeea-deef649b0dc1", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "DidCommMessageRecord", + "value": Object { + "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", + "createdAt": "2023-03-18T18:54:01.098Z", + "id": "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64", + "message": Object { + "@id": "2a6a3dad-8838-489b-aeea-deef649b0dc1", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiY3F4ZW8ybzVIWmVjYWc3VHo5aTJXcTRqejQxTml4THJjZW5HV0s2QmJKVyIsImNyZWRfZGVmX2lkIjoiQTRDWVBBU0pZUlpSdDk4WVdyYWMzSDozOkNMOjcyODI2NTpUQUciLCJibGluZGVkX21zIjp7InUiOiI3OTE4NzUwNzgzMzQxMjU3ODU3OTUzMjc2OTU5MjcxOTcyMDQwMjQxMTU1MzcyODEwOTQ2NTEwMjk5MDA1ODEyMTcwNjkwMjIzMTQ2ODU0NDk1MTI1NjQ1MTg3ODQxNzk0NjA3OTUwOTQ1OTM3MDYxMjk1ODgwMjIxMzE2NTg1MTIyNDY1Mzk1MjAwNDQ3MDIwNzAxNDA0NjEwMDc4MzkyMjA4NzkxMjk5NzYwMzM4OTIxNDMzMDc1Njk2ODU0NTY3MTIzNjYxNTYwNDMwNDE3NzQwMzc5MzA4NDQzODcyOTU1NzAwNTk1MTg2NzcxODY3MzM5NzQ5NDgzODYxNTQ2NTE2MTU4NTM5MjkyNDQzNTQ3OTg3MDUwMzE4OTAyOTI5OTgyNzMzMDk1ODk4MDIyMjg2OTk1OTQwMjkzMTg3NTg5NDgwNTgwNTAwNjM0NzAyNjQxNDM0MTgxNjIwMTU4OTU3MzUyNTE1OTA4NDE2MjI4MDQ0NDA2OTU4MTk1MDg4Mjc0ODI4Njc3OTQxMDgxOTczOTg3NjU1MDEzNDUxMzA4ODQyMjYyMzY4MTQzOTExNjIxMTE0NzYyNTk3Nzg1MDczMTM4MDg3NTQ2MDIyMzc1NjQxODQ5ODI2OTg2MjYwMDE5NTAzNzE3OTk0NjM3MzIyNDExNTgzNTY0MTQ1NjcwMTM5OTQ1MjQxOTU2Njk2MDQ3MTQzNjA4NjQ0MDM5OTg2NTYwODUyMzA1MTczMjUxMTUyMDIzODI5NjI3MzQyODM2NDI3MjkwNDQ5NTA3OTY0Nzk4MTQ2NjkzOTUxMDkwNzUwOTAyNiIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6Ijk4MjIzNzMzMDcwMDk1NjM0MzU5MTE2MDEyOTgzMDcyMjc5MjcxMzk1MTg0NjA0NDcxNDQ5NzA1Nzg0MTEyNDAyODMwNzIyMTUxMzIxIiwidl9kYXNoX2NhcCI6IjU5ODA0MTY4ODAxODk1NDAzMjkwMzczMTA3NzA4MzUwODc1NjY4MDcyNDY3ODAyOTcyOTIyNjUzNDE5ODU2MjMyNTIzNDI4OTUxODQ4NjEyNDE1MjM5Nzk3Mjk5ODY2MzIxNjU5NDQ1MTM1NzQ4NzU2MDY1NjgyOTY5MjY4ODI5MTYyMDA0NjQ4NzYwMzg4NTg4NDkyNjg1NDI1MTg1MDU2OTAxOTkxMjcwMzYwMDk3MDc5NjEyNTYxMzY4NzU1OTcwMjY5MjI4MDYzMjMyODU0NzI0NDkyOTA5Njg5MDMyOTg4NjYyMjk5Mzg3MDU2NDEwNjU5MDU3MDUwNjE0MDQ2NzE1NjA0NTgyMzM2NTg4MjMxMjI3MTEzMDEzMDQxMTA0NTU2NzM1MDE3ODUwNzUzODcwNjc2ODYxNDA4MjA0NzkzMDIzNTYwMDEwNTEzODAwNzA4MjAyNjAyNjQ0Mjg2NzI4NjAyOTk5MzU5MDcwODQxMTQ5MTAzMjA5MzY0ODkyMzMzMDYwMDgzMTA5NDIzOTQ5NzE4NTk5NjEzMzk2NjIyMjc4MTExMzk5ODU0MTcyMjMyNTQzOTk1Njc5NDk3Mjk1Nzc1NzA0MjA0MTQxOTU2NDI1MDc4NjYzMzgwMDA1Nzc2ODY2MTcxNTY4OTU1NjE4NjAwMTA2NzkxMjIyNDkyODA2NzI1ODU1NDY2Nzk4OTEzMTc2NDcxMDY3MTk5ODQ2ODEwNDI5MDIzMDc3ODI3OTc1OTIzMDIzNjU3MTg0NjkwNzE0MjkxNDk0MDc5MTM1NzYyOTUxNTc0MjMzNjMwMjExNDQ1Njc3NzE1Mzg3Mzc1NjkyMjAzODE3NDczNDk5NDExNzE5MzIwMjczNzExOTIzMzM3MzYzMTAyOTkwMDcyMjE2MjYzMzUxMzMxNTk4ODk1OTU3MzU1MDc1NTEzODE0NTUwNzkyMCIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiNTY0MTY1NjA2NjM0MTUwODgzMjg2MjM1NDM1MjUyODQ3MjAxMTk4NTQyNDAxMjYzNTY2MDQ2MTA3OTU3NzI4NTQ0MTMwMjgzNjUyNjQzOTI0NDc2NTU2NTIzNzg0ODI1OTgyMzMwMzc4NDI4OTM0MDkxNDcwNDM0OTAwMTM3NzkwNDkxMjM4NTA4ODk2ODQxMzkwNjQ4MTA1NzY5ODYzMzI1OTAzODI1Mjk1MjU2OTQ0NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMzE1MDIwOTY0MDY4NjgyMDQ0Mzc4MjEifQ==", + }, + "mime-type": "application/json", + }, + ], + "~service": Object { + "recipientKeys": Array [ + "cqxeo2o5HZecag7Tz9i2Wq4jz41NixLrcenGWK6BbJW", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3001", + }, + "~thread": Object { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + }, + "metadata": Object {}, + "role": "sender", + "updatedAt": "2023-03-18T18:54:01.099Z", + }, + }, +} +`; + +exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the schema and credential definition, and create link secret records for issuers 1`] = ` +Object { + "1-4e4f-41d9-94c4-f49351b811f1": Object { + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "tags": Object { + "isDefault": true, + "linkSecretId": "Wallet: 0.3 Update AnonCreds - Issuer", + }, + "type": "AnonCredsLinkSecretRecord", + "value": Object { + "_tags": Object { + "isDefault": true, + }, + "id": "1-4e4f-41d9-94c4-f49351b811f1", + "linkSecretId": "Wallet: 0.3 Update AnonCreds - Issuer", + "metadata": Object {}, + "updatedAt": "2023-03-19T22:50:20.522Z", + "value": undefined, + }, + }, + "1545e17d-fc88-4020-a1f7-e6dbcf1e5266": Object { + "id": "1545e17d-fc88-4020-a1f7-e6dbcf1e5266", + "tags": Object { + "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222", + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", + "tag": "TAG2222", + }, + "type": "AnonCredsCredentialDefinitionRecord", + "value": Object { + "credentialDefinition": Object { + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", + "tag": "TAG2222", + "type": "CL", + "value": Object { + "primary": Object { + "n": "92672464557302826159958381706610232890780336783477671819498833000372263812875113518039840314305532823865676182383425212337361529127538393953888294696727490398569250059424479369124987018050461872589017845243006613503064725987487445193151580781503573638936354603906684667833347097853363102011613363551325414493877438329911648643160153986822516630519571283817404410939266429143144325310144873915523634615108054232698216677813083664813055224968248142239446186423096615162232894052206134565411335121805318762068246410255953720296084525738290155785653879950387998340378428740625858243516259978797729172915362664095388670853", + "r": Object { + "age": "66774168049579501626527407565561158517617240253618394664527561632035323705337586053746273530704030779131642005263474574499533256973752287111528352278167213322154697290967283640418150957238004730763043665983334023181560033670971095508406493073727137576662898702804435263291473328275724172150330235410304531103984478435316648590218258879268883696376276091511367418038567366131461327869666106899795056026111553656932251156588986604454718398629113510266779047268855074155849278155719183039926867214509122089958991364786653941718444527779068428328047815224843863247382688134945397530917090461254004883032104714157971400208", + "height": "36770374391380149834988196363447736840005566975684817148359676140020826239618728242171844190597784913998189387814084045750250841733745991085876913508447852492274928778550079342017977247125002133117906534740912461625630470754160325262589990928728689070499835994964192507742581994860212500470412940278375419595406129858839275229421691764136274418279944569154327695608011398611897919792595046386574831604431186160019573221025054141054966299987505071844770166968281403659227192031982497703452822527121064221030191938050276126255137769594174387744686048921264418842943478063585931864099188919773279516048122408000535396365", + "master_secret": "26619502892062275386286102324954654427871501074061444846499515284182097331967223335934051936866595058991987589854477281430063143491959604612779394547177027208671151839864660333634457188140162529133121090987235146837242477233778516233683361556079466930407338673047472758762971774183683006400366713364299999136369605402942210978218705656266115751492424192940375368169431001551131077280268253962541139755004287154221749191778445668471756569604156885298127934116907544590473960073154419342138695278066485640775060389330807300193554886282756714343171543381166744147102049996134009291163457413551838522312496539196521595692", + "name": "86741028136853574348723360731891313985090403925160846711944073250686426070668157504590860843944722066104971819518996745252253900749842002049747953678564857190954502037349272982356665401492886602390599170831356482930058593126740772109115907363756874709445041702269262783286817223011097284796236690595266721670997137095592005971209969288260603902458413116126663192645410011918509026240763669966445865557485752253073758758805818980495379553872266089697405986128733558878942127067722757597848458411141451957344742184798866278323991155218917859626726262257431337439505881892995617030558234045945209395337282759265659447047", + }, + "rctxt": "71013751275772779114070724661642241189015436101735233481124050655632421295506098157799226697991094582116557937036881377025107827713675564553986787961039221830812177248435167562891351835998258222703796710987072076518659197627933717399137564619646356496210281862112127733957003638837075816198062819168957810762822613691407808469027306413697001991060047213339777833838291591976754857934071589843434238025803790508552421154902537027548698271140571140256835534208651964449214890690159171682094521879102663244464066621388809286987873635426369915309596945084951678722672915158041830248278889303704844284468270547467324686757", + "s": "14126994029068124564262196574803727042317991235159231485233854758856355239996741822278406673337232628669751727662479515044513565209261235580848666630891738643990084502393352476512637677170660741636200618878417433799077613673205726221908822955109963272016538705991333626487531499501561952303907487494079241110050020874027756313402672435051524680914533743665605349121374703526870439925807395782970618162620991315112088226807823652545755186406850860290372739405126488851340032404507898084409367889215777693868794728141508635105180827151292046483128114528214465463152927678575672993454367871685772245405671312263615738674", + "z": "90415953543044389740703639345387867170174070770040351538453902580989033567810029650534915348296084212079064544906463014824475317557221991571331212308335167828473551776349999211544897426382305096215624787217055491736755052175278235595298571339706430785816901931128536495808042995635624112114867111658850659510246291844949806165980806847525704751270260070165853067310918184720602183083989806069386048683955313982129380729637761521928446431397104973906871109766946008926113644488012281655650467201044142029022180536946134328567554182760495139058952910079169456941591963348364521142012653606596379566245637724637892435425", + }, + "revocation": Object { + "g": "1 1864FF219549D1BC1E492955305FC5EED27C114580F206532D2F5D983A1DD3BD 1 0414758D7B6B254A9CA81E1084721A97CA312497C21BB9B16096636C59F9D105 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "g_dash": "1 2327DA248E721E3935D81C5579DD3707882FFB962B518D37FB1112D96CC63611 1 164989452135CF5D840A20EE354DBF26BEEC74DE7FD53672E55224BEE0228128 1 0634D5E85C210319BFD2535AFD8F7F79590B2F5CC61AF794218CC50B43FBB8C6 1 0A63F1C0FC2C4540156C7A2E2A2DF1DDF99879C25B4F622933707DD6074A0F1B 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + "h": "1 0A031B1932CDFEE76C448CA0B13A7DDC81615036DA17B81DB2E5DFC7D1F6CD6F 1 06F46C9CC7D32A11C7D2A308D4C71BEE42B3BD9DD54141284D92D64D3AC2CE04 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h0": "1 1C88CA353EF878B74E7F515C88E2CBF11FDC3047E3C5057B34ECC2635B4F8FA5 1 1D645261FBC6164EC493BB700B5D8D5C8BF876FD9BA034B107753C79A53B0321 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h1": "1 16AC82FE7769689173EABA532E7A489DF87F81AE891C1FDA90FE9813F6761D71 1 147E45451C76CD3A9B0649B12E27EA0BF4E85E632D1B2BEC3EC9FFFA51780ACE 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h2": "1 2522C4FAA35392EE9B35DAC9CD8E270364598A5ED019CB34695E9C01D43C16DC 1 21D353FB299C9E39C976055BF4555198C63F912DBE3471E930185EF5A20470E5 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "h_cap": "1 1E3272ABDFD9BF05DB5A7667335A48B9026C9EA2C8DB9FA6E59323BBEB955FE2 1 031BD12497C5BBD68BEA2D0D41713CDFFDCBE462D603C54E9CA5F50DE792E1AB 1 05A917EBAA7D4B321E34F37ADC0C3212CE297E67C7D7FEC4E28AD4CE863B7516 1 16780B2C5BF22F7868BF7F442987AF1382F6465A581F6824245EFB90D4BB8B62 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + "htilde": "1 24D87DBC6283534AE2AA38C45E52D83CC1E70BD589C813F412CC68563F52A2CA 1 05189BC1AAEE8E2A6CB92F65A8C0A18E4125EE61E5CEF1809EF68B388844D1B1 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "pk": "1 0A165BF9A5546F44298356622C58CA29D2C8D194402CAFCAF5944BE65239474E 1 24BA0620893059732B89897F601F37EF92F9F29B4526E094DA9DC612EB5A90CD 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", + "u": "1 1F654067166C73E14C4600C2349F0756763653A0B66F8872D99F9642F3BD2013 1 24B074FFB3EE1E5E7A17A06F4BCB4082478224BD4711619286266B59E3110777 1 001B07BEE5A1E36C0BBC31E56E039B39BB0A1BA2F491C2F674EC5CB89150FC2F 1 0F4F1E71A11EB1215DE5A081B7651E1E22C30FCCC5566E13F0A8062DB67B9E32 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + "y": "1 020240A177435C7D5B1DBDB78A5F0A34A353447991E670BA09E69CCD03FA6800 1 1501D3C784703A097EDDE368B27B85229030C2942C4874CB913C7AAB8C3EF61A 1 109DB12EF355D8A477E353970300E8C0AC2E48793D3DC13416BFF75145BAD753 1 079C6F242737A5D97AC34CDE4FDE4BEC057A399E73E4EF87E7024048163A005F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", + }, + }, + }, + "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222", + "id": "1545e17d-fc88-4020-a1f7-e6dbcf1e5266", + "metadata": Object {}, + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "598dbcc3-5272-4503-9c67-b0cb69a9d3d6": Object { + "id": "598dbcc3-5272-4503-9c67-b0cb69a9d3d6", + "tags": Object { + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "messageId": "2a6a3dad-8838-489b-aeea-deef649b0dc1", + "messageName": "request-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/request-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "receiver", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "DidCommMessageRecord", + "value": Object { + "_tags": Object {}, + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "createdAt": "2023-03-18T18:54:01.126Z", + "id": "598dbcc3-5272-4503-9c67-b0cb69a9d3d6", + "message": Object { + "@id": "2a6a3dad-8838-489b-aeea-deef649b0dc1", + "@type": "https://didcomm.org/issue-credential/1.0/request-credential", + "requests~attach": Array [ + Object { + "@id": "libindy-cred-request-0", + "data": Object { + "base64": "eyJwcm92ZXJfZGlkIjoiY3F4ZW8ybzVIWmVjYWc3VHo5aTJXcTRqejQxTml4THJjZW5HV0s2QmJKVyIsImNyZWRfZGVmX2lkIjoiQTRDWVBBU0pZUlpSdDk4WVdyYWMzSDozOkNMOjcyODI2NTpUQUciLCJibGluZGVkX21zIjp7InUiOiI3OTE4NzUwNzgzMzQxMjU3ODU3OTUzMjc2OTU5MjcxOTcyMDQwMjQxMTU1MzcyODEwOTQ2NTEwMjk5MDA1ODEyMTcwNjkwMjIzMTQ2ODU0NDk1MTI1NjQ1MTg3ODQxNzk0NjA3OTUwOTQ1OTM3MDYxMjk1ODgwMjIxMzE2NTg1MTIyNDY1Mzk1MjAwNDQ3MDIwNzAxNDA0NjEwMDc4MzkyMjA4NzkxMjk5NzYwMzM4OTIxNDMzMDc1Njk2ODU0NTY3MTIzNjYxNTYwNDMwNDE3NzQwMzc5MzA4NDQzODcyOTU1NzAwNTk1MTg2NzcxODY3MzM5NzQ5NDgzODYxNTQ2NTE2MTU4NTM5MjkyNDQzNTQ3OTg3MDUwMzE4OTAyOTI5OTgyNzMzMDk1ODk4MDIyMjg2OTk1OTQwMjkzMTg3NTg5NDgwNTgwNTAwNjM0NzAyNjQxNDM0MTgxNjIwMTU4OTU3MzUyNTE1OTA4NDE2MjI4MDQ0NDA2OTU4MTk1MDg4Mjc0ODI4Njc3OTQxMDgxOTczOTg3NjU1MDEzNDUxMzA4ODQyMjYyMzY4MTQzOTExNjIxMTE0NzYyNTk3Nzg1MDczMTM4MDg3NTQ2MDIyMzc1NjQxODQ5ODI2OTg2MjYwMDE5NTAzNzE3OTk0NjM3MzIyNDExNTgzNTY0MTQ1NjcwMTM5OTQ1MjQxOTU2Njk2MDQ3MTQzNjA4NjQ0MDM5OTg2NTYwODUyMzA1MTczMjUxMTUyMDIzODI5NjI3MzQyODM2NDI3MjkwNDQ5NTA3OTY0Nzk4MTQ2NjkzOTUxMDkwNzUwOTAyNiIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6Ijk4MjIzNzMzMDcwMDk1NjM0MzU5MTE2MDEyOTgzMDcyMjc5MjcxMzk1MTg0NjA0NDcxNDQ5NzA1Nzg0MTEyNDAyODMwNzIyMTUxMzIxIiwidl9kYXNoX2NhcCI6IjU5ODA0MTY4ODAxODk1NDAzMjkwMzczMTA3NzA4MzUwODc1NjY4MDcyNDY3ODAyOTcyOTIyNjUzNDE5ODU2MjMyNTIzNDI4OTUxODQ4NjEyNDE1MjM5Nzk3Mjk5ODY2MzIxNjU5NDQ1MTM1NzQ4NzU2MDY1NjgyOTY5MjY4ODI5MTYyMDA0NjQ4NzYwMzg4NTg4NDkyNjg1NDI1MTg1MDU2OTAxOTkxMjcwMzYwMDk3MDc5NjEyNTYxMzY4NzU1OTcwMjY5MjI4MDYzMjMyODU0NzI0NDkyOTA5Njg5MDMyOTg4NjYyMjk5Mzg3MDU2NDEwNjU5MDU3MDUwNjE0MDQ2NzE1NjA0NTgyMzM2NTg4MjMxMjI3MTEzMDEzMDQxMTA0NTU2NzM1MDE3ODUwNzUzODcwNjc2ODYxNDA4MjA0NzkzMDIzNTYwMDEwNTEzODAwNzA4MjAyNjAyNjQ0Mjg2NzI4NjAyOTk5MzU5MDcwODQxMTQ5MTAzMjA5MzY0ODkyMzMzMDYwMDgzMTA5NDIzOTQ5NzE4NTk5NjEzMzk2NjIyMjc4MTExMzk5ODU0MTcyMjMyNTQzOTk1Njc5NDk3Mjk1Nzc1NzA0MjA0MTQxOTU2NDI1MDc4NjYzMzgwMDA1Nzc2ODY2MTcxNTY4OTU1NjE4NjAwMTA2NzkxMjIyNDkyODA2NzI1ODU1NDY2Nzk4OTEzMTc2NDcxMDY3MTk5ODQ2ODEwNDI5MDIzMDc3ODI3OTc1OTIzMDIzNjU3MTg0NjkwNzE0MjkxNDk0MDc5MTM1NzYyOTUxNTc0MjMzNjMwMjExNDQ1Njc3NzE1Mzg3Mzc1NjkyMjAzODE3NDczNDk5NDExNzE5MzIwMjczNzExOTIzMzM3MzYzMTAyOTkwMDcyMjE2MjYzMzUxMzMxNTk4ODk1OTU3MzU1MDc1NTEzODE0NTUwNzkyMCIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiNTY0MTY1NjA2NjM0MTUwODgzMjg2MjM1NDM1MjUyODQ3MjAxMTk4NTQyNDAxMjYzNTY2MDQ2MTA3OTU3NzI4NTQ0MTMwMjgzNjUyNjQzOTI0NDc2NTU2NTIzNzg0ODI1OTgyMzMwMzc4NDI4OTM0MDkxNDcwNDM0OTAwMTM3NzkwNDkxMjM4NTA4ODk2ODQxMzkwNjQ4MTA1NzY5ODYzMzI1OTAzODI1Mjk1MjU2OTQ0NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMzE1MDIwOTY0MDY4NjgyMDQ0Mzc4MjEifQ==", + }, + "mime-type": "application/json", + }, + ], + "~service": Object { + "recipientKeys": Array [ + "cqxeo2o5HZecag7Tz9i2Wq4jz41NixLrcenGWK6BbJW", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3001", + }, + "~thread": Object { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + }, + "metadata": Object {}, + "role": "receiver", + "updatedAt": "2023-03-18T18:54:01.126Z", + }, + }, + "6ef35f59-a732-42f0-9c5e-4540cd3a672f": Object { + "id": "6ef35f59-a732-42f0-9c5e-4540cd3a672f", + "tags": Object { + "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG", + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", + "tag": "TAG", + }, + "type": "AnonCredsCredentialDefinitionRecord", + "value": Object { + "credentialDefinition": Object { + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", + "tag": "TAG", + "type": "CL", + "value": Object { + "primary": Object { + "n": "92212366077388130017820454980772482128748816766820141476572599854614095851660955000471493059368591899172871902601780138917819366396362308478329294184309858890996528496805316851980442998603067852135492500241351106196875782591605768921500179261268030423733287913264566336690041275292095018304899931956463465418485815424864260174164039300668997079647515281912887296402163314193409758676035183692610399804909476026418386307889108672419432084350222061008099663029495600327790438170442656903258282723208685959709427842790363181237326817713760262728130215152068903053780106153722598661062532884431955981726066921637468626277", + "r": Object { + "age": "12830581846716232289919923091802380953776468678758115385731032778424701987000173171859986490394782070339145726689704906636521504338663443469452098276346339448054923530423862972901740020260863939784049655599141309168321131841197392728580317478651190091260391159517458959241170623799027865010022955890184958710784660242539198197998462816406524943537217991903198815091955260278449922637325465043293444707204707128649276474679898162587929569212222042385297095967670138838722149998051089657830225229881876437390119475653879155105350339634203813849831587911926503279160004910687478611349149984784835918594248713746244647783", + "master_secret": "61760181601132349837705650289020474131050187135887129471275844481815813236212130783118399756778708344638568886652376797607377320325668612002653752234977886335615451602379984880071434500085608574636210148262041392898193694256008614118948399335181637372037261847305940365423773073896368876304671332779131812342778821167205383614143093932646167069176375555949468490333033638790088487176980785886865670928635382374747549737473235069853277820515331625504955674335885563904945632728269515723913822149934246500994026445014344664596837782532383727917670585587931554459150014400148586199456993200824425072825041491149065115358", + "name": "26931653629593338073547610164492146524581067674323312766422801723649824593245481234130445257275008372300577748467390938672361842062002005882497002927312107798057743381013725196864084323240188855871993429346248168719358184490582297236588103100736704037766893167139178159330117766371806271005063205199099350905918805615139883380562348264630567225617537443345104841331985857206740142310735949731954114795552226430346325242557801443933408634628778255674180716568613268278944764455783252702248656985033565125477742417595184280107251126994232013125430027211388949790163391384834400043466265407965987657397646084753620067162", + }, + "rctxt": "49138795132156579347604024288478735151511429635862925688354411685205551763173458098934068417340097826251030547752551543780926866551808708614689637810970695962341030571486307177314332719168625736959985286432056963760600243473038903885347227651607234887915878119362501367507071709125019506105125043394599512754034429977523734855754182754166158276654375145600716372728023694171066421047665189687655246390105632221713801254689564447819382923248801463300558408016868673087319876644152902663657524012266707505607127264589517707325298805787788577090696580253467312664036297509153665682462337661380935241888630672980409135218", + "s": "51390585781167888666038495435187170763184923351566453067945476469346756595806461020566734704158200027078692575370502193819960413516290740555746465017482403889478846290536023708403164732218491843776868132606601025003681747438312581577370961516850128243993069117644352618102176047630881347535103984514944899145266563740618494984195198066875837169587608421653434298405108448043919659694417868161307274719186874014050768478275366248108923366328095899343801270111152240906954275776825865228792303252410200003812030838965966766135547588341334766187306815530098180130152857685278588510653805870629396608258594629734808653690", + "z": "60039858321231958911193979301402644724013798961769784342413248136534681852773598059805490735235936787666273383388316713664379360735859198156203333524277752965063504355175962212112042368638829236003950022345790744597825843498279654720032726822247321101635671237626308268641767351508666548662103083107416168951088459343716911392807952489009684909391952363633692353090657169830487309162716174148340837088238136793727262599036868196525437496909391247737814314203700293659965465494637540937762691328712617352605531361117679740841379808332881579693119257467828678864789270752346248637901288389165259844857126172669320275054", + }, + }, + }, + "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG", + "id": "6ef35f59-a732-42f0-9c5e-4540cd3a672f", + "metadata": Object {}, + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "STORAGE_VERSION_RECORD_ID": Object { + "id": "STORAGE_VERSION_RECORD_ID", + "tags": Object {}, + "type": "StorageVersionRecord", + "value": Object { + "createdAt": "2023-03-18T18:53:43.140Z", + "id": "STORAGE_VERSION_RECORD_ID", + "metadata": Object {}, + "storageVersion": "0.4", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "a56d83c5-2427-4f06-9a90-585623cf854a": Object { + "id": "a56d83c5-2427-4f06-9a90-585623cf854a", + "tags": Object { + "connectionId": undefined, + "credentialIds": Array [], + "state": "offer-sent", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + }, + "type": "CredentialRecord", + "value": Object { + "createdAt": "2023-03-18T18:53:59.859Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "John", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "99", + }, + Object { + "mime-type": "text/plain", + "name": "height", + "value": "180", + }, + ], + "credentials": Array [], + "id": "a56d83c5-2427-4f06-9a90-585623cf854a", + "metadata": Object { + "_anoncreds/credential": Object { + "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728266:TAG2222", + "schemaId": "A4CYPASJYRZRt98YWrac3H:2:AnotherSchema:5.12", + }, + }, + "protocolVersion": "v2", + "state": "offer-sent", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf": Object { + "id": "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf", + "tags": Object { + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "messageId": "578fc144-1e01-418c-b564-1523eb1e95b8", + "messageName": "issue-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/issue-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "DidCommMessageRecord", + "value": Object { + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "createdAt": "2023-03-18T18:54:01.192Z", + "id": "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf", + "message": Object { + "@id": "578fc144-1e01-418c-b564-1523eb1e95b8", + "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", + "credentials~attach": Array [ + Object { + "@id": "libindy-cred-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJhZ2UiOnsicmF3IjoiOTkiLCJlbmNvZGVkIjoiOTkifSwibmFtZSI6eyJyYXciOiJKb2huIiwiZW5jb2RlZCI6Ijc2MzU1NzEzOTAzNTYxODY1ODY2NzQxMjkyOTg4NzQ2MTkxOTcyNTIzMDE1MDk4Nzg5NDU4MjQwMDc3NDc4ODI2NTEzMTE0NzQzMjU4In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjMyMTIwMDQ1ODc4MzIxMjcyMzA1ODI5MTc3NzMzMTIwNzE1OTY5NDEyNjkwNjUyNDQ4OTc0MTA4NzEzNjU0ODc3NTg2MzIzMTI3ODk2IiwiYSI6IjIyMjY0MTYwNjIwODcwNDUyNTExMjcyMzE1MzMzMDA0MjQzMzY3NTM2NzM3NDMwNjM1NjExMjcwMDkwOTE4NDMwNzc0ODEzMjAzNjQwNjMxMjIyNDczMzk0MjQ4MTgzMDIzMjIyNzExNDUwMzQxMDcxOTQyNDQwMDgwMjY2Nzk1Mzg5Mzg5Njc1NjYwOTUzNTQyMDE4OTA3NjQ3NzI4OTQ4NjY1MzA2Njg0NjExNDU1NTI5NzM5OTY1NDcyMjQ2NDQxMzE1NzAxMzM1ODc1MDY3MjExMDk3NzcyOTgwMjU1NDIxMDMzMTI1MjAyMTQzNDk3NjMyOTAyMjM1NDAyMzU5OTA1MzY5MzE4MjI1NTc4MjUxNjY4NTYzNzc1NTY0MDM2MjUxNzE0Mzk3MTEzNjQ3OTg0MjcxMTE5MTU2NDQ3NjI1OTk1NjE5MjAwMDk4MTgzNzY1NjkzMTg1ODEzNjA1NDU3OTQwMzE0MDU2MDkzMTI2MzQ3OTU5MzYwODIyMzg0OTEzODg3Mjg3ODI2NjkyNDIyNDMyNDUwMDA5OTYxNjQ2MjMzNTE3MjY3NDU1OTkyMjA3MTE3Mzk5NzU1NjY3MTA3MzM1NTQ0MzEwNDQwNDE1NDE5NTk5NTA1OTgxMzkwMjk5NDUxNzQyODg4NDg0MTc0NTU5MDA5NDgwNjU5MDk2Nzg2ODI2MDgxNzc3MzcwNTk1MTU3OTg5NjQ1MDYxNDI2OTA2ODM2MDk5NTU5MDQ0MDI4ODM2MzYwOTM2MDkwOTkxNjA1OTU0NTM2OTQxMjgxODQwNzk2MjkxODc0ODk2NDEzNTM5IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDExMzE1MTE0OTUxODg0MDQ5ODkyNjAwNTY3NTgzMTc4ODkwMyIsInYiOiI2NTE5ODc4MzYyODQ5NzExNDY4NDkyNDM1OTM3MDU4NzMzOTYxMTkxNjA3NjI4MzUzNjkxMDg1MzM5MDMwNDU0OTkyODc0ODYyODkyNDg4ODYzNTA3MDQ1MTM1MzA4ODI1NDA2NzYwMTQwNDQzNzM0NDYzODE5NTM2MzE0NzcxMTQ3MDk4MjU2ODMzNTc2MjIwNDI5ODQyNjc3NzMwMzQwODYwNjE2NTcxNzc5NjU4OTIxNDY4Mjc0NTUwOTc5NjYyMDkxNzEwNDU5MDk2MDgzMzYzNTc1Mjc0MjQzNzIyMzIzOTIxMjY5MDYyMjE0NjQyNzQyMTI0MzQ4MTY0MDUxNzE3MTk5MTkzODY3NTM3NTEzNjYzOTY1ODQzMDI5MjAxODA0OTE2MTEzNzMxODYzOTUzNjQ5MDkwNDgzNzMyMTkxNTQ2MTEwMjAxNTg0NzMxODg4NTE5NjA2MjE1OTkyNTgxNzk2MDg2NzUzOTE5NzUxMjkwMDI3MDI4NzczMTAwOTc5ODI5MzQ5NzA0MTUyMDEzNjg2MzU1MzM1MjIyNjU5MDY2NzE0NDQ2NDc4NzY3MTE5NDE4MjY3OTg5NTAyNzc4MjMzNzM3MjM4MjU1MTQxNzQyMjk4NTU3MDY2NzA2MTM0NzYwMjQwMzY3OTMzMzc5NzYzMTc5MTI1NTI4MDQwMzkxNjQwNTIyNTM5NjE5NTU0NTE0NTk4OTUxNTg0NjA3MjYwNzk1NzE1MDMyMjM4NTQ3ODMyMzA0NTY2MzQ4NjYzMTc0NzQwMDE2MzQ2NTU2MTM1ODc4MzgxNTYzODQ2NzU0MzQzMjk0NTIzNjc0NDI3NjQxNjAxNjAwNjE2NzI3NjEyMzc0MzI2NzY4ODA5NjAyNTE5MTAzOTk3NDY4OTg1NTg3Nzg4MjI3Njc5MzQ4NTgwNzk1OTkyOTkxNzMzMDg5MTUyMTg2MDg4OTU2MTg2MTQ0OTkyMDI5OTI2OTUxOTU0OTQyNjYwMDUxOTM0MDc5NzkxODI1NzA2MTExNzg0MDU2NDM2OTA2MDgxMDQ2MDQ5ODI0ODE1NDE0MTc5NTMzMDA2ODE4NzQ3NzgwNTQ5In0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjEwMTUyMDI0OTk1MTUxMzcyOTgwNzI5NDM1MTQ5MzgyMzQxMDQ3MDIzNjI2NjA4MDc3ODg1NzQ2Mjg4MTE3MjI2OTAyNzM4OTY5OTEwMTU0NjgzNzM2MzI4MzQ2MDg0MjcwMDA3MTY2NTg3ODI5MjE2MzEzNDc4MDk3Njc0MTI0ODU2ODIyMjA4NzI0Nzk1NTE0ODgzOTYwMzE5NDc5OTg4NzAzNDUyNjI4NjYxMDc3MTg3OTIyMzA1NDc5MDE2NzQzOTk0NzYwMzE5NzI1OTExODk0MjM2NDMxMDkxMTIyNTUxNTU0NzgwODg0NjQ2MjE0MTUzMDUzMTM2NDMwMTk4OTA5MTM0OTk4OTM2NjY3MzI4ODI2MDcwNzEzMzk0NDg0NDI0ODUxNjkxMzUxNDc0NjAxMjIwODk2NTIyMDYzNDA5NzA4NDA1Njk2MzY5MjA0MzU0NzE1MDkxMzk2Mzc4Mzc3MzA0ODk3MzMwOTM0Mjc2NTQyNjE2NjAxNTk1ODI5NzgxOTg3NTMyNzkxMzIyNTgzOTE1Njk1OTY2MjM3MTc4Njg1NTMzNTE3MTQxNTAyNDE3MzQxMDIzMTA1MTczMjMwMTcwNzUzODYwMjgxNDAxODk4MDE5OTQwNjA2MzczOTYwMzYxNjA3NTE2NjgyMDg4MTc1NzU4ODA0Mzg4MTM5MTQ0MDkwMjg5MzI5NzMzNTQ1NDg4MjUyNjczNDIyODkzMzc1MzE5ODQ2OTMwOTIyNjIwNzAzMTEwMDgwODU5OTE4ODQ0MzgyOTQ3ODczMjAwNzA4MTY2MzA0NDk4ODk0MDA4NTMyIiwiYyI6IjIyNDQyNTM5MzYwMzYzNjQyODI1ODkxNTc5ODgzMDE5Mjc3Mjk0NTQ2MjUwMDEzNTM3MzI2OTY2NzM3MzE0NTUxMjEwMjU3MjU2NDU5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", + }, + "mime-type": "application/json", + }, + ], + "~please_ack": Object { + "on": Array [ + "RECEIPT", + ], + }, + "~service": Object { + "recipientKeys": Array [ + "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3000", + }, + "~thread": Object { + "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + }, + "metadata": Object {}, + "role": "sender", + "updatedAt": "2023-03-18T18:54:01.192Z", + }, + }, + "be76cfbf-111b-4332-b1fe-7a1fea272188": Object { + "id": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "tags": Object { + "connectionId": undefined, + "credentialIds": Array [], + "state": "done", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "CredentialRecord", + "value": Object { + "createdAt": "2023-03-18T18:53:59.068Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "John", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "99", + }, + ], + "credentials": Array [], + "id": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "metadata": Object { + "_anoncreds/credential": Object { + "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG", + "schemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "d7353d4a-24fc-405f-9bf5-f99fae726349": Object { + "id": "d7353d4a-24fc-405f-9bf5-f99fae726349", + "tags": Object { + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "messageId": "c5fc78be-b355-4411-86f3-3d97482b9841", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/1.0/offer-credential", + "protocolMajorVersion": "1", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", + }, + "type": "DidCommMessageRecord", + "value": Object { + "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", + "createdAt": "2023-03-18T18:53:59.857Z", + "id": "d7353d4a-24fc-405f-9bf5-f99fae726349", + "message": Object { + "@id": "c5fc78be-b355-4411-86f3-3d97482b9841", + "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", + "attributes": Array [ + Object { + "name": "name", + "value": "John", + }, + Object { + "name": "age", + "value": "99", + }, + ], + }, + "offers~attach": Array [ + Object { + "@id": "libindy-cred-offer-0", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiODUxODAxMDMyNzEzNDg5NzYxOTg5MzAzNjMzMDkzOTEyOTExMDUxNjI0OTQ0OTYzMTgzNzM2MDY3NDkwOTc2MDYxODEwMDgxODkxMzQiLCJ4el9jYXAiOiI4NDk0NDg4MjQzNTk2NTkwOTc2MjQzMjc0NDg4ODk2Mjc1NTcyODAyMTQ1ODE5NDQzNTE0NzQxMzk1NDI1NjM5MzQwMTczMDIzMTQ5NzI3MDY5NzMzMzQwODgzMTU4MzQ1NTYzOTA5OTcxNDMzNTg1MjMwODAxNTYyMTM0NjczNjM1ODg5NTA3Njg5ODQwOTgyODU5Mzg1NjA1MTc1NTkxNDYxOTkyMDExNzU2Mzg1MTI3MTQ3ODgxNDMwODEzNjYxNzY0MDU5MzE0ODk4MTc2NzQzMTQ5MjYzMDMwMDQ1NzMwMDMzMzI2NzgyMzg1OTY0NjcxMzg1ODQ2MzcxNjQ5MzQxMTg2MDM5NjE4MTQwOTIwMDUxMzg1MDAwNTYxMDcyMTc5NTEyMzc5Nzk0OTU4NjE1ODIyODI2OTExNzIwNTQyNTE0MTQ1NDc5MTAxOTUyMzM4MDMwMDY1MDk5NjcxOTU2OTMxMzE2NjE5MjM0NTQ0NTE5NTQ1ODQ1MzA4MzgxMjQyNTM0NDcyOTc3NjY0MjAwMjc2MTMyOTgxODE1ODAzNTIxOTExMzk4ODkxMjE0NjE1NzA1MDM2ODM2ODU1NDU1NzY4ODg4MTUxNDgzODAyNDcyODQyMzczNzE0MTI0NTYwMzIyNTI3NDE4MTEwNzYyMjgyNzY4NzMyNTIzMDQyMDA3MDY2OTk2ODIxMTQwMzE1NDg0NzI4NTM2NzIwNDI3MDg5MTI2NDk1NTAzMjc0ODQ4MDM3MjUzOTM3NjI3MDU2ODUzMTQ4NjE5NDA4NDYxOTI5NzEzMjM4MjEwNDc4MjcyMTIxNTUwNjQzODc4ODM1NDYwMzY1OTIwMjE3NTk5NDYyNDUzMDMyNDQ4MjYyMTM3NjE5ODY0OTU4MzA1MDE3MjA4OTYwNDc1MTQxODgwMTMiLCJ4cl9jYXAiOltbIm5hbWUiLCI1MDcyNzU2NDE2NDA2ODIxNzU1OTc0MzUxMTg0NjE1NjA4NDY2NTk3Mzk0NzA2MTY1NDg2ODAzMjc3MjMyMzQyOTk4MDA0MzY0OTU0MTczMzc0NDIwOTc5NTkwMDcyODgxNDgxNDA0MTg2OTExODg5NzQ4MTgzMzQ1OTk5NzQ0NzgxMTQ1MTMwNzEyNDIzODY0Nzc1MzQzNjAzNTk2NDM3Mzg4OTgzNTExNDAzODA0NjEyNjU1MDE5NzQ4MTI5NDk3ODY2NTcwMDQyMjcwNDQxNDQ5MjYwODY0NzgyMzI5MjAxNDEzMTc5ODU3NzA0MjM5OTMyMTg4NTc4NzE3MDczNzM3NjUyNzY5MzY5NDg4OTgxNzg2NDQwNTExODAzMjMzNDMxNzA4NDk4MTU2NTA0OTUzNzkzNjU2NjQ2NzMyNTU4MzQwNDI2MDI1MjA3NTk0OTIwMDY4OTc2OTQ4Nzg2OTUxNzM3MDIwNDQ0NTA5NzYyMDQ2MzIzNzA0MDQ3MjU1ODU3NDE5ODE3MDc5NTI3NDgzNTE1NDY2NTAyMDkzOTY1NDMzMzk3MjQ1MzA4MjQ5MDgyMTQ4Mjc4NDA1MzI5Njg1Mjc0MDYwNjk0MzI0MTI2ODgxMjkyMDIyMjY1ODczMjk5MDU0NDU1OTA5NzkyNjUwNjAyMTk0NjUzMjYxMDk0ODYwOTc2NzA4ODE1ODgwMjExMTY0MTkwMDM0NjY0MzI2MDc3NjcwNzkyMDE4NTE2MzMzNDI3NjkwODYwMjIxODEwMzk5MDgxMjc5NjAwNTYzMjk3MjI0NjM0MDM0NjcxNTIwODE5MzU3NzQ0Njk2NzU1Njg1NDI2NjIzMzAwMjQ3MDUwODE4NTQ2MDM2NjA0NjMxNjcyNzE5MjI0NDA4NTE2NDM4NTgxMDM5Njk4NzI0MSJdLFsibWFzdGVyX3NlY3JldCIsIjU2MzYzNTgyMDQ5Mjg4OTY1OTg1MDA4NzgyMzU0NjgyNjMwNDkxMzQ3MTM1NDIxNTAyMDEyMTIwMzI4MDI4ODIyMjUyMzg4NjgwNTMwNTgxMTcwMTgwNDU1MTcyNTc3ODkyMTEyMTY1OTM0Mjk5NjUyNzAxNDExMzUyNDkzMzkyODU0ODI4NzMyMDQzMDI0MDI0MzM0MzMzNzc0NjEyOTEzOTUyMjAzNjM1NDk2MDQ0ODMzMDI5NDE2NjUwOTU5NjE0ODgzNTUwOTMxNzgzNTA5MzE1Nzg4MDEyODQ0MzAwMDQwMDE5MTY5MTc3NTI1OTgxMTU3OTkwNjQzMDcyMjQyNzcxMjU0MTYyNzMxOTU4NzI2Nzc1NjYwMjkxODIzMDcyNDk1Mzg0NzM5MTcwODc4ODMxNzkxMjQzMjEzMjU5MzA5ODQxNjU3MjUwOTg1NzMxMjEyNzE2MDM2MDY3MDUxNjM2NzA0MjA1NDEzMDk2MDU3MTA2NTM2MTI2ODUyNDU0NzcwMzQzMTMwMTczMjAwNjEzMDIxOTE4MzgzMDQxOTU4MTkwOTE2NzQ0NjU4NTI0ODA1NjM4Mzk2OTY3OTA3MzIwNjY1MDU1MzcwMjY0NjAxMDczMjc5NDMyNjM5MjM3Njc1NTA0OTg1NzQyNTI4NjYwMTAyMDEzNzIxMzA2MTE4MTg0NDk1MTEyNDQ2NDYyNDc2NTkwMjYxODkxMjA0OTQxOTA4MjMyNzMzNDA3MTg4MDA3NzE2NTA2OTUzMDY0Nzc5NDk5ODExNzI0ODI5NjcyNjY2NzIyNjIzOTAxMTc1OTk0NTIyNjkwMjk1ODI0MDgyNzY5NjQ0NDYxOTAxMDk2NzI3MTE5NzAzMjUzNzI4NjY3MTU1MzA5MDYzNDUyNDY2MDY3NzU5NzIwOTgyNDA3MiJdLFsiYWdlIiwiMTM2NTQxMjE0MjM5MTcyNDQxNzQ1MjU3MjcyMDI3MTA4NDYwMzU0MjgxMTA2OTA2MzYwNDIwMDE0NjUyMDIxMDgyNDEzODM2ODEyMjk3NjY3ODk2MTYzNDkzMjM4NDIxNDI4NjMyNTMxODE0ODk4NzkwMDg4OTg2NjgyMTE2OTAyMzc4NDgwNTE4OTUxNDExNzg1OTk3NTk5MDMyNDYxNjExNjIyMDUyNjMzMDQ5ODYxMzc5MTQzNzI4MTM5MTUyMDkyMzI0ODc3MjMxMTYwNTgzNzA5NjE0MzA1NzQ1MjA5MjQwNjU2MDU4NjY3OTMwODEzNzYyNDY5MDc2ODc5MTk1Nzg0Nzg4NTE2NjI3MjgxMDY0NjE3MzgzMDc4Njc5MTkwODIwMzQwNTgwNDY2MjU3ODU3NjA1MTc2MTg4NTI3OTMxMDI4MTMzNTY5Njc0Mzg2ODAwMTA2MDE2MDg1Nzc0OTcyMzI1NTAyNDA2MTY0OTY0MjU2OTUxNDI3ODAxMTQzNTQxMzUzMzI0Nzg0MzA5OTY4MjIyOTU1NDk4Njk3NTAwMDUwMzc0MDg0NjIwNzQ4MTk0NzIyMTI2NjE2OTY3OTY3Mzc1NTM3Nzc5NTc4NTMwMDIxODExNTA2MTIxNjcxMDUwNDgzNTM2MjA3Njc3MTg5NDQwNjEwNzk0NTcyNzI5MTgzMzAyMjM1MDkxMDg4NTU2ODc5NTg3OTE3MDMzMzQyODcyMzg2NDQ5MTQ0NzgwMDYyNjc4MzA3NzE4MzU1MjQ5MTUxNjc5MDA1MzkxNDA5NDE4OTQxMjEzNDkxMjQyMjg2NTAwODcyMzQxNDI3Nzk1MjQ1ODYzODE2MDY2NDY3NDkxOTg4OTU3MDEwNDIxNDA3NDkyMDUxOTc0NTMwNjIxOTk1ODU0ODczNTM5Mjk3MyJdXX0sIm5vbmNlIjoiNzk3NjAzMjE3NzA5MzM1MzAwMTcwODI4In0=", + }, + "mime-type": "application/json", + }, + ], + "~service": Object { + "recipientKeys": Array [ + "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3000", + }, + }, + "metadata": Object {}, + "role": "sender", + "updatedAt": "2023-03-18T18:54:00.011Z", + }, + }, + "de4c170b-b277-4220-b9dc-7e645ff4f041": Object { + "id": "de4c170b-b277-4220-b9dc-7e645ff4f041", + "tags": Object { + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", + "schemaIssuerDid": undefined, + "schemaName": "AnotherSchema", + "schemaVersion": "5.12", + }, + "type": "AnonCredsSchemaRecord", + "value": Object { + "id": "de4c170b-b277-4220-b9dc-7e645ff4f041", + "metadata": Object {}, + "schema": Object { + "attrNames": Array [ + "name", + "height", + "age", + ], + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "name": "AnotherSchema", + "version": "5.12", + }, + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, + "e531476a-8147-44db-9e3f-2c8f97fa8f94": Object { + "id": "e531476a-8147-44db-9e3f-2c8f97fa8f94", + "tags": Object { + "associatedRecordId": "a56d83c5-2427-4f06-9a90-585623cf854a", + "messageId": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", + "messageName": "offer-credential", + "messageType": "https://didcomm.org/issue-credential/2.0/offer-credential", + "protocolMajorVersion": "2", + "protocolMinorVersion": "0", + "protocolName": "issue-credential", + "role": "sender", + "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + }, + "type": "DidCommMessageRecord", + "value": Object { + "associatedRecordId": "a56d83c5-2427-4f06-9a90-585623cf854a", + "createdAt": "2023-03-18T18:54:00.005Z", + "id": "e531476a-8147-44db-9e3f-2c8f97fa8f94", + "message": Object { + "@id": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", + "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", + "credential_preview": Object { + "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", + "attributes": Array [ + Object { + "name": "name", + "value": "John", + }, + Object { + "name": "age", + "value": "99", + }, + Object { + "name": "height", + "value": "180", + }, + ], + }, + "formats": Array [ + Object { + "attach_id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "format": "hlindy/cred-abstract@v2.0", + }, + ], + "offers~attach": Array [ + Object { + "@id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", + "data": Object { + "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6QW5vdGhlclNjaGVtYTo1LjEyIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY2OlRBRzIyMjIiLCJrZXlfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjQwMTQ0MTA0NDg3MjM0NDU2MTc1NzYwMDc1NzMxNjUyNjg1MTk0MjE5MTk5NDk3NDczNTM4NjU4ODM3OTIyODMzNTEzMDg0Nzk2MDQ5IiwieHpfY2FwIjoiMzgxOTQyMjM1Mzc3MzYwODEyNjY0MDA4MjYzNDQxMDg2MDMwOTMyMjAxNzgzMjM3ODQxODQ5NDg3ODk2ODg1MTYwODY2MTY1MDM3NzI2MTIxNjU0MjcwOTg5NDY3NjAzNDExOTAzODk4MzUwMDAzNDIwODg3MzI4NTUwMTY2MTI1ODMyMjAxOTQzMTkwNzAxMDU4NTAwMDE5ODM1NjA1ODczNDYzOTkwODg3NzQ0NjY3MzU0MjM2Njc3MzcyODg0ODQyNjE5NTEwMTUwOTA2MjI1OTMzMjc1ODEyNjg2NDg3NTg5NjY3ODI3MjAwODcwOTQ0OTIyMjk5MzI3OTI4MDQ1MTk1OTIwMDI3NTc0MDQwNDA4ODU5MzAwMzY1MDYwODc3Nzg2ODkwOTE1MDU5NTA2ODc1OTI0NzE2OTI1MDM2MTc4Njg2NDE5NTYyMzcwODI4MTMzODY2Nzg3NzkyMDcwNjAyNDQzNTkzMTk2NzEzNzcyNDM2NTYzODI0MzkwMDIyNzg4MjU2MzA4NjU4OTc0OTEzMTk1ODYxODUwMTQ3ODE1Mjg5NzQwOTA4NDk1MjQ3NTAyNjYyNDc3NzQ2NTI5ODA3Mzg0OTgxODI5MDc3NTQ4OTI2NzExMDkzNzQ5MjM1ODU4NjUwNDc5NzE5NDI4MzUwMzAwNzUyNjQ0OTg1MTQ5MTMxNjA1NjUzMDIxMDYxNzkwMjY3MzQyNTY4NTkyNTY2MTQ0MDM5NzY4OTg0NTMyNDMzNzk0MzUzNjQ2Nzg1MjA3NDgzOTk2ODQ0OTcxNTgzNzY3NDQ5ODYyODgxMjMxMjI1MzM4MzAzMTQ4NzA0ODczMDEzNDM3MDgyNzY1MTk4OTY2MzE5NTM0OTkyNjk4MzMzMDQ0MDI3MjIyNTYyNTIzNzk3ODk5Mjk2MTQ1NDU5IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiOTE5MTc5NzQ4MTE5NTg5MTY3Njc5MjQxODk5NzY0ODIwNTk0MDc5OTQxNzIzOTgzOTYyNzQ1MTczODM0NDQxMDQ3MjU4MDcyOTE4OTUzNjIzOTQ4MDMyNzI2NDgyNzI2MTEwOTk2Mjk3MDU3NTYwNjcwNzAxOTU1MTkxNDc0NjM0MzQ0ODMxMzg3NTk4NzI2MzMxMjc0NjI4NDU3Njk5NzczMDA1NDMwMDIxNzMwMzg4MzcwMTEyMjc3MzI2MzU4OTgwMTA3ODIzNzUzODc3MTU0NjIwMDkzMjE5MjYyNjAxNDM2NzMyNTgzNDI4Nzc4NDA4OTc0NTQyNzkzMDk0NTQ5MTczOTA3MzQ3OTUxNTc1NjM5NzU2NDg5MTA0Mzk0MTY3NzExMzY1MjM3OTI1MjAwNjk4OTg5NTI5MTQ3OTIzNTYzNDMyODgyMzgwMTg0NzU0NzkzODMwMTE3MTQ1MDAwMTI0NDYxNjkzOTcxMDQ5MjgzNDk1NTE4MDQxMDc5ODUyMzAwMjk0NDM1MjYzOTIwNDU0NTU3MzUxNDQ1MDM3NDI4MDg3OTk2Mzg2NjY3NjU3Nzk5OTYyNzQzNzIyNzA3NzczOTEzMzc0NzIxODUyNTQ3MjkwMTY5MjI5NTAzMTQxOTMwODYzNTk4NTExNjc4NDEyMDE0MzE2MDM2MzYxMzczNDcwOTQwMDEyODcwMDgwMDA2MzE0NzYxNzYzNzUyNzYwODk5MTQ3NzA1MTA0NzQyNjAxNjkxMzMxNjkzMDIwMjg2MjA2NzQ2NzE0MzI3NjU2MjA2NTMzMjk3NDg4MjU2NTM2NTQ3MzY4MjM2OTQ2MDM5NzAzMzc0OTMzNTE0NTc2NDg2NjQyNTY4MjgyNTY2MjMyNDU1NTU5MDY4MzE3NzU5NDM0ODU4NTI3MDg2NjQ0Il0sWyJoZWlnaHQiLCI5MjMwMzkyNDc1NjI4ODc1MjA4OTM0NjM0NzE4MjYzNzA4MDIzOTI1MDU0NjY2NDgzMzgxMzIyMzc3MDg1MjMxMjU4MTM4MzgwOTU1NTk3NDQxNTEyOTYwNDA2MjI3MjUwODgyNjA3NjExMDkwODk3MTM1NDcxNzAwMDIzNDcwOTM2ODg4MDE3NDY5Nzk0ODYzNDk4NzUyNTI3Njc3MjMwMTEwNzg0ODQzNzI0NDUyNTUzODYyOTA2MzM5MDc0OTIzNDU4NTQ3NDYzODcwNzU3OTg5MzMxNzk4OTI2MjM4MjUxMTM2NTYzNjM2MjIyOTQwNDkwMzY3MjQ2OTg0OTU2NTE5MTAzODcwNDE0MDM5NzM2MDE2MDY5MzA2NjQ0NjQzODI4OTgxMTE3OTM3NzYyNDAzODY1Mjc1MDU5MjEyOTY2NzIxOTU3MzM0MTM2ODEyMDI0OTE0MzA4MzAxMzk5MzM4NzMyOTIzNTA0MjA5MDM5ODMxMTc5NjU1NTkyNjg0MjMyMTIzMTI2Mjc4ODQzNDMyOTUwMTk1Mjg3MzE4ODI3NTM2MTMwNDQ3NzM3MTgwMjk3MDE0ODEzNDg3NDQyOTg2NjQ1NzQyNjEyMzE5NzQxNDY2MDMyNTg5OTU0NzYwNjE4MDU0MDUxMjAzMTE1NTAxNDcxNDExMzg3NzU0NDk5MzAwNTU4MTc5NjM5NDAxOTM0NTAzMTMyMDEzMjAzOTg2NzkyMTEzMDAzNTkwODg1NTc3NjgyMzU2NDY3MjA5NTUwNjQxODQxMDYyNTkzNDYyODIwODg3NzgxNDYyODM3ODkzODcxNDM4MzM3Mjc5MTcwMTExMTQ5MTU4NDMzNDE0ODI1NTkyNjcyODU2MzM5OTM4NTgyODg2NzM3OTIwMjc1MzI0MjEwMTUzMjE5MjI2OTYiXSxbImFnZSIsIjkxNTg1ODk3NDkwNzE0ODA3OTY2MDYzOTg5MjE1NTMxNDkyOTQwMDI5NDcyMTM4MjgwNjcxNjcyMjQ0NjY5MDc5NzIyNTQyMDU0NTU3NjY0MTcxMDI1NzM1NjQ4NTIwMTM4ODQ4ODAxNzIyMTc4MTcxMTA5NTc0MTMyNTExMzM1MDEwNTc5NzExMzcyODM5MjI3MDExOTg4MTUyMTEwMzI4MTE5MjkyMjI4NjM3MDU4MDQ3NzYwODYwOTQ0NTY3MzQxMjY4MTY4Mjk3NjE5MDM2ODEwMjYwODM2NDI1NDkwMzU3NjE4NzM4NTYxNTY2MTUxODQ3MzIxNzM1MjQ5ODk1MDU5NTY2OTQxODI5MjE0Nzc0MTA0NzYyNTQwMjcyMjk2NjE1NTE3NjUwMDcyNDQyMTI0NjY5MDEzMTc1ODAyMDk5MDQxMzk3MzE5ODQ0OTA2MDgwOTYxNTcyMTcwNjg2NzgzNDM1Mjg2MDUyMzE5ODY3ODExMDE5MjAxMDYwODM2OTM3Mzc0MzM0NDM5MTQxMDAzMTI3NTcyNjgzNTgwODI0OTkwOTg3MjE5MzU4NzkzOTM2NTU4Nzk3MjI0MDQzNTM1ODA5NzMyNzgxMjE1NzEwNjI1MjQzODYwNTk4MTk0MjU2MjAwODkwOTA3ODAzMDcyMTAzNzc3MzkwODk4MDczOTgyNjY3Njc1ODg0MjI3MjU0Mzc2OTI5Mjg3ODQyNDE0MTE0MjcwNDQwMTEzNDUxNjk4NzE5Nzc5NjQyNTI4MDA4NDM3Mzk5NjI0NTE3OTM4Nzg5MDc3ODE5ODA0MDY5MzcxOTM0NzExMTIyNTQyODU0OTg4MDA0Mjc4NDkwMjAxNTk2NjE0MjUwODc3NDYxMDczNjc3NTUzNzYxMTMyMTA5Nzg3NTQ2ODE1ODk5Njc2NCJdLFsibmFtZSIsIjYyNzgwNTIwMTM3MzI3NTUzMDc3MDg4NTE4NDg1NDYyMTA0NjEzMjEyNzY3ODUwMzYwNTc3NDQ4MDUxNTk5MTMxMTM1NTI2NzQ3Nzc2NzMzMDg1MDMwODcyMDE1OTM2MTI2NzE0MTIxMDgxMzg3ODU2MTkwMTkzMzI3ODY3OTE0NTEzODM2NTQ1OTY4Mjg1NTc5ODEyODMxMDI4ODc2Nzg1NzI3OTQ2MTEwNzg5Mzc0MjcyODgzMzkyOTgwNDkwODk3NDkwMTc5MDQ0ODM0NTgwMzQ2ODY4NDI2ODc0ODU4NTY1OTg4NTUyMDcwNjI1NDczNjM4MDM3Njc5NTU1NTk2MzE5MTc3Nzc5OTcxMTIxMjQzMjgyMTIyOTQ2NjY0ODMxOTgxMTg3MzQ3MzcyMjkxMjYwOTM3MzkzNDA1ODk5OTI0NjM4MzE3ODI5MDczODMxMjI4ODc1Njg5MTcyMTg4NjIyMDI5NzcxNzM5MTQ5NDY2Mzg3NTM5NjkyNDQ5NDU5MjczNDI5NzM5MjMzNjkyNTEzNDA5OTkyNDgxNTQ4ODk0NjAzNjM3MTYzNjA4MTM0MTAzMTk3Nzc3NTM4OTYwMDcyMjcyMzYyNzM4NDM1MTM3MDcyNzIxMjExMDYxNTg4MDE3ODczODg3MTEwNDA2OTk1NDQ4ODIwMDEzMDA5MjgyMzk0OTczMDMwMDI5MTY3NjQ5NzY1OTI1MTUxMzY4NTg5OTkyNzMyMDE1ODAwNjAzNzYxOTI3MTg3MDM4MDkxNDY3MDE1MjA3MzIwNDczMDM0NDA3MDIyNDA0NjQ4MTI0NTk2NjQwNjU1NjY1MTIzMzY5Njc0ODI2NDE3MjE2ODUxNTM4Njc1NTM3NzAwOTg4MTQzNzE1NTE3NzMwMTM4NjA4NzkxMjcyMzM0MDUyMzY4OCJdXX0sIm5vbmNlIjoiNDE0MzQ4Njg0NDk2OTAxNjkyMjI2OTY0In0=", + }, + "mime-type": "application/json", + }, + ], + "~service": Object { + "recipientKeys": Array [ + "DXubCT3ahg6N7aASVFVei1GNUTecne8m3iRWjVNiAw31", + ], + "routingKeys": Array [], + "serviceEndpoint": "http://localhost:3000", + }, + "~thread": Object { + "thid": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", + }, + }, + "metadata": Object {}, + "role": "sender", + "updatedAt": "2023-03-18T18:54:00.014Z", + }, + }, + "fcdba9cd-3132-4e46-9677-f78c5a146cf0": Object { + "id": "fcdba9cd-3132-4e46-9677-f78c5a146cf0", + "tags": Object { + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", + "schemaIssuerDid": undefined, + "schemaName": "Test Schema", + "schemaVersion": "5.0", + }, + "type": "AnonCredsSchemaRecord", + "value": Object { + "id": "fcdba9cd-3132-4e46-9677-f78c5a146cf0", + "metadata": Object {}, + "schema": Object { + "attrNames": Array [ + "name", + "age", + ], + "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", + "name": "Test Schema", + "version": "5.0", + }, + "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", + "updatedAt": "2023-03-19T22:50:20.522Z", + }, + }, +} +`; diff --git a/packages/anoncreds/src/utils/metadata.ts b/packages/anoncreds/src/utils/metadata.ts index 1d8448ebfa..c8c1c245fc 100644 --- a/packages/anoncreds/src/utils/metadata.ts +++ b/packages/anoncreds/src/utils/metadata.ts @@ -6,14 +6,14 @@ * * MUST be used with {@link AnonCredsCredentialMetadata} */ -export const AnonCredsCredentialMetadataKey = '_anonCreds/anonCredsCredential' +export const AnonCredsCredentialMetadataKey = '_anoncreds/credential' /** * Metadata key for strong metadata on an AnonCreds credential request. * * MUST be used with {@link AnonCredsCredentialRequestMetadata} */ -export const AnonCredsCredentialRequestMetadataKey = '_anonCreds/anonCredsCredentialRequest' +export const AnonCredsCredentialRequestMetadataKey = '_anoncreds/credentialRequest' /** * Metadata for an AnonCreds credential that will be stored diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 4084d5a415..1b51e55d09 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -14,6 +14,7 @@ import type { import type { KeyEntryObject, Session } from '@hyperledger/aries-askar-shared' import { + WalletExportPathExistsError, WalletKeyExistsError, isValidSeed, isValidPrivateKey, @@ -317,6 +318,13 @@ export class AskarWallet implements Wallet { // Close this wallet before copying await this.close() + // Export path already exists + if (await this.fileSystem.exists(destinationPath)) { + throw new WalletExportPathExistsError( + `Unable to create export, wallet export at path '${exportConfig.path}' already exists` + ) + } + // Copy wallet to the destination path await this.fileSystem.copyFile(sourcePath, destinationPath) @@ -332,6 +340,8 @@ export class AskarWallet implements Wallet { await this._open(this.walletConfig) } catch (error) { + if (error instanceof WalletExportPathExistsError) throw error + const errorMessage = `Error exporting wallet '${this.walletConfig.id}': ${error.message}` this.logger.error(errorMessage, { error, diff --git a/packages/core/src/error/IndySdkError.ts b/packages/core/src/error/IndySdkError.ts deleted file mode 100644 index f67a0721f6..0000000000 --- a/packages/core/src/error/IndySdkError.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { IndyError } from '../utils/indyError' - -import { AriesFrameworkError } from './AriesFrameworkError' - -export class IndySdkError extends AriesFrameworkError { - public constructor(indyError: IndyError, message?: string) { - const base = `${indyError.name}(${indyError.indyName}): ${indyError.message}` - - super(message ? `${message}: ${base}` : base, { cause: indyError }) - } -} diff --git a/packages/core/src/error/index.ts b/packages/core/src/error/index.ts index 7122734300..45ebd04bd7 100644 --- a/packages/core/src/error/index.ts +++ b/packages/core/src/error/index.ts @@ -1,6 +1,5 @@ export * from './AriesFrameworkError' export * from './RecordNotFoundError' export * from './RecordDuplicateError' -export * from './IndySdkError' export * from './ClassValidationError' export * from './MessageSendingError' diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts index b19448a771..1fcbc44ed8 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-connectionless-credentials.e2e.test.ts @@ -165,7 +165,7 @@ describe('V2 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { credentialDefinitionId, }, }, @@ -186,7 +186,7 @@ describe('V2 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { credentialDefinitionId, }, }, @@ -246,7 +246,7 @@ describe('V2 Connectionless Credentials', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { credentialDefinitionId: credentialDefinitionId, }, }, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts index 8d1b7cda57..88422a79c4 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2-credentials-auto-accept.e2e.test.ts @@ -90,7 +90,7 @@ describe('V2 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -126,8 +126,8 @@ describe('V2 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredentialRequest': expect.any(Object), - '_anonCreds/anonCredsCredential': { + '_anoncreds/credentialRequest': expect.any(Object), + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -232,8 +232,8 @@ describe('V2 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredentialRequest': expect.any(Object), - '_anonCreds/anonCredsCredential': { + '_anoncreds/credentialRequest': expect.any(Object), + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -254,7 +254,7 @@ describe('V2 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredential': { + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, @@ -311,8 +311,8 @@ describe('V2 Credentials Auto Accept', () => { createdAt: expect.any(Date), metadata: { data: { - '_anonCreds/anonCredsCredentialRequest': expect.any(Object), - '_anonCreds/anonCredsCredential': { + '_anoncreds/credentialRequest': expect.any(Object), + '_anoncreds/credential': { schemaId, credentialDefinitionId: credentialDefinitionId, }, diff --git a/packages/core/src/plugins/Module.ts b/packages/core/src/plugins/Module.ts index bca1bc5ff7..183a47c7f8 100644 --- a/packages/core/src/plugins/Module.ts +++ b/packages/core/src/plugins/Module.ts @@ -1,12 +1,18 @@ import type { DependencyManager } from './DependencyManager' import type { AgentContext } from '../agent' import type { FeatureRegistry } from '../agent/FeatureRegistry' +import type { Update } from '../storage/migration/updates' import type { Constructor } from '../utils/mixins' export interface Module { api?: Constructor register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void initialize?(agentContext: AgentContext): Promise + + /** + * List of updates that should be executed when the framework version is updated. + */ + updates?: Update[] } export interface ApiModule extends Module { diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index 3c08c2fe64..7a99b8d408 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -1,11 +1,12 @@ -import type { UpdateConfig, UpdateToVersion } from './updates' +import type { Update, UpdateConfig, UpdateToVersion } from './updates' import type { BaseAgent } from '../../agent/BaseAgent' +import type { Module } from '../../plugins' import type { FileSystem } from '../FileSystem' import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' -import { isIndyError } from '../../utils/indyError' import { isFirstVersionEqualToSecond, isFirstVersionHigherThanSecond, parseVersionString } from '../../utils/version' +import { WalletExportPathExistsError } from '../../wallet/error' import { WalletError } from '../../wallet/error/WalletError' import { StorageUpdateService } from './StorageUpdateService' @@ -146,11 +147,42 @@ export class UpdateAssistant = BaseAgent> { try { for (const update of neededUpdates) { + const registeredModules = Object.values(this.agent.dependencyManager.registeredModules) + const modulesWithUpdate: Array<{ module: Module; update: Update }> = [] + + // Filter modules that have an update script for the current update + for (const registeredModule of registeredModules) { + const moduleUpdate = registeredModule.updates?.find( + (module) => module.fromVersion === update.fromVersion && module.toVersion === update.toVersion + ) + + if (moduleUpdate) { + modulesWithUpdate.push({ + module: registeredModule, + update: moduleUpdate, + }) + } + } + this.agent.config.logger.info( - `Starting update of agent storage from version ${update.fromVersion} to version ${update.toVersion}` + `Starting update of agent storage from version ${update.fromVersion} to version ${update.toVersion}. Found ${modulesWithUpdate.length} extension module(s) with update scripts` ) await update.doUpdate(this.agent, this.updateConfig) + this.agent.config.logger.info( + `Finished update of core agent storage from version ${update.fromVersion} to version ${update.toVersion}. Starting update of extension modules` + ) + + for (const moduleWithUpdate of modulesWithUpdate) { + this.agent.config.logger.info( + `Starting update of extension module ${moduleWithUpdate.module.constructor.name} from version ${moduleWithUpdate.update.fromVersion} to version ${moduleWithUpdate.update.toVersion}` + ) + await moduleWithUpdate.update.doUpdate(this.agent, this.updateConfig) + this.agent.config.logger.info( + `Finished update of extension module ${moduleWithUpdate.module.constructor.name} from version ${moduleWithUpdate.update.fromVersion} to version ${moduleWithUpdate.update.toVersion}` + ) + } + // Update the framework version in storage await this.storageUpdateService.setCurrentStorageVersion(this.agent.context, update.toVersion) this.agent.config.logger.info( @@ -173,9 +205,9 @@ export class UpdateAssistant = BaseAgent> { } } catch (error) { // Backup already exists at path - if (error instanceof AriesFrameworkError && isIndyError(error.cause, 'CommonIOError')) { + if (error instanceof WalletExportPathExistsError) { const backupPath = this.getBackupPath(updateIdentifier) - const errorMessage = `Error updating storage with updateIdentifier ${updateIdentifier} because of an IO error. This is probably because the backup at path ${backupPath} already exists` + const errorMessage = `Error updating storage with updateIdentifier ${updateIdentifier} because the backup at path ${backupPath} already exists` this.agent.config.logger.fatal(errorMessage, { error, updateIdentifier, diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index e1d0521dd5..a66ef7c832 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -62,7 +62,7 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { storageService.records = JSON.parse(aliceCredentialRecordsString) expect(await updateAssistant.isUpToDate()).toBe(false) - expect(await updateAssistant.getNeededUpdates()).toEqual([ + expect(await updateAssistant.getNeededUpdates('0.3.1')).toEqual([ { fromVersion: '0.2', toVersion: '0.3', diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index 47cf43bff6..e8479803fc 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -12,21 +12,24 @@ import { DependencyManager } from '../../../plugins' import * as uuid from '../../../utils/uuid' import { UpdateAssistant } from '../UpdateAssistant' -const backupDate = new Date('2022-01-21T22:50:20.522Z') +const backupDate = new Date('2023-03-18T22:50:20.522Z') jest.useFakeTimers().setSystemTime(backupDate) const walletConfig = { - id: `Wallet: 0.3 Update`, - key: `Key: 0.3 Update`, + id: `Wallet: 0.4 Update`, + key: `Key: 0.4 Update`, } -describe('UpdateAssistant | v0.3 - v0.3.1', () => { - it(`should correctly update the did records`, async () => { +describe('UpdateAssistant | v0.3.1 - v0.4', () => { + it(`should correctly update the did records and remove cache records`, async () => { // We need to mock the uuid generation to make sure we generate consistent uuids for the new records created. let uuidCounter = 1 const uuidSpy = jest.spyOn(uuid, 'uuid').mockImplementation(() => `${uuidCounter++}-4e4f-41d9-94c4-f49351b811f1`) - const aliceDidRecordsString = readFileSync(path.join(__dirname, '__fixtures__/alice-8-dids-0.3.json'), 'utf8') + const aliceDidRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/alice-2-sov-dids-one-cache-record-0.3.json'), + 'utf8' + ) const dependencyManager = new DependencyManager() const storageService = new InMemoryStorageService() @@ -59,10 +62,10 @@ describe('UpdateAssistant | v0.3 - v0.3.1', () => { storageService.records = JSON.parse(aliceDidRecordsString) expect(await updateAssistant.isUpToDate()).toBe(false) - expect(await updateAssistant.getNeededUpdates()).toEqual([ + expect(await updateAssistant.getNeededUpdates('0.4')).toEqual([ { - fromVersion: '0.3', - toVersion: '0.3.1', + fromVersion: '0.3.1', + toVersion: '0.4', doUpdate: expect.any(Function), }, ]) diff --git a/packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-sov-dids-one-cache-record-0.3.json b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-sov-dids-one-cache-record-0.3.json new file mode 100644 index 0000000000..1edebe9e11 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__fixtures__/alice-2-sov-dids-one-cache-record-0.3.json @@ -0,0 +1,87 @@ +{ + "STORAGE_VERSION_RECORD_ID": { + "value": { + "_tags": {}, + "metadata": {}, + "id": "STORAGE_VERSION_RECORD_ID", + "createdAt": "2023-03-18T18:35:02.888Z", + "storageVersion": "0.3.1", + "updatedAt": "2023-03-18T18:35:02.888Z" + }, + "id": "STORAGE_VERSION_RECORD_ID", + "type": "StorageVersionRecord", + "tags": {} + }, + "DID_POOL_CACHE": { + "value": { + "metadata": {}, + "id": "DID_POOL_CACHE", + "createdAt": "2023-03-18T18:53:44.165Z", + "entries": [ + { + "key": "A4CYPASJYRZRt98YWrac3H", + "value": { + "nymResponse": { + "did": "A4CYPASJYRZRt98YWrac3H", + "verkey": "5wFaN9wUdLipt6rFjhep9pp2aanJKqe5MywkEAHqhRNS", + "role": "101" + }, + "poolId": "bcovrin:test2" + } + } + ], + "updatedAt": "2023-03-18T18:53:44.166Z" + }, + "id": "DID_POOL_CACHE", + "type": "CacheRecord", + "tags": {} + }, + "8168612b-73d1-4917-9a61-84e8102988f0": { + "value": { + "_tags": { + "recipientKeyFingerprints": [], + "qualifiedIndyDid": "did:indy:bcovrin:test:8DFqUo6UtQLLZETE7Gm29k" + }, + "metadata": {}, + "id": "8168612b-73d1-4917-9a61-84e8102988f0", + "did": "did:sov:8DFqUo6UtQLLZETE7Gm29k", + "role": "created", + "createdAt": "2023-03-18T18:35:04.191Z", + "updatedAt": "2023-03-18T18:35:04.191Z" + }, + "id": "8168612b-73d1-4917-9a61-84e8102988f0", + "type": "DidRecord", + "tags": { + "recipientKeyFingerprints": [], + "qualifiedIndyDid": "did:indy:bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", + "role": "created", + "method": "sov", + "did": "did:sov:8DFqUo6UtQLLZETE7Gm29k", + "methodSpecificIdentifier": "8DFqUo6UtQLLZETE7Gm29k" + } + }, + "4993c740-5cd9-4c79-a7d8-23d1266d31be": { + "value": { + "_tags": { + "recipientKeyFingerprints": [], + "qualifiedIndyDid": "did:indy:bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c" + }, + "metadata": {}, + "id": "4993c740-5cd9-4c79-a7d8-23d1266d31be", + "did": "did:sov:Pow4pdnPgTS7JAXvWkoF2c", + "role": "created", + "createdAt": "2023-03-18T18:35:07.208Z", + "updatedAt": "2023-03-18T18:35:07.208Z" + }, + "id": "4993c740-5cd9-4c79-a7d8-23d1266d31be", + "type": "DidRecord", + "tags": { + "recipientKeyFingerprints": [], + "qualifiedIndyDid": "did:indy:bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", + "role": "created", + "method": "sov", + "did": "did:sov:Pow4pdnPgTS7JAXvWkoF2c", + "methodSpecificIdentifier": "Pow4pdnPgTS7JAXvWkoF2c" + } + } +} diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap index 8d76122ef4..daefd3a533 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap @@ -121,7 +121,7 @@ Object { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, - "storageVersion": "0.3.1", + "storageVersion": "0.4", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, @@ -757,7 +757,7 @@ Object { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, - "storageVersion": "0.3.1", + "storageVersion": "0.4", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, @@ -885,7 +885,7 @@ Object { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, - "storageVersion": "0.3.1", + "storageVersion": "0.4", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap index d75c5d4c22..c96903e554 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap @@ -1,513 +1,47 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UpdateAssistant | v0.3 - v0.3.1 should correctly update the did records 1`] = ` +exports[`UpdateAssistant | v0.3.1 - v0.4 should correctly update the did records and remove cache records 1`] = ` Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "1-4e4f-41d9-94c4-f49351b811f1", + "4993c740-5cd9-4c79-a7d8-23d1266d31be": Object { + "id": "4993c740-5cd9-4c79-a7d8-23d1266d31be", "tags": Object { - "did": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", + "did": "did:indy:bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", - "recipientKeyFingerprints": Array [ - "z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU", - ], + "method": "indy", + "methodSpecificIdentifier": "bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", + "qualifiedIndyDid": undefined, + "recipientKeyFingerprints": Array [], "role": "created", }, "type": "DidRecord", "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU", - ], - "role": "created", - }, - "createdAt": "2022-12-27T13:51:21.344Z", - "did": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#57a05508-1d1c-474c-8c68-1afcf3188720", - "publicKeyBase58": "BDqjd9f7HnsSssQ2u14gym93UT5Lbde1tjbYPysB9j96", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#59d2ce6f-c9fc-49c6-a8f6-eab14820b028", - "publicKeyBase58": "avivQP6GvWj6cBbxbXSSZnZTA4tGsvQ5DB9FXm45tZt", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#57a05508-1d1c-474c-8c68-1afcf3188720", - ], - "routingKeys": Array [ - "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop", - ], - "serviceEndpoint": "ws://ssi.mediator.com", - "type": "did-communication", - }, - ], - }, - "id": "1-4e4f-41d9-94c4-f49351b811f1", + "createdAt": "2023-03-18T18:35:07.208Z", + "did": "did:indy:bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", + "id": "4993c740-5cd9-4c79-a7d8-23d1266d31be", "metadata": Object {}, "role": "created", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-03-18T22:50:20.522Z", }, }, - "2-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "2-4e4f-41d9-94c4-f49351b811f1", + "8168612b-73d1-4917-9a61-84e8102988f0": Object { + "id": "8168612b-73d1-4917-9a61-84e8102988f0", "tags": Object { - "did": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", + "did": "did:indy:bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", - "recipientKeyFingerprints": Array [ - "z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz", - ], - "role": "received", - }, - "type": "DidRecord", - "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz", - ], - "role": "received", - }, - "createdAt": "2022-12-27T13:51:51.414Z", - "did": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#6173438e-09a5-4b1e-895a-0563f5a169b7", - "publicKeyBase58": "Ft541AjJwHLLky1i26amoJsREix9WkWeM33u7K9Czo2c", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#a0448ee3-093a-4b16-bc59-8bf8559d60a5", - "publicKeyBase58": "JCfZJ72mtgGE9xuekJKV6yoAGzLgQCNkdHKkct8uaNKb", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#6173438e-09a5-4b1e-895a-0563f5a169b7", - ], - "routingKeys": Array [], - "serviceEndpoint": "http://ssi.verifier.com", - "type": "did-communication", - }, - ], - }, - "id": "2-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, - "role": "received", - "updatedAt": "2022-01-21T22:50:20.522Z", - }, - }, - "3-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "3-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "did": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "recipientKeyFingerprints": Array [ - "z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G", - ], - "role": "created", - }, - "type": "DidRecord", - "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G", - ], - "role": "created", - }, - "createdAt": "2022-12-27T13:50:32.815Z", - "did": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", - "publicKeyBase58": "Ge4aWoosGdqcGer1Peg3ocQnC7AW3o6o4aYhq8BrsxLt", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#c30ea91b-5f49-461f-b0bd-046f947ef668", - "publicKeyBase58": "HKBdBGRK8uxgCwge2QHPBzVuuayEQFhC2LM3g1fzMFGE", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", - ], - "routingKeys": Array [], - "serviceEndpoint": "didcomm:transport/queue", - "type": "did-communication", - }, - ], - }, - "id": "3-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, - "role": "created", - "updatedAt": "2022-01-21T22:50:20.522Z", - }, - }, - "4-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "4-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "did": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "recipientKeyFingerprints": Array [ - "z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE", - ], - "role": "created", - }, - "type": "DidRecord", - "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE", - ], - "role": "created", - }, - "createdAt": "2022-12-27T13:51:50.193Z", - "did": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#22219a28-b52a-4024-bc0f-62d3969131fd", - "publicKeyBase58": "HAeF9FMw5dre1jDFqQ9WwQHQfaRKWaLaVrUqqmdQ5znr", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#0f9535d1-9c9c-4491-be1e-1628f365b513", - "publicKeyBase58": "FztF8HCahTeL9gYHoHnDFo6HruwnKB19ZtbHFbLndAmE", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#22219a28-b52a-4024-bc0f-62d3969131fd", - ], - "routingKeys": Array [ - "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop", - ], - "serviceEndpoint": "ws://ssi.mediator.com", - "type": "did-communication", - }, - ], - }, - "id": "4-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, - "role": "created", - "updatedAt": "2022-01-21T22:50:20.522Z", - }, - }, - "5-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "5-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "did": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "recipientKeyFingerprints": Array [ - "z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh", - ], - "role": "received", - }, - "type": "DidRecord", - "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh", - ], - "role": "received", - }, - "createdAt": "2022-12-27T13:50:44.957Z", - "did": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", - "publicKeyBase58": "AZgwNkbN9K4aMRwxB6bLyxaoBx4N2W2n2aLEWUQ9GDuK", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#bf888dc5-0f54-4e71-9058-c43bebfc7d01", - "publicKeyBase58": "3Q8wpgxdCVdJfznYERZR1r9eVnCy7oxpjzGVucDLF2tG", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", - ], - "routingKeys": Array [], - "serviceEndpoint": "ws://ssi.mediator.com", - "type": "did-communication", - }, - ], - }, - "id": "5-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, - "role": "received", - "updatedAt": "2022-01-21T22:50:20.522Z", - }, - }, - "6-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "6-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "did": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "recipientKeyFingerprints": Array [ - "z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH", - ], - "role": "received", - }, - "type": "DidRecord", - "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH", - ], - "role": "received", - }, - "createdAt": "2022-12-27T13:50:34.057Z", - "did": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", - "publicKeyBase58": "CxjNF9dwPknoDmtoWYKCvdoSYamFBJw6rHjsan6Ht38u", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#c0917f9f-1102-4f13-800c-ff7b522999eb", - "publicKeyBase58": "Eso3A6AmL5qiWr9syqHx8NgdQ8EMkYcitmrW5G7bX9uU", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", - ], - "routingKeys": Array [], - "serviceEndpoint": "ws://ssi.issuer.com", - "type": "did-communication", - }, - ], - }, - "id": "6-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, - "role": "received", - "updatedAt": "2022-01-21T22:50:20.522Z", - }, - }, - "7-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "7-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "did": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "recipientKeyFingerprints": Array [ - "z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM", - ], - "role": "received", - }, - "type": "DidRecord", - "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM", - ], - "role": "received", - }, - "createdAt": "2022-12-27T13:51:22.817Z", - "did": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#5ea98568-dfcd-4614-9495-ba95ec2665d3", - "publicKeyBase58": "EcYfDpf1mWoA7soZohD8e9uf2dj9VLH2SjuYDhq5Xd3y", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#506e5ead-ddbc-44ef-848b-6593a692a916", - "publicKeyBase58": "EeQHhb6CqWGrQR9PfWpS1L8CedsbK2mZfPdaxaHN4s8b", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#5ea98568-dfcd-4614-9495-ba95ec2665d3", - ], - "routingKeys": Array [], - "serviceEndpoint": "http://ssi.verifier.com", - "type": "did-communication", - }, - ], - }, - "id": "7-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, - "role": "received", - "updatedAt": "2022-01-21T22:50:20.522Z", - }, - }, - "8-4e4f-41d9-94c4-f49351b811f1": Object { - "id": "8-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { - "did": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "legacyUnqualifiedDid": undefined, - "method": "peer", - "methodSpecificIdentifier": "1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "recipientKeyFingerprints": Array [ - "z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma", - ], + "method": "indy", + "methodSpecificIdentifier": "bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", + "qualifiedIndyDid": undefined, + "recipientKeyFingerprints": Array [], "role": "created", }, "type": "DidRecord", "value": Object { - "_tags": Object { - "method": "peer", - "recipientKeyFingerprints": Array [ - "z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma", - ], - "role": "created", - }, - "createdAt": "2022-12-27T13:50:43.937Z", - "did": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "didDocument": Object { - "@context": Array [ - "https://w3id.org/did/v1", - ], - "authentication": Array [ - Object { - "controller": "#id", - "id": "#12b8b7d4-87b9-4638-a929-f98df2f1f566", - "publicKeyBase58": "FBRBjETqkDPcmXncUi3DfthYtRTBtNZEne7TQyS9kozC", - "type": "Ed25519VerificationKey2018", - }, - ], - "id": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "keyAgreement": Array [ - Object { - "controller": "#id", - "id": "#77b9cf84-2441-419f-b295-945d06e29edc", - "publicKeyBase58": "EgsQArrmCUru9MxR1RNNiomnMFz6E3ia2GfjVvoCjAWY", - "type": "X25519KeyAgreementKey2019", - }, - ], - "service": Array [ - Object { - "id": "#inline-0", - "priority": 0, - "recipientKeys": Array [ - "#12b8b7d4-87b9-4638-a929-f98df2f1f566", - ], - "routingKeys": Array [], - "serviceEndpoint": "didcomm:transport/queue", - "type": "did-communication", - }, - ], - }, - "id": "8-4e4f-41d9-94c4-f49351b811f1", + "createdAt": "2023-03-18T18:35:04.191Z", + "did": "did:indy:bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", + "id": "8168612b-73d1-4917-9a61-84e8102988f0", "metadata": Object {}, "role": "created", - "updatedAt": "2022-01-21T22:50:20.522Z", + "updatedAt": "2023-03-18T22:50:20.522Z", }, }, "STORAGE_VERSION_RECORD_ID": Object { @@ -515,11 +49,11 @@ Object { "tags": Object {}, "type": "StorageVersionRecord", "value": Object { - "createdAt": "2022-09-08T19:35:53.872Z", + "createdAt": "2023-03-18T18:35:02.888Z", "id": "STORAGE_VERSION_RECORD_ID", "metadata": Object {}, - "storageVersion": "0.3.1", - "updatedAt": "2022-01-21T22:50:20.522Z", + "storageVersion": "0.4", + "updatedAt": "2023-03-18T22:50:20.522Z", }, }, } diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap new file mode 100644 index 0000000000..29d6fd05da --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap @@ -0,0 +1,196 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpdateAssistant | Backup | Aries Askar should create a backup 1`] = ` +Array [ + Object { + "_tags": Object { + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "autoAcceptCredential": "contentApproved", + "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", + "createdAt": "2022-03-21T22:50:20.522Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentials": Array [], + "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-03-22T22:50:20.522Z", + }, + Object { + "_tags": Object { + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialIds": Array [ + "a77114e1-c812-4bff-a53c-3d5003fcc278", + ], + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + }, + "autoAcceptCredential": "contentApproved", + "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", + "createdAt": "2022-03-21T22:50:20.535Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentials": Array [ + Object { + "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", + "credentialRecordType": "indy", + }, + ], + "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + "_internal/indyRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", + "vr_prime": null, + }, + "master_secret_name": "Wallet: PopulateWallet2", + "nonce": "373984270150786864433163", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", + "updatedAt": "2022-03-22T22:50:20.522Z", + }, + Object { + "_tags": Object { + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "autoAcceptCredential": "contentApproved", + "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", + "createdAt": "2022-03-21T22:50:20.740Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentials": Array [], + "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-03-22T22:50:20.522Z", + }, + Object { + "_tags": Object { + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialIds": Array [ + "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + ], + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + }, + "autoAcceptCredential": "contentApproved", + "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", + "createdAt": "2022-03-21T22:50:20.746Z", + "credentialAttributes": Array [ + Object { + "mime-type": "text/plain", + "name": "name", + "value": "Alice", + }, + Object { + "mime-type": "text/plain", + "name": "age", + "value": "25", + }, + Object { + "mime-type": "text/plain", + "name": "dateOfBirth", + "value": "2020-01-01", + }, + ], + "credentials": Array [ + Object { + "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", + "credentialRecordType": "indy", + }, + ], + "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", + "metadata": Object { + "_internal/indyCredential": Object { + "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", + "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", + }, + "_internal/indyRequest": Object { + "master_secret_blinding_data": Object { + "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", + "vr_prime": null, + }, + "master_secret_name": "Wallet: PopulateWallet2", + "nonce": "698370616023883730498375", + }, + }, + "protocolVersion": "v1", + "state": "done", + "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", + "updatedAt": "2022-03-22T22:50:20.522Z", + }, +] +`; diff --git a/packages/core/src/storage/migration/__tests__/backup-askar.test.ts b/packages/core/src/storage/migration/__tests__/backup-askar.test.ts new file mode 100644 index 0000000000..170584f2f1 --- /dev/null +++ b/packages/core/src/storage/migration/__tests__/backup-askar.test.ts @@ -0,0 +1,168 @@ +import type { FileSystem } from '../../FileSystem' +import type { StorageUpdateError } from '../error/StorageUpdateError' + +import { readFileSync, unlinkSync } from 'fs' +import path from 'path' + +import { describeRunInNodeVersion } from '../../../../../../tests/runInVersion' +import { AskarModule } from '../../../../../askar/src' +import { askarModuleConfig } from '../../../../../askar/tests/helpers' +import { getAgentOptions } from '../../../../tests/helpers' +import { Agent } from '../../../agent/Agent' +import { InjectionSymbols } from '../../../constants' +import { AriesFrameworkError } from '../../../error' +import { CredentialExchangeRecord, CredentialRepository } from '../../../modules/credentials' +import { JsonTransformer } from '../../../utils' +import { StorageUpdateService } from '../StorageUpdateService' +import { UpdateAssistant } from '../UpdateAssistant' + +const agentOptions = getAgentOptions( + 'UpdateAssistant | Backup | Aries Askar', + {}, + { + askar: new AskarModule(askarModuleConfig), + } +) + +const aliceCredentialRecordsString = readFileSync( + path.join(__dirname, '__fixtures__/alice-4-credentials-0.1.json'), + 'utf8' +) + +const backupDate = new Date('2022-03-22T22:50:20.522Z') +jest.useFakeTimers().setSystemTime(backupDate) +const backupIdentifier = backupDate.getTime() + +describeRunInNodeVersion([18], 'UpdateAssistant | Backup | Aries Askar', () => { + let updateAssistant: UpdateAssistant + let agent: Agent + let backupPath: string + + beforeEach(async () => { + agent = new Agent(agentOptions) + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) + backupPath = `${fileSystem.dataPath}/migration/backup/${backupIdentifier}` + + // If tests fail it's possible the cleanup has been skipped. So remove before running tests + const doesFileSystemExist = await fileSystem.exists(backupPath) + if (doesFileSystemExist) { + unlinkSync(backupPath) + } + const doesbackupFileSystemExist = await fileSystem.exists(`${backupPath}-error`) + if (doesbackupFileSystemExist) { + unlinkSync(`${backupPath}-error`) + } + + updateAssistant = new UpdateAssistant(agent, { + v0_1ToV0_2: { + mediationRoleUpdateStrategy: 'allMediator', + }, + }) + + await updateAssistant.initialize() + }) + + afterEach(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should create a backup', async () => { + const aliceCredentialRecordsJson = JSON.parse(aliceCredentialRecordsString) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const aliceCredentialRecords = Object.values(aliceCredentialRecordsJson).map((data: any) => { + const record = JsonTransformer.fromJSON(data.value, CredentialExchangeRecord) + + record.setTags(data.tags) + return record + }) + + const credentialRepository = agent.dependencyManager.resolve(CredentialRepository) + const storageUpdateService = agent.dependencyManager.resolve(StorageUpdateService) + + // Add 0.1 data and set version to 0.1 + for (const credentialRecord of aliceCredentialRecords) { + await credentialRepository.save(agent.context, credentialRecord) + } + await storageUpdateService.setCurrentStorageVersion(agent.context, '0.1') + + // Expect an update is needed + expect(await updateAssistant.isUpToDate()).toBe(false) + + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) + // Backup should not exist before update + expect(await fileSystem.exists(backupPath)).toBe(false) + + const walletSpy = jest.spyOn(agent.wallet, 'export') + + // Create update + await updateAssistant.update() + + // A wallet export should have been initiated + expect(walletSpy).toHaveBeenCalledWith({ key: agent.wallet.walletConfig?.key, path: backupPath }) + + // Backup should be cleaned after update + expect(await fileSystem.exists(backupPath)).toBe(false) + + expect( + (await credentialRepository.getAll(agent.context)).sort((a, b) => a.id.localeCompare(b.id)) + ).toMatchSnapshot() + }) + + it('should restore the backup if an error occurs during the update', async () => { + const aliceCredentialRecordsJson = JSON.parse(aliceCredentialRecordsString) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const aliceCredentialRecords = Object.values(aliceCredentialRecordsJson).map((data: any) => { + const record = JsonTransformer.fromJSON(data.value, CredentialExchangeRecord) + + record.setTags(data.tags) + return record + }) + + const credentialRepository = agent.dependencyManager.resolve(CredentialRepository) + const storageUpdateService = agent.dependencyManager.resolve(StorageUpdateService) + + // Add 0.1 data and set version to 0.1 + for (const credentialRecord of aliceCredentialRecords) { + await credentialRepository.save(agent.context, credentialRecord) + } + await storageUpdateService.setCurrentStorageVersion(agent.context, '0.1') + + // Expect an update is needed + expect(await updateAssistant.isUpToDate()).toBe(false) + jest.spyOn(updateAssistant, 'getNeededUpdates').mockResolvedValue([ + { + fromVersion: '0.1', + toVersion: '0.2', + doUpdate: async () => { + throw new AriesFrameworkError("Uh oh I'm broken") + }, + }, + ]) + + const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) + // Backup should not exist before update + expect(await fileSystem.exists(backupPath)).toBe(false) + + let updateError: StorageUpdateError | undefined = undefined + + try { + await updateAssistant.update() + } catch (error) { + updateError = error + } + + expect(updateError?.cause?.message).toEqual("Uh oh I'm broken") + + // Only backup error should exist after update + expect(await fileSystem.exists(backupPath)).toBe(false) + expect(await fileSystem.exists(`${backupPath}-error`)).toBe(true) + + // Wallet should be same as when we started because of backup + expect((await credentialRepository.getAll(agent.context)).sort((a, b) => a.id.localeCompare(b.id))).toEqual( + aliceCredentialRecords.sort((a, b) => a.id.localeCompare(b.id)) + ) + }) +}) diff --git a/packages/core/src/storage/migration/index.ts b/packages/core/src/storage/migration/index.ts index c908e9655d..477cfc3df5 100644 --- a/packages/core/src/storage/migration/index.ts +++ b/packages/core/src/storage/migration/index.ts @@ -2,3 +2,4 @@ export * from './repository/StorageVersionRecord' export * from './repository/StorageVersionRepository' export * from './StorageUpdateService' export * from './UpdateAssistant' +export { Update } from './updates' diff --git a/packages/core/src/storage/migration/updates.ts b/packages/core/src/storage/migration/updates.ts index 294975e0f4..4e1d09a898 100644 --- a/packages/core/src/storage/migration/updates.ts +++ b/packages/core/src/storage/migration/updates.ts @@ -5,6 +5,7 @@ import type { VersionString } from '../../utils/version' import { updateV0_1ToV0_2 } from './updates/0.1-0.2' import { updateV0_2ToV0_3 } from './updates/0.2-0.3' import { updateV0_3ToV0_3_1 } from './updates/0.3-0.3.1' +import { updateV0_3_1ToV0_4 } from './updates/0.3.1-0.4' export const INITIAL_STORAGE_VERSION = '0.1' @@ -40,6 +41,11 @@ export const supportedUpdates = [ toVersion: '0.3.1', doUpdate: updateV0_3ToV0_3_1, }, + { + fromVersion: '0.3.1', + toVersion: '0.4', + doUpdate: updateV0_3_1ToV0_4, + }, ] as const // Current version is last toVersion from the supported updates diff --git a/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/cache.test.ts b/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/cache.test.ts new file mode 100644 index 0000000000..477cdb0ffa --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/cache.test.ts @@ -0,0 +1,53 @@ +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { Agent } from '../../../../../agent/Agent' +import * as testModule from '../cache' + +const agentConfig = getAgentConfig('Migration Cache 0.3.1-0.4') +const agentContext = getAgentContext() + +const storageService = { + getAll: jest.fn(), + deleteById: jest.fn(), +} + +jest.mock('../../../../../agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn(() => storageService), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.3.1-0.4 | Cache', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + describe('migrateCacheToV0_4()', () => { + it('should fetch all cache records and remove them ', async () => { + const records = [{ id: 'first' }, { id: 'second' }] + + mockFunction(storageService.getAll).mockResolvedValue(records) + + await testModule.migrateCacheToV0_4(agent) + + expect(storageService.getAll).toHaveBeenCalledTimes(1) + expect(storageService.getAll).toHaveBeenCalledWith(agent.context, expect.anything()) + expect(storageService.deleteById).toHaveBeenCalledTimes(2) + + const [, , firstId] = mockFunction(storageService.deleteById).mock.calls[0] + const [, , secondId] = mockFunction(storageService.deleteById).mock.calls[1] + expect(firstId).toEqual('first') + expect(secondId).toEqual('second') + }) + }) +}) diff --git a/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/did.test.ts b/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/did.test.ts new file mode 100644 index 0000000000..fce2f75fb3 --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.3.1-0.4/__tests__/did.test.ts @@ -0,0 +1,81 @@ +import { getAgentConfig, getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { Agent } from '../../../../../agent/Agent' +import { DidDocumentRole, DidRecord } from '../../../../../modules/dids' +import { DidRepository } from '../../../../../modules/dids/repository/DidRepository' +import { JsonTransformer } from '../../../../../utils' +import { uuid } from '../../../../../utils/uuid' +import { Metadata } from '../../../../Metadata' +import * as testModule from '../did' + +const agentConfig = getAgentConfig('Migration DidRecord 0.3.1-0.4') +const agentContext = getAgentContext() + +jest.mock('../../../../../modules/dids/repository/DidRepository') +const DidRepositoryMock = DidRepository as jest.Mock +const didRepository = new DidRepositoryMock() + +jest.mock('../../../../../agent/Agent', () => { + return { + Agent: jest.fn(() => ({ + config: agentConfig, + context: agentContext, + dependencyManager: { + resolve: jest.fn(() => didRepository), + }, + })), + } +}) + +// Mock typed object +const AgentMock = Agent as jest.Mock + +describe('0.3.1-0.4 | Did', () => { + let agent: Agent + + beforeEach(() => { + agent = new AgentMock() + }) + + describe('migrateDidRecordToV0_4()', () => { + it('should fetch all records and apply the needed updates ', async () => { + const records: DidRecord[] = [getDid({ did: 'did:sov:123', qualifiedIndyDid: 'did:indy:local:123' })] + + mockFunction(didRepository.findByQuery).mockResolvedValue(records) + + await testModule.migrateDidRecordToV0_4(agent) + + expect(didRepository.findByQuery).toHaveBeenCalledTimes(1) + expect(didRepository.findByQuery).toHaveBeenCalledWith(agent.context, { + method: 'sov', + role: DidDocumentRole.Created, + }) + expect(didRepository.findByQuery).toHaveBeenCalledTimes(1) + + const [, didRecord] = mockFunction(didRepository.update).mock.calls[0] + expect(didRecord).toEqual({ + type: 'DidRecord', + id: expect.any(String), + did: 'did:indy:local:123', + metadata: expect.any(Metadata), + role: DidDocumentRole.Created, + _tags: { + qualifiedIndyDid: undefined, + }, + }) + }) + }) +}) + +function getDid({ did, qualifiedIndyDid }: { did: string; qualifiedIndyDid: string }) { + return JsonTransformer.fromJSON( + { + role: DidDocumentRole.Created, + id: uuid(), + did, + _tags: { + qualifiedIndyDid, + }, + }, + DidRecord + ) +} diff --git a/packages/core/src/storage/migration/updates/0.3.1-0.4/cache.ts b/packages/core/src/storage/migration/updates/0.3.1-0.4/cache.ts new file mode 100644 index 0000000000..5ee3174e3b --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.3.1-0.4/cache.ts @@ -0,0 +1,32 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' +import type { StorageService } from '../../../StorageService' + +import { InjectionSymbols } from '../../../../constants' +import { BaseRecord } from '../../../BaseRecord' + +/** + * removes the all cache records as used in 0.3.0, as they have been updated to use the new cache interface. + */ +export async function migrateCacheToV0_4(agent: Agent) { + agent.config.logger.info('Removing 0.3 cache records from storage') + + const storageService = agent.dependencyManager.resolve>(InjectionSymbols.StorageService) + + agent.config.logger.debug(`Fetching all cache records`) + const records = await storageService.getAll(agent.context, CacheRecord) + + for (const record of records) { + agent.config.logger.debug(`Removing cache record with id ${record.id}`) + await storageService.deleteById(agent.context, CacheRecord, record.id) + agent.config.logger.debug(`Successfully removed cache record with id ${record.id}`) + } +} + +class CacheRecord extends BaseRecord { + public static readonly type = 'CacheRecord' + public readonly type = CacheRecord.type + + public getTags() { + return this._tags + } +} diff --git a/packages/core/src/storage/migration/updates/0.3.1-0.4/did.ts b/packages/core/src/storage/migration/updates/0.3.1-0.4/did.ts new file mode 100644 index 0000000000..f9844b8c1c --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.3.1-0.4/did.ts @@ -0,0 +1,51 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' +import type { DidRecord } from '../../../../modules/dids' + +import { DidDocumentRole, DidRepository } from '../../../../modules/dids' + +/** + * Migrates the {@link DidRecord} to 0.4 compatible format. It fetches all did records from storage + * with method sov and applies the needed updates to the records. After a record has been transformed, it is updated + * in storage and the next record will be transformed. + * + * The following transformations are applied: + * - {@link migrateSovDidToIndyDid} + */ +export async function migrateDidRecordToV0_4(agent: Agent) { + agent.config.logger.info('Migrating did records to storage version 0.4') + const didRepository = agent.dependencyManager.resolve(DidRepository) + + agent.config.logger.debug(`Fetching all did records with did method did:sov from storage`) + const allSovDids = await didRepository.findByQuery(agent.context, { + method: 'sov', + role: DidDocumentRole.Created, + }) + + agent.config.logger.debug(`Found a total of ${allSovDids.length} did:sov did records to update.`) + for (const sovDidRecord of allSovDids) { + agent.config.logger.debug(`Migrating did:sov did record with id ${sovDidRecord.id} to storage version 0.4`) + + const oldDid = sovDidRecord.did + migrateSovDidToIndyDid(agent, sovDidRecord) + + // Save updated did record + await didRepository.update(agent.context, sovDidRecord) + + agent.config.logger.debug( + `Successfully migrated did:sov did record with old did ${oldDid} to new did ${sovDidRecord.did} for storage version 0.4` + ) + } +} + +export function migrateSovDidToIndyDid(agent: Agent, didRecord: DidRecord) { + agent.config.logger.debug( + `Migrating did record with id ${didRecord.id} and did ${didRecord.did} to indy did for version 0.4` + ) + + const qualifiedIndyDid = didRecord.getTag('qualifiedIndyDid') as string + + didRecord.did = qualifiedIndyDid + + // Unset qualifiedIndyDid tag + didRecord.setTag('qualifiedIndyDid', undefined) +} diff --git a/packages/core/src/storage/migration/updates/0.3.1-0.4/index.ts b/packages/core/src/storage/migration/updates/0.3.1-0.4/index.ts new file mode 100644 index 0000000000..d43b32a15f --- /dev/null +++ b/packages/core/src/storage/migration/updates/0.3.1-0.4/index.ts @@ -0,0 +1,9 @@ +import type { BaseAgent } from '../../../../agent/BaseAgent' + +import { migrateCacheToV0_4 } from './cache' +import { migrateDidRecordToV0_4 } from './did' + +export async function updateV0_3_1ToV0_4(agent: Agent): Promise { + await migrateDidRecordToV0_4(agent) + await migrateCacheToV0_4(agent) +} diff --git a/packages/core/src/utils/__tests__/indyError.test.ts b/packages/core/src/utils/__tests__/indyError.test.ts deleted file mode 100644 index 154b99146d..0000000000 --- a/packages/core/src/utils/__tests__/indyError.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { isIndyError } from '../indyError' - -describe('isIndyError()', () => { - it('should return true when the name is "IndyError" and no errorName is passed', () => { - const error = { name: 'IndyError' } - - expect(isIndyError(error)).toBe(true) - }) - - it('should return false when the name is not "IndyError"', () => { - const error = { name: 'IndyError2' } - - expect(isIndyError(error)).toBe(false) - expect(isIndyError(error, 'WalletAlreadyExistsError')).toBe(false) - }) - - it('should return true when indyName matches the passed errorName', () => { - const error = { name: 'IndyError', indyName: 'WalletAlreadyExistsError' } - - expect(isIndyError(error, 'WalletAlreadyExistsError')).toBe(true) - }) - - it('should return false when the indyName does not match the passes errorName', () => { - const error = { name: 'IndyError', indyName: 'WalletAlreadyExistsError' } - - // @ts-expect-error not a valid error name - expect(isIndyError(error, 'DoesNotMatchError')).toBe(false) - }) - - // Below here are temporary until indy-sdk releases new version - it('should return true when the indyName is missing but the message contains a matching error code', () => { - const error = { name: 'IndyError', message: '212' } - - expect(isIndyError(error, 'WalletItemNotFound')).toBe(true) - }) - - it('should return false when the indyName is missing and the message contains a valid but not matching error code', () => { - const error = { name: 'IndyError', message: '212' } - - // @ts-expect-error not a valid error name - expect(isIndyError(error, 'DoesNotMatchError')).toBe(false) - }) - - it('should throw an error when the indyName is missing and the message contains an invalid error code', () => { - const error = { name: 'IndyError', message: '832882' } - - // @ts-expect-error not a valid error name - expect(() => isIndyError(error, 'SomeNewErrorWeDoNotHave')).toThrowError( - 'Could not determine errorName of indyError 832882' - ) - }) -}) diff --git a/packages/core/src/utils/indyError.ts b/packages/core/src/utils/indyError.ts deleted file mode 100644 index 0472ac0f04..0000000000 --- a/packages/core/src/utils/indyError.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { AriesFrameworkError } from '../error' - -export const indyErrors = { - 100: 'CommonInvalidParam1', - 101: 'CommonInvalidParam2', - 102: 'CommonInvalidParam3', - 103: 'CommonInvalidParam4', - 104: 'CommonInvalidParam5', - 105: 'CommonInvalidParam6', - 106: 'CommonInvalidParam7', - 107: 'CommonInvalidParam8', - 108: 'CommonInvalidParam9', - 109: 'CommonInvalidParam10', - 110: 'CommonInvalidParam11', - 111: 'CommonInvalidParam12', - 112: 'CommonInvalidState', - 113: 'CommonInvalidStructure', - 114: 'CommonIOError', - 115: 'CommonInvalidParam13', - 116: 'CommonInvalidParam14', - 200: 'WalletInvalidHandle', - 201: 'WalletUnknownTypeError', - 202: 'WalletTypeAlreadyRegisteredError', - 203: 'WalletAlreadyExistsError', - 204: 'WalletNotFoundError', - 205: 'WalletIncompatiblePoolError', - 206: 'WalletAlreadyOpenedError', - 207: 'WalletAccessFailed', - 208: 'WalletInputError', - 209: 'WalletDecodingError', - 210: 'WalletStorageError', - 211: 'WalletEncryptionError', - 212: 'WalletItemNotFound', - 213: 'WalletItemAlreadyExists', - 214: 'WalletQueryError', - 300: 'PoolLedgerNotCreatedError', - 301: 'PoolLedgerInvalidPoolHandle', - 302: 'PoolLedgerTerminated', - 303: 'LedgerNoConsensusError', - 304: 'LedgerInvalidTransaction', - 305: 'LedgerSecurityError', - 306: 'PoolLedgerConfigAlreadyExistsError', - 307: 'PoolLedgerTimeout', - 308: 'PoolIncompatibleProtocolVersion', - 309: 'LedgerNotFound', - 400: 'AnoncredsRevocationRegistryFullError', - 401: 'AnoncredsInvalidUserRevocId', - 404: 'AnoncredsMasterSecretDuplicateNameError', - 405: 'AnoncredsProofRejected', - 406: 'AnoncredsCredentialRevoked', - 407: 'AnoncredsCredDefAlreadyExistsError', - 500: 'UnknownCryptoTypeError', - 600: 'DidAlreadyExistsError', - 700: 'PaymentUnknownMethodError', - 701: 'PaymentIncompatibleMethodsError', - 702: 'PaymentInsufficientFundsError', - 703: 'PaymentSourceDoesNotExistError', - 704: 'PaymentOperationNotSupportedError', - 705: 'PaymentExtraFundsError', - 706: 'TransactionNotAllowedError', -} as const - -type IndyErrorValues = (typeof indyErrors)[keyof typeof indyErrors] - -export interface IndyError { - name: 'IndyError' - message: string - indyName?: string -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function isIndyError(error: any, errorName?: IndyErrorValues): error is IndyError { - if (typeof error !== 'object' || error === null) return false - - const indyError = error.name === 'IndyError' - - // if no specific indy error name is passed - // or the error is no indy error - // we can already return - if (!indyError || !errorName) return indyError - - // NodeJS Wrapper is missing some type names. When a type is missing it will - // only have the error code as string in the message field - // Until that is fixed we take that into account to make AFJ work with rn-indy-sdk - // See: https://github.com/AbsaOSS/rn-indy-sdk/pull/24 - // See: https://github.com/hyperledger/indy-sdk/pull/2283 - if (!error.indyName) { - const errorCode = Number(error.message) - if (!isNaN(errorCode) && Object.prototype.hasOwnProperty.call(indyErrors, errorCode)) { - // We already check if the property is set. We can safely ignore this typescript error - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - return errorName === indyErrors[errorCode] - } - - throw new AriesFrameworkError(`Could not determine errorName of indyError ${error.message}`) - } - - return error.indyName === errorName -} diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 667448efc3..9204882365 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -19,6 +19,12 @@ export interface Wallet extends Disposable { rotateKey(walletConfig: WalletConfigRekey): Promise close(): Promise delete(): Promise + + /** + * Export the wallet to a file at the given path and encrypt it with the given key. + * + * @throws {WalletExportPathExistsError} When the export path already exists + */ export(exportConfig: WalletExportImportConfig): Promise import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise diff --git a/packages/core/src/wallet/error/WalletExportPathExistsError.ts b/packages/core/src/wallet/error/WalletExportPathExistsError.ts new file mode 100644 index 0000000000..cf46e028e7 --- /dev/null +++ b/packages/core/src/wallet/error/WalletExportPathExistsError.ts @@ -0,0 +1,7 @@ +import { WalletError } from './WalletError' + +export class WalletExportPathExistsError extends WalletError { + public constructor(message: string, { cause }: { cause?: Error } = {}) { + super(message, { cause }) + } +} diff --git a/packages/core/src/wallet/error/index.ts b/packages/core/src/wallet/error/index.ts index 1337a5dd46..92040216f4 100644 --- a/packages/core/src/wallet/error/index.ts +++ b/packages/core/src/wallet/error/index.ts @@ -3,3 +3,4 @@ export { WalletNotFoundError } from './WalletNotFoundError' export { WalletInvalidKeyError } from './WalletInvalidKeyError' export { WalletError } from './WalletError' export { WalletKeyExistsError } from './WalletKeyExistsError' +export { WalletExportPathExistsError } from './WalletExportPathExistsError' diff --git a/packages/indy-sdk/src/anoncreds/utils/tails.ts b/packages/indy-sdk/src/anoncreds/utils/tails.ts index f803ea5d78..787d757322 100644 --- a/packages/indy-sdk/src/anoncreds/utils/tails.ts +++ b/packages/indy-sdk/src/anoncreds/utils/tails.ts @@ -1,9 +1,9 @@ import type { IndySdk } from '../../types' import type { AgentContext, FileSystem } from '@aries-framework/core' -import { AriesFrameworkError, getDirFromFilePath, IndySdkError, InjectionSymbols } from '@aries-framework/core' +import { AriesFrameworkError, getDirFromFilePath, InjectionSymbols } from '@aries-framework/core' -import { isIndyError } from '../../error' +import { IndySdkError, isIndyError } from '../../error' import { IndySdkSymbol } from '../../types' /** diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index 72463c7b66..b3d5bed9a9 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -29,6 +29,7 @@ import { TypedArrayEncoder, WalletDuplicateError, WalletError, + WalletExportPathExistsError, WalletInvalidKeyError, WalletKeyExistsError, WalletNotFoundError, @@ -308,6 +309,15 @@ export class IndySdkWallet implements Wallet { if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) } + + // Export path already exists + if (isIndyError(error, 'CommonIOError')) { + throw new WalletExportPathExistsError( + `Unable to create export, wallet export at path '${exportConfig.path}' already exists`, + { cause: error } + ) + } + const errorMessage = `Error exporting wallet: ${error.message}` this.logger.error(errorMessage, { error, diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index d32e8947ec..ffcc98b929 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -205,7 +205,6 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { role: DidDocumentRole.Created, tags: { recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), - qualifiedIndyDid: did, }, }) From 692defa45ffcb4f36b0fa36970c4dc27aa75317c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 29 Mar 2023 22:46:50 +0200 Subject: [PATCH 105/139] fix: add reflect-metadata (#1409) Signed-off-by: Timo Glastra --- packages/anoncreds/package.json | 3 ++- packages/anoncreds/src/index.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 9f1ab9d6e2..937a2d0fcd 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -27,7 +27,8 @@ "@aries-framework/core": "0.3.3", "bn.js": "^5.2.1", "class-transformer": "0.5.1", - "class-validator": "0.13.1" + "class-validator": "0.13.1", + "reflect-metadata": "^0.1.13" }, "devDependencies": { "@aries-framework/node": "0.3.3", diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 52174f62e9..02013d9977 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -1,3 +1,5 @@ +import 'reflect-metadata' + export * from './models' export * from './services' export * from './error' From ef00099bbca78cda92dba8b446fa68539634ad96 Mon Sep 17 00:00:00 2001 From: Amit-Padmani <106090107+Amit-Padmani@users.noreply.github.com> Date: Thu, 30 Mar 2023 22:00:10 +0530 Subject: [PATCH 106/139] chore(bbs-signatures): make module public (#1411) Signed-off-by: Amit-Padmani --- packages/bbs-signatures/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index 96f1e6cdde..f63fed535d 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -2,8 +2,7 @@ "name": "@aries-framework/bbs-signatures", "main": "build/index", "types": "build/index", - "version": "0.3.0", - "private": true, + "version": "0.3.3", "files": [ "build" ], From 47636b4a08ffbfa9a3f2a5a3c5aebda44f7d16c8 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 30 Mar 2023 21:21:48 +0200 Subject: [PATCH 107/139] feat(anoncreds): store method name in records (#1387) Signed-off-by: Timo Glastra --- .../src/services/AnonCredsRsHolderService.ts | 10 +++++++ .../AnonCredsRsHolderService.test.ts | 15 +++++++++++ .../__tests__/AnonCredsRsServices.test.ts | 10 +++++++ .../src/services/__tests__/helpers.ts | 1 + .../anoncreds-rs/tests/anoncreds-flow.test.ts | 3 +++ packages/anoncreds-rs/tests/indy-flow.test.ts | 3 +++ packages/anoncreds/src/AnonCredsApi.ts | 19 ++++++++++--- .../legacy-indy-format-services.test.ts | 1 + packages/anoncreds/src/models/internal.ts | 1 + .../AnonCredsCredentialDefinitionRecord.ts | 10 +++++++ .../repository/AnonCredsCredentialRecord.ts | 10 +++++++ .../src/repository/AnonCredsSchemaRecord.ts | 10 +++++++ .../AnonCredsCredentialRecord.test.ts | 2 ++ .../services/AnonCredsHolderServiceOptions.ts | 1 + .../services/registry/AnonCredsRegistry.ts | 7 +++++ .../anoncreds/src/services/registry/index.ts | 1 + .../updates/0.3.1-0.4/credentialDefinition.ts | 1 + .../anoncreds/src/updates/0.3.1-0.4/schema.ts | 1 + .../__tests__/__snapshots__/0.3.test.ts.snap | 8 ++++++ .../tests/InMemoryAnonCredsRegistry.ts | 2 ++ packages/anoncreds/tests/anoncreds.test.ts | 27 ++++++++----------- packages/core/src/storage/StorageService.ts | 2 +- .../src/IndySdkToAskarMigrationUpdater.ts | 2 ++ .../services/IndySdkAnonCredsRegistry.ts | 2 ++ .../services/IndySdkHolderService.ts | 8 ++++++ .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 2 ++ 26 files changed, 139 insertions(+), 20 deletions(-) diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index 93b1ed2b41..e543fd32b7 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -33,6 +33,7 @@ import { AnonCredsLinkSecretRepository, AnonCredsRestrictionWrapper, legacyIndyCredentialDefinitionIdRegex, + AnonCredsRegistryService, } from '@aries-framework/anoncreds' import { AriesFrameworkError, JsonTransformer, TypedArrayEncoder, injectable, utils } from '@aries-framework/core' import { @@ -265,6 +266,10 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const credentialRepository = agentContext.dependencyManager.resolve(AnonCredsCredentialRepository) + const methodName = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, credential.cred_def_id).methodName + await credentialRepository.save( agentContext, new AnonCredsCredentialRecord({ @@ -276,6 +281,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schemaIssuerId: schema.issuerId, schemaVersion: schema.version, credentialRevocationId: processedCredential.revocationRegistryIndex?.toString(), + methodName, }) ) @@ -305,6 +311,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schemaId: credentialRecord.credential.schema_id, credentialRevocationId: credentialRecord.credentialRevocationId, revocationRegistryId: credentialRecord.credential.rev_reg_id, + methodName: credentialRecord.methodName, } } @@ -321,6 +328,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schemaName: options.schemaName, schemaVersion: options.schemaVersion, schemaIssuerId: options.schemaIssuerId, + methodName: options.methodName, }) return credentialRecords.map((credentialRecord) => ({ @@ -332,6 +340,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schemaId: credentialRecord.credential.schema_id, credentialRevocationId: credentialRecord.credentialRevocationId, revocationRegistryId: credentialRecord.credential.rev_reg_id, + methodName: credentialRecord.methodName, })) } @@ -397,6 +406,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { schemaId: credentialRecord.credential.schema_id, credentialRevocationId: credentialRecord.credentialRevocationId, revocationRegistryId: credentialRecord.credential.rev_reg_id, + methodName: credentialRecord.methodName, }, interval: proofRequest.non_revoked, } diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts index 87bdcf30f8..b7de80d8ae 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsHolderService.test.ts @@ -11,6 +11,7 @@ import type { import type { JsonObject } from '@hyperledger/anoncreds-nodejs' import { + AnonCredsModuleConfig, AnonCredsHolderServiceSymbol, AnonCredsLinkSecretRecord, AnonCredsCredentialRecord, @@ -21,6 +22,7 @@ import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { AnonCredsCredentialDefinitionRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialDefinitionRepository' import { AnonCredsCredentialRepository } from '../../../../anoncreds/src/repository/AnonCredsCredentialRepository' import { AnonCredsLinkSecretRepository } from '../../../../anoncreds/src/repository/AnonCredsLinkSecretRepository' +import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../core/tests/helpers' import { AnonCredsRsHolderService } from '../AnonCredsRsHolderService' @@ -53,6 +55,12 @@ const agentContext = getAgentContext({ [AnonCredsLinkSecretRepository, anoncredsLinkSecretRepositoryMock], [AnonCredsCredentialRepository, anoncredsCredentialRepositoryMock], [AnonCredsHolderServiceSymbol, anonCredsHolderService], + [ + AnonCredsModuleConfig, + new AnonCredsModuleConfig({ + registries: [new InMemoryAnonCredsRegistry({})], + }), + ], ], agentConfig, }) @@ -219,6 +227,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaIssuerId: 'schemaIssuerDid', schemaName: 'schemaName', schemaVersion: 'schemaVersion', + methodName: 'inMemory', }) ) getByCredentialIdMock.mockResolvedValueOnce( @@ -230,6 +239,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaIssuerId: 'schemaIssuerDid', schemaName: 'schemaName', schemaVersion: 'schemaVersion', + methodName: 'inMemory', }) ) @@ -436,6 +446,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaIssuerId: 'schemaIssuerDid', schemaName: 'schemaName', schemaVersion: 'schemaVersion', + methodName: 'inMemory', }) ) @@ -466,6 +477,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaIssuerId: 'schemaIssuerDid', schemaName: 'schemaName', schemaVersion: 'schemaVersion', + methodName: 'inMemory', }) ) expect( @@ -502,6 +514,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaIssuerId: 'schemaIssuerDid', schemaName: 'schemaName', schemaVersion: 'schemaVersion', + methodName: 'inMemory', }), ]) @@ -512,6 +525,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaName: 'schemaName', schemaVersion: 'schemaVersion', issuerId: 'issuerDid', + methodName: 'inMemory', }) expect(findByQueryMock).toHaveBeenCalledWith(agentContext, { @@ -521,6 +535,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsHolderService', () => { schemaName: 'schemaName', schemaVersion: 'schemaVersion', issuerId: 'issuerDid', + methodName: 'inMemory', }) expect(credentialInfo).toMatchObject([ { diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index dcaadd8916..49b73ed7f3 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -1,6 +1,7 @@ import type { AnonCredsProofRequest } from '@aries-framework/anoncreds' import { + AnonCredsModuleConfig, AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, AnonCredsVerifierServiceSymbol, @@ -43,6 +44,12 @@ const agentContext = getAgentContext({ [AnonCredsIssuerServiceSymbol, anonCredsIssuerService], [AnonCredsHolderServiceSymbol, anonCredsHolderService], [AnonCredsVerifierServiceSymbol, anonCredsVerifierService], + [ + AnonCredsModuleConfig, + new AnonCredsModuleConfig({ + registries: [registry], + }), + ], ], agentConfig, }) @@ -96,6 +103,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsServices', () => { new AnonCredsSchemaRecord({ schema: schemaState.schema, schemaId: schemaState.schemaId, + methodName: 'inMemory', }) ) @@ -104,6 +112,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsServices', () => { new AnonCredsCredentialDefinitionRecord({ credentialDefinition: credentialDefinitionState.credentialDefinition, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + methodName: 'inMemory', }) ) @@ -180,6 +189,7 @@ describeRunInNodeVersion([18], 'AnonCredsRsServices', () => { credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, revocationRegistryId: null, credentialRevocationId: undefined, // Should it be null in this case? + methodName: 'inMemory', }) const proofRequest: AnonCredsProofRequest = { diff --git a/packages/anoncreds-rs/src/services/__tests__/helpers.ts b/packages/anoncreds-rs/src/services/__tests__/helpers.ts index b71cfdcf6d..a6d97632f1 100644 --- a/packages/anoncreds-rs/src/services/__tests__/helpers.ts +++ b/packages/anoncreds-rs/src/services/__tests__/helpers.ts @@ -158,6 +158,7 @@ export function createCredentialForHolder(options: { credentialDefinitionId, credentialId, schemaId, + methodName: 'inMemory', } const returnObj = { credential: credentialObj.toJson() as unknown as AnonCredsCredential, diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts index 84a440213d..26620aa6d2 100644 --- a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts @@ -127,6 +127,7 @@ describeRunInNodeVersion([18], 'AnonCreds format services using anoncreds-rs', ( new AnonCredsSchemaRecord({ schema: schemaState.schema, schemaId: schemaState.schemaId, + methodName: 'inMemory', }) ) @@ -135,6 +136,7 @@ describeRunInNodeVersion([18], 'AnonCreds format services using anoncreds-rs', ( new AnonCredsCredentialDefinitionRecord({ credentialDefinition: credentialDefinitionState.credentialDefinition, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + methodName: 'inMemory', }) ) @@ -268,6 +270,7 @@ describeRunInNodeVersion([18], 'AnonCreds format services using anoncreds-rs', ( credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, revocationRegistryId: null, credentialRevocationId: undefined, // FIXME: should be null? + methodName: 'inMemory', }) expect(holderCredentialRecord.metadata.data).toEqual({ diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index 00fed973f1..f875c1e787 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -128,6 +128,7 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', new AnonCredsSchemaRecord({ schema: schemaState.schema, schemaId: schemaState.schemaId, + methodName: 'inMemory', }) ) @@ -136,6 +137,7 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', new AnonCredsCredentialDefinitionRecord({ credentialDefinition: credentialDefinitionState.credentialDefinition, credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + methodName: 'inMemory', }) ) @@ -268,6 +270,7 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, revocationRegistryId: null, credentialRevocationId: undefined, // FIXME: should be null? + methodName: 'inMemory', }) expect(holderCredentialRecord.metadata.data).toEqual({ diff --git a/packages/anoncreds/src/AnonCredsApi.ts b/packages/anoncreds/src/AnonCredsApi.ts index 77333e1afd..ede2e691e3 100644 --- a/packages/anoncreds/src/AnonCredsApi.ts +++ b/packages/anoncreds/src/AnonCredsApi.ts @@ -10,9 +10,11 @@ import type { RegisterCredentialDefinitionReturn, RegisterSchemaOptions, RegisterSchemaReturn, + AnonCredsRegistry, GetCredentialsOptions, } from './services' import type { Extensible } from './services/registry/base' +import type { SimpleQuery } from '@aries-framework/core' import { AgentContext, inject, injectable } from '@aries-framework/core' @@ -163,7 +165,7 @@ export class AnonCredsApi { try { const result = await registry.registerSchema(this.agentContext, options) - await this.storeSchemaRecord(result) + await this.storeSchemaRecord(registry, result) return result } catch (error) { @@ -179,6 +181,10 @@ export class AnonCredsApi { } } + public async getCreatedSchemas(query: SimpleQuery) { + return this.anonCredsSchemaRepository.findByQuery(this.agentContext, query) + } + /** * Retrieve a {@link AnonCredsCredentialDefinition} from the registry associated * with the {@link credentialDefinitionId} @@ -264,7 +270,7 @@ export class AnonCredsApi { options: options.options, }) - await this.storeCredentialDefinitionRecord(result, credentialDefinitionPrivate, keyCorrectnessProof) + await this.storeCredentialDefinitionRecord(registry, result, credentialDefinitionPrivate, keyCorrectnessProof) return result } catch (error) { @@ -280,6 +286,10 @@ export class AnonCredsApi { } } + public async getCreatedCredentialDefinitions(query: SimpleQuery) { + return this.anonCredsCredentialDefinitionRepository.findByQuery(this.agentContext, query) + } + /** * Retrieve a {@link AnonCredsRevocationRegistryDefinition} from the registry associated * with the {@link revocationRegistryDefinitionId} @@ -357,6 +367,7 @@ export class AnonCredsApi { } private async storeCredentialDefinitionRecord( + registry: AnonCredsRegistry, result: RegisterCredentialDefinitionReturn, credentialDefinitionPrivate?: Record, keyCorrectnessProof?: Record @@ -371,6 +382,7 @@ export class AnonCredsApi { const credentialDefinitionRecord = new AnonCredsCredentialDefinitionRecord({ credentialDefinitionId: result.credentialDefinitionState.credentialDefinitionId, credentialDefinition: result.credentialDefinitionState.credentialDefinition, + methodName: registry.methodName, }) // TODO: do we need to store this metadata? For indy, the registration metadata contains e.g. @@ -412,7 +424,7 @@ export class AnonCredsApi { } } - private async storeSchemaRecord(result: RegisterSchemaReturn): Promise { + private async storeSchemaRecord(registry: AnonCredsRegistry, result: RegisterSchemaReturn): Promise { try { // If we have both the schema and the schemaId we will store a copy of the schema. We may need to handle an // edge case in the future where we e.g. don't have the id yet, and it is registered through a different channel @@ -420,6 +432,7 @@ export class AnonCredsApi { const schemaRecord = new AnonCredsSchemaRecord({ schemaId: result.schemaState.schemaId, schema: result.schemaState.schema, + methodName: registry.methodName, }) await this.anonCredsSchemaRepository.save(this.agentContext, schemaRecord) diff --git a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts index c854914f38..4950d50d3b 100644 --- a/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts +++ b/packages/anoncreds/src/formats/__tests__/legacy-indy-format-services.test.ts @@ -242,6 +242,7 @@ describe('Legacy indy format services', () => { credentialDefinitionId: legacyCredentialDefinitionId, revocationRegistryId: null, credentialRevocationId: null, + methodName: 'indy', }) expect(holderCredentialRecord.metadata.data).toEqual({ diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index 39452f736a..4693e02859 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -7,6 +7,7 @@ export interface AnonCredsCredentialInfo { credentialDefinitionId: string revocationRegistryId?: string | undefined credentialRevocationId?: string | undefined + methodName: string } export interface AnonCredsRequestedAttributeMatch { diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts index bbacfb4f73..2986566069 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts @@ -8,6 +8,7 @@ export interface AnonCredsCredentialDefinitionRecordProps { id?: string credentialDefinitionId: string credentialDefinition: AnonCredsCredentialDefinition + methodName: string } export type DefaultAnonCredsCredentialDefinitionTags = { @@ -15,6 +16,7 @@ export type DefaultAnonCredsCredentialDefinitionTags = { credentialDefinitionId: string issuerId: string tag: string + methodName: string } export class AnonCredsCredentialDefinitionRecord extends BaseRecord< @@ -28,6 +30,12 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord< public credentialDefinitionId!: string public credentialDefinition!: AnonCredsCredentialDefinition + /** + * AnonCreds method name. We don't use names explicitly from the registry (there's no identifier for a registry) + * @see https://hyperledger.github.io/anoncreds-methods-registry/ + */ + public methodName!: string + public constructor(props: AnonCredsCredentialDefinitionRecordProps) { super() @@ -35,6 +43,7 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord< this.id = props.id ?? utils.uuid() this.credentialDefinitionId = props.credentialDefinitionId this.credentialDefinition = props.credentialDefinition + this.methodName = props.methodName } } @@ -45,6 +54,7 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord< schemaId: this.credentialDefinition.schemaId, issuerId: this.credentialDefinition.issuerId, tag: this.credentialDefinition.tag, + methodName: this.methodName, } } } diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts index 7515dd09c2..56657f8993 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialRecord.ts @@ -13,6 +13,7 @@ export interface AnonCredsCredentialRecordProps { schemaVersion: string schemaIssuerId: string issuerId: string + methodName: string } export type DefaultAnonCredsCredentialTags = { @@ -22,6 +23,7 @@ export type DefaultAnonCredsCredentialTags = { credentialRevocationId?: string revocationRegistryId?: string schemaId: string + methodName: string // the following keys can be used for every `attribute name` in credential. [key: `attr::${string}::marker`]: true | undefined @@ -47,6 +49,12 @@ export class AnonCredsCredentialRecord extends BaseRecord< public readonly linkSecretId!: string public readonly credential!: AnonCredsCredential + /** + * AnonCreds method name. We don't use names explicitly from the registry (there's no identifier for a registry) + * @see https://hyperledger.github.io/anoncreds-methods-registry/ + */ + public readonly methodName!: string + public constructor(props: AnonCredsCredentialRecordProps) { super() @@ -56,6 +64,7 @@ export class AnonCredsCredentialRecord extends BaseRecord< this.credential = props.credential this.credentialRevocationId = props.credentialRevocationId this.linkSecretId = props.linkSecretId + this.methodName = props.methodName this.setTags({ issuerId: props.issuerId, schemaIssuerId: props.schemaIssuerId, @@ -74,6 +83,7 @@ export class AnonCredsCredentialRecord extends BaseRecord< credentialRevocationId: this.credentialRevocationId, revocationRegistryId: this.credential.rev_reg_id, linkSecretId: this.linkSecretId, + methodName: this.methodName, } for (const [key, value] of Object.entries(this.credential.values)) { diff --git a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts index 9a5499951e..6eac754527 100644 --- a/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsSchemaRecord.ts @@ -8,6 +8,7 @@ export interface AnonCredsSchemaRecordProps { id?: string schemaId: string schema: AnonCredsSchema + methodName: string } export type DefaultAnonCredsSchemaTags = { @@ -15,6 +16,7 @@ export type DefaultAnonCredsSchemaTags = { issuerId: string schemaName: string schemaVersion: string + methodName: string } export class AnonCredsSchemaRecord extends BaseRecord< @@ -28,6 +30,12 @@ export class AnonCredsSchemaRecord extends BaseRecord< public schemaId!: string public schema!: AnonCredsSchema + /** + * AnonCreds method name. We don't use names explicitly from the registry (there's no identifier for a registry) + * @see https://hyperledger.github.io/anoncreds-methods-registry/ + */ + public methodName!: string + public constructor(props: AnonCredsSchemaRecordProps) { super() @@ -35,6 +43,7 @@ export class AnonCredsSchemaRecord extends BaseRecord< this.id = props.id ?? utils.uuid() this.schema = props.schema this.schemaId = props.schemaId + this.methodName = props.methodName } } @@ -45,6 +54,7 @@ export class AnonCredsSchemaRecord extends BaseRecord< issuerId: this.schema.issuerId, schemaName: this.schema.name, schemaVersion: this.schema.version, + methodName: this.methodName, } } } diff --git a/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts b/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts index feb80ada78..111ce0a291 100644 --- a/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts +++ b/packages/anoncreds/src/repository/__tests__/AnonCredsCredentialRecord.test.ts @@ -20,6 +20,7 @@ describe('AnoncredsCredentialRecords', () => { schemaIssuerId: 'schemaIssuerDid', schemaName: 'schemaName', schemaVersion: 'schemaVersion', + methodName: 'methodName', }) const tags = anoncredsCredentialRecords.getTags() @@ -38,6 +39,7 @@ describe('AnoncredsCredentialRecords', () => { 'attr::attr1::marker': true, 'attr::attr2::value': 'value2', 'attr::attr2::marker': true, + methodName: 'methodName', }) }) }) diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index e3a677e27d..7bfe190380 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -67,6 +67,7 @@ export interface GetCredentialsOptions { schemaName: string schemaVersion: string issuerId: string + methodName: string } // TODO: Maybe we can make this a bit more specific? diff --git a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts index 870eb90571..85bf72ba2b 100644 --- a/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts +++ b/packages/anoncreds/src/services/registry/AnonCredsRegistry.ts @@ -12,6 +12,13 @@ import type { AgentContext } from '@aries-framework/core' * @public */ export interface AnonCredsRegistry { + /** + * A name to identify the registry. This will be stored as part of the reigstered anoncreds objects to allow querying + * for created objects using a specific registry. Multilpe implementations can use the same name, but they should in that + * case also reference objects on the same networks. + */ + methodName: string + supportedIdentifier: RegExp getSchema(agentContext: AgentContext, schemaId: string): Promise diff --git a/packages/anoncreds/src/services/registry/index.ts b/packages/anoncreds/src/services/registry/index.ts index fd154074fd..6577018992 100644 --- a/packages/anoncreds/src/services/registry/index.ts +++ b/packages/anoncreds/src/services/registry/index.ts @@ -4,3 +4,4 @@ export * from './SchemaOptions' export * from './RevocationRegistryDefinitionOptions' export * from './RevocationStatusListOptions' export { AnonCredsResolutionMetadata } from './base' +export * from './AnonCredsRegistryService' diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts index bd0f3efd49..bcbde09c36 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts @@ -70,6 +70,7 @@ export async function migrateAnonCredsCredentialDefinitionRecordToV0_4 { }) // Check if record was created - const anonCredsSchemaRepository = agent.dependencyManager.resolve(AnonCredsSchemaRepository) - const schemaRecord = await anonCredsSchemaRepository.getBySchemaId( - agent.context, - 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0' - ) - + const [schemaRecord] = await agent.modules.anoncreds.getCreatedSchemas({ + schemaId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0', + }) expect(schemaRecord).toMatchObject({ schemaId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ/anoncreds/v0/SCHEMA/Employee Credential/1.0.0', + methodName: 'inMemory', schema: { attrNames: ['name', 'age'], issuerId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ', @@ -166,6 +164,7 @@ describe('AnonCreds API', () => { issuerId: 'did:indy:pool:localtest:6xDN7v3AiGgusRp4bqZACZ', schemaName: 'Employee Credential', schemaVersion: '1.0.0', + methodName: 'inMemory', }) }) @@ -231,17 +230,12 @@ describe('AnonCreds API', () => { }, }) - // Check if record was created - const anonCredsCredentialDefinitionRepository = agent.dependencyManager.resolve( - AnonCredsCredentialDefinitionRepository - ) - const credentialDefinitionRecord = await anonCredsCredentialDefinitionRepository.getByCredentialDefinitionId( - agent.context, - 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG' - ) - + const [credentialDefinitionRecord] = await agent.modules.anoncreds.getCreatedCredentialDefinitions({ + credentialDefinitionId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG', + }) expect(credentialDefinitionRecord).toMatchObject({ credentialDefinitionId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG', + methodName: 'inMemory', credentialDefinition: { issuerId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX', tag: 'TAG', @@ -264,6 +258,7 @@ describe('AnonCreds API', () => { }) expect(credentialDefinitionRecord.getTags()).toEqual({ + methodName: 'inMemory', credentialDefinitionId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX/anoncreds/v0/CLAIM_DEF/75206/TAG', schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', issuerId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX', diff --git a/packages/core/src/storage/StorageService.ts b/packages/core/src/storage/StorageService.ts index 180af06bd4..5a80b83781 100644 --- a/packages/core/src/storage/StorageService.ts +++ b/packages/core/src/storage/StorageService.ts @@ -4,7 +4,7 @@ import type { AgentContext } from '../agent' import type { Constructor } from '../utils/mixins' // https://stackoverflow.com/questions/51954558/how-can-i-remove-a-wider-type-from-a-union-type-without-removing-its-subtypes-in/51955852#51955852 -export type SimpleQuery = Partial> & TagsBase +export type SimpleQuery> = Partial> & TagsBase interface AdvancedQuery { $and?: Query[] diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index baf6a7ca6b..73db32bdce 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -394,6 +394,8 @@ export class IndySdkToAskarMigrationUpdater { schemaVersion, credentialId: row.name, linkSecretId: this.defaultLinkSecretId, + // Hardcode methodName to indy as all IndySDK credentials are indy credentials + methodName: 'indy', }) const tags = transformFromRecordTagValues(record.getTags()) diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 8c222b1a18..733e08190c 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -32,6 +32,8 @@ import { import { anonCredsRevocationStatusListFromIndySdk } from '../utils/transform' export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { + public readonly methodName = 'indy' + /** * This class supports resolving and registering objects with did:indy as well as legacy indy identifiers. * It needs to include support for the schema, credential definition, revocation registry as well diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 4d804f8e99..91cb2407cc 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -196,6 +196,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { schemaId: result.schema_id, credentialRevocationId: result.cred_rev_id, revocationRegistryId: result.rev_reg_id, + methodName: 'indy', } } catch (error) { agentContext.config.logger.error(`Error getting Indy Credential '${options.credentialId}'`, { @@ -209,6 +210,11 @@ export class IndySdkHolderService implements AnonCredsHolderService { public async getCredentials(agentContext: AgentContext, options: GetCredentialsOptions) { assertIndySdkWallet(agentContext.wallet) + // Indy SDK only supports indy credentials + if (options.methodName && options.methodName !== 'indy') { + return [] + } + const credentials = await this.indySdk.proverGetCredentials(agentContext.wallet.handle, { cred_def_id: options.credentialDefinitionId, schema_id: options.schemaId, @@ -225,6 +231,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { schemaId: credential.schema_id, credentialRevocationId: credential.cred_rev_id, revocationRegistryId: credential.rev_reg_id, + methodName: 'indy', })) } @@ -335,6 +342,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { schemaId: credential.cred_info.schema_id, revocationRegistryId: credential.cred_info.rev_reg_id, credentialRevocationId: credential.cred_info.cred_rev_id, + methodName: 'indy', }, interval: credential.interval, })) diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index ca2c1149f0..433f9d0ae5 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -39,6 +39,8 @@ import { import { anonCredsRevocationStatusListFromIndyVdr } from './utils/transform' export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { + public readonly methodName = 'indy' + public readonly supportedIdentifier = indyVdrAnonCredsRegistryIdentifierRegex public async getSchema(agentContext: AgentContext, schemaId: string): Promise { From 95582560cc071e088e4b23d962a13dfcd93e308f Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 31 Mar 2023 16:13:29 +0200 Subject: [PATCH 108/139] chore!: update dependencies (#1388) Signed-off-by: Timo Glastra --- .github/workflows/continuous-integration.yml | 2 +- demo/package.json | 6 +- jest.config.base.ts | 14 +- jest.config.ts | 3 + package.json | 29 +- packages/action-menu/jest.config.ts | 1 - packages/action-menu/package.json | 6 +- packages/anoncreds-rs/jest.config.ts | 1 - packages/anoncreds-rs/package.json | 6 +- packages/anoncreds/jest.config.ts | 1 - packages/anoncreds/package.json | 6 +- .../__tests__/__snapshots__/0.3.test.ts.snap | 426 +- packages/askar/jest.config.ts | 1 - packages/askar/package.json | 8 +- packages/bbs-signatures/jest.config.ts | 2 +- packages/bbs-signatures/package.json | 4 +- packages/core/jest.config.ts | 1 - packages/core/package.json | 22 +- .../__tests__/__snapshots__/0.1.test.ts.snap | 1798 ++--- .../__tests__/__snapshots__/0.2.test.ts.snap | 558 +- .../__tests__/__snapshots__/0.3.test.ts.snap | 30 +- .../__snapshots__/backup-askar.test.ts.snap | 90 +- .../__snapshots__/backup.test.ts.snap | 90 +- packages/core/tests/logger.ts | 46 +- .../jest.config.ts | 1 - .../indy-sdk-to-askar-migration/package.json | 4 +- packages/indy-sdk/jest.config.ts | 1 - packages/indy-sdk/package.json | 8 +- .../__tests__/IndySdkIndyDidRegistrar.test.ts | 13 +- packages/indy-vdr/jest.config.ts | 1 - packages/indy-vdr/package.json | 4 +- .../__tests__/IndyVdrIndyDidRegistrar.test.ts | 19 +- packages/node/jest.config.ts | 1 - packages/node/package.json | 8 +- packages/openid4vc-client/jest.config.ts | 1 - packages/openid4vc-client/package.json | 4 +- packages/question-answer/jest.config.ts | 1 - packages/question-answer/package.json | 6 +- packages/react-native/jest.config.ts | 1 - packages/react-native/package.json | 22 +- packages/tenants/jest.config.ts | 1 - packages/tenants/package.json | 6 +- samples/extension-module/package.json | 6 +- tests/jest.config.ts | 1 - yarn.lock | 6522 ++++++++--------- 45 files changed, 4591 insertions(+), 5191 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index d75c6b89b0..f1afad5234 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -84,7 +84,7 @@ jobs: strategy: matrix: - node-version: [14.x, 16.x, 17.x, 18.x] + node-version: [16.x, 18.x] steps: - name: Checkout aries-framework-javascript diff --git a/demo/package.json b/demo/package.json index 289273b81d..45b43f4a7f 100644 --- a/demo/package.json +++ b/demo/package.json @@ -16,7 +16,8 @@ "dependencies": { "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6" + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", + "inquirer": "^8.2.5" }, "devDependencies": { "@aries-framework/anoncreds": "*", @@ -28,9 +29,8 @@ "@aries-framework/node": "*", "@types/figlet": "^1.5.4", "@types/indy-sdk": "^1.16.26", - "@types/inquirer": "^8.1.3", + "@types/inquirer": "^8.2.6", "clear": "^0.1.0", - "commander": "^8.3.0", "figlet": "^1.5.2", "ts-node": "^10.4.0" } diff --git a/jest.config.base.ts b/jest.config.base.ts index 5848093da6..09cb2d5760 100644 --- a/jest.config.base.ts +++ b/jest.config.base.ts @@ -3,9 +3,6 @@ import type { Config } from '@jest/types' const config: Config.InitialOptions = { preset: 'ts-jest', testEnvironment: 'node', - coveragePathIgnorePatterns: ['/build/', '/node_modules/', '/__tests__/', 'tests'], - coverageDirectory: '/coverage/', - verbose: true, testMatch: ['**/?(*.)+(spec|test).[tj]s?(x)'], moduleNameMapper: { '@aries-framework/(.+)': [ @@ -14,10 +11,13 @@ const config: Config.InitialOptions = { '/packages/$1/src', ], }, - globals: { - 'ts-jest': { - isolatedModules: true, - }, + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + isolatedModules: true, + }, + ], }, } diff --git a/jest.config.ts b/jest.config.ts index c4f6c766dc..49fdadb66e 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -5,6 +5,9 @@ import base from './jest.config.base' const config: Config.InitialOptions = { ...base, roots: [''], + verbose: true, + coveragePathIgnorePatterns: ['/build/', '/node_modules/', '/__tests__/', 'tests'], + coverageDirectory: '/coverage/', projects: [ '/packages/*/jest.config.ts', '/tests/jest.config.ts', diff --git a/package.json b/package.json index 0269aa22bf..222fb1d497 100644 --- a/package.json +++ b/package.json @@ -28,42 +28,41 @@ }, "devDependencies": { "@types/cors": "^2.8.10", - "@types/eslint": "^7.2.13", + "@types/eslint": "^8.21.2", "@types/express": "^4.17.13", - "@types/jest": "^26.0.23", + "@types/jest": "^29.5.0", "@types/node": "^16.11.7", - "@types/uuid": "^8.3.1", + "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", - "@types/ws": "^7.4.6", + "@types/ws": "^8.5.4", "@typescript-eslint/eslint-plugin": "^5.48.1", "@typescript-eslint/parser": "^5.48.1", "conventional-changelog-conventionalcommits": "^5.0.0", "conventional-recommended-bump": "^6.1.0", "cors": "^2.8.5", - "dotenv": "^10.0.0", - "eslint": "^7.28.0", + "eslint": "^8.36.0", "eslint-config-prettier": "^8.3.0", - "eslint-import-resolver-typescript": "^2.4.0", + "eslint-import-resolver-typescript": "^3.5.3", "eslint-plugin-import": "^2.23.4", - "eslint-plugin-prettier": "^3.4.0", - "eslint-plugin-workspaces": "^0.7.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-workspaces": "^0.8.0", "express": "^4.17.1", "indy-sdk": "^1.16.0-dev-1655", - "jest": "^27.0.4", - "lerna": "^4.0.0", + "jest": "^29.5.0", + "lerna": "^6.5.1", "prettier": "^2.3.1", "rxjs": "^7.8.0", - "ts-jest": "^27.0.3", + "ts-jest": "^29.0.5", "ts-node": "^10.0.0", "tsconfig-paths": "^4.1.2", "tsyringe": "^4.7.0", - "typescript": "~4.9.4", - "ws": "^7.4.6" + "typescript": "~4.9.5", + "ws": "^8.13.0" }, "resolutions": { "@types/node": "^16.11.7" }, "engines": { - "node": ">= 14" + "node": "^16 || ^18" } } diff --git a/packages/action-menu/jest.config.ts b/packages/action-menu/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/action-menu/jest.config.ts +++ b/packages/action-menu/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index 9ee1173bdc..1a07638d9b 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -26,12 +26,12 @@ "dependencies": { "@aries-framework/core": "0.3.3", "class-transformer": "0.5.1", - "class-validator": "0.13.1", + "class-validator": "0.14.0", "rxjs": "^7.2.0" }, "devDependencies": { "reflect-metadata": "^0.1.13", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/anoncreds-rs/jest.config.ts b/packages/anoncreds-rs/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/anoncreds-rs/jest.config.ts +++ b/packages/anoncreds-rs/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index c6ab13dfd5..9e74c5ae66 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -28,14 +28,14 @@ "@aries-framework/anoncreds": "0.3.3", "@hyperledger/anoncreds-shared": "^0.1.0-dev.11", "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", + "class-validator": "0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", "reflect-metadata": "^0.1.13", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/anoncreds/jest.config.ts b/packages/anoncreds/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/anoncreds/jest.config.ts +++ b/packages/anoncreds/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 937a2d0fcd..818d0ed29c 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -27,15 +27,15 @@ "@aries-framework/core": "0.3.3", "bn.js": "^5.2.1", "class-transformer": "0.5.1", - "class-validator": "0.13.1", + "class-validator": "0.14.0", "reflect-metadata": "^0.1.13" }, "devDependencies": { "@aries-framework/node": "0.3.3", "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", "indy-sdk": "^1.16.0-dev-1636", - "rimraf": "^4.0.7", + "rimraf": "^4.4.0", "rxjs": "^7.8.0", - "typescript": "~4.9.4" + "typescript": "~4.9.5" } } diff --git a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap index d52cb5052c..2fcae732f8 100644 --- a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap @@ -1,64 +1,64 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the credential exchange records for holders 1`] = ` -Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { +{ + "1-4e4f-41d9-94c4-f49351b811f1": { "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "isDefault": true, "linkSecretId": "Wallet: 0.3 Update AnonCreds - Holder", }, "type": "AnonCredsLinkSecretRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "isDefault": true, }, "id": "1-4e4f-41d9-94c4-f49351b811f1", "linkSecretId": "Wallet: 0.3 Update AnonCreds - Holder", - "metadata": Object {}, + "metadata": {}, "updatedAt": "2023-03-19T22:50:20.522Z", "value": undefined, }, }, - "2c250bf3-da8b-46ac-999d-509e4e6daafa": Object { + "2c250bf3-da8b-46ac-999d-509e4e6daafa": { "id": "2c250bf3-da8b-46ac-999d-509e4e6daafa", - "tags": Object { + "tags": { "connectionId": undefined, - "credentialIds": Array [ + "credentialIds": [ "f54d231b-ef4f-4da5-adad-b10a1edaeb18", ], "state": "done", "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "CredentialRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:54:00.023Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "John", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "99", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "f54d231b-ef4f-4da5-adad-b10a1edaeb18", "credentialRecordType": "anoncreds", }, ], "id": "2c250bf3-da8b-46ac-999d-509e4e6daafa", - "metadata": Object { - "_anoncreds/credential": Object { + "metadata": { + "_anoncreds/credential": { "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG", "schemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0", }, - "_anoncreds/credentialRequest": Object { - "master_secret_blinding_data": Object { + "_anoncreds/credentialRequest": { + "master_secret_blinding_data": { "v_prime": "6088566065720309491695644944398283228337587174153857313170975821102428665682789111613194763354086540665993822078019981371868225077833338619179176775427438467982451441607103798898879602785159234518625137830139620180247716943526165654371269235270542103763086097868993123576876140373079243750364373248313759006451117374448224809216784667062369066076812328680472952148248732117690061334364498707450807760707599232005951883007442927332478453073050250159545354197772368724822531644722135760544102661829321297308144745035201971564171469931191452967102169235498946760810509797149446495254099095221645804379785022515460071863075055785600423275733199", "vr_prime": null, }, @@ -72,9 +72,9 @@ Object { "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "669093c0-b1f6-437a-b285-9cef598bb748": Object { + "669093c0-b1f6-437a-b285-9cef598bb748": { "id": "669093c0-b1f6-437a-b285-9cef598bb748", - "tags": Object { + "tags": { "associatedRecordId": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", "messageId": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", "messageName": "offer-credential", @@ -86,68 +86,68 @@ Object { "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", "createdAt": "2023-03-18T18:54:01.134Z", "id": "669093c0-b1f6-437a-b285-9cef598bb748", - "message": Object { + "message": { "@id": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "John", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "99", }, - Object { + { "mime-type": "text/plain", "name": "height", "value": "180", }, ], }, - "formats": Array [ - Object { + "formats": [ + { "attach_id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", "format": "hlindy/cred-abstract@v2.0", }, ], - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6QW5vdGhlclNjaGVtYTo1LjEyIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY2OlRBRzIyMjIiLCJrZXlfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjQwMTQ0MTA0NDg3MjM0NDU2MTc1NzYwMDc1NzMxNjUyNjg1MTk0MjE5MTk5NDk3NDczNTM4NjU4ODM3OTIyODMzNTEzMDg0Nzk2MDQ5IiwieHpfY2FwIjoiMzgxOTQyMjM1Mzc3MzYwODEyNjY0MDA4MjYzNDQxMDg2MDMwOTMyMjAxNzgzMjM3ODQxODQ5NDg3ODk2ODg1MTYwODY2MTY1MDM3NzI2MTIxNjU0MjcwOTg5NDY3NjAzNDExOTAzODk4MzUwMDAzNDIwODg3MzI4NTUwMTY2MTI1ODMyMjAxOTQzMTkwNzAxMDU4NTAwMDE5ODM1NjA1ODczNDYzOTkwODg3NzQ0NjY3MzU0MjM2Njc3MzcyODg0ODQyNjE5NTEwMTUwOTA2MjI1OTMzMjc1ODEyNjg2NDg3NTg5NjY3ODI3MjAwODcwOTQ0OTIyMjk5MzI3OTI4MDQ1MTk1OTIwMDI3NTc0MDQwNDA4ODU5MzAwMzY1MDYwODc3Nzg2ODkwOTE1MDU5NTA2ODc1OTI0NzE2OTI1MDM2MTc4Njg2NDE5NTYyMzcwODI4MTMzODY2Nzg3NzkyMDcwNjAyNDQzNTkzMTk2NzEzNzcyNDM2NTYzODI0MzkwMDIyNzg4MjU2MzA4NjU4OTc0OTEzMTk1ODYxODUwMTQ3ODE1Mjg5NzQwOTA4NDk1MjQ3NTAyNjYyNDc3NzQ2NTI5ODA3Mzg0OTgxODI5MDc3NTQ4OTI2NzExMDkzNzQ5MjM1ODU4NjUwNDc5NzE5NDI4MzUwMzAwNzUyNjQ0OTg1MTQ5MTMxNjA1NjUzMDIxMDYxNzkwMjY3MzQyNTY4NTkyNTY2MTQ0MDM5NzY4OTg0NTMyNDMzNzk0MzUzNjQ2Nzg1MjA3NDgzOTk2ODQ0OTcxNTgzNzY3NDQ5ODYyODgxMjMxMjI1MzM4MzAzMTQ4NzA0ODczMDEzNDM3MDgyNzY1MTk4OTY2MzE5NTM0OTkyNjk4MzMzMDQ0MDI3MjIyNTYyNTIzNzk3ODk5Mjk2MTQ1NDU5IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiOTE5MTc5NzQ4MTE5NTg5MTY3Njc5MjQxODk5NzY0ODIwNTk0MDc5OTQxNzIzOTgzOTYyNzQ1MTczODM0NDQxMDQ3MjU4MDcyOTE4OTUzNjIzOTQ4MDMyNzI2NDgyNzI2MTEwOTk2Mjk3MDU3NTYwNjcwNzAxOTU1MTkxNDc0NjM0MzQ0ODMxMzg3NTk4NzI2MzMxMjc0NjI4NDU3Njk5NzczMDA1NDMwMDIxNzMwMzg4MzcwMTEyMjc3MzI2MzU4OTgwMTA3ODIzNzUzODc3MTU0NjIwMDkzMjE5MjYyNjAxNDM2NzMyNTgzNDI4Nzc4NDA4OTc0NTQyNzkzMDk0NTQ5MTczOTA3MzQ3OTUxNTc1NjM5NzU2NDg5MTA0Mzk0MTY3NzExMzY1MjM3OTI1MjAwNjk4OTg5NTI5MTQ3OTIzNTYzNDMyODgyMzgwMTg0NzU0NzkzODMwMTE3MTQ1MDAwMTI0NDYxNjkzOTcxMDQ5MjgzNDk1NTE4MDQxMDc5ODUyMzAwMjk0NDM1MjYzOTIwNDU0NTU3MzUxNDQ1MDM3NDI4MDg3OTk2Mzg2NjY3NjU3Nzk5OTYyNzQzNzIyNzA3NzczOTEzMzc0NzIxODUyNTQ3MjkwMTY5MjI5NTAzMTQxOTMwODYzNTk4NTExNjc4NDEyMDE0MzE2MDM2MzYxMzczNDcwOTQwMDEyODcwMDgwMDA2MzE0NzYxNzYzNzUyNzYwODk5MTQ3NzA1MTA0NzQyNjAxNjkxMzMxNjkzMDIwMjg2MjA2NzQ2NzE0MzI3NjU2MjA2NTMzMjk3NDg4MjU2NTM2NTQ3MzY4MjM2OTQ2MDM5NzAzMzc0OTMzNTE0NTc2NDg2NjQyNTY4MjgyNTY2MjMyNDU1NTU5MDY4MzE3NzU5NDM0ODU4NTI3MDg2NjQ0Il0sWyJoZWlnaHQiLCI5MjMwMzkyNDc1NjI4ODc1MjA4OTM0NjM0NzE4MjYzNzA4MDIzOTI1MDU0NjY2NDgzMzgxMzIyMzc3MDg1MjMxMjU4MTM4MzgwOTU1NTk3NDQxNTEyOTYwNDA2MjI3MjUwODgyNjA3NjExMDkwODk3MTM1NDcxNzAwMDIzNDcwOTM2ODg4MDE3NDY5Nzk0ODYzNDk4NzUyNTI3Njc3MjMwMTEwNzg0ODQzNzI0NDUyNTUzODYyOTA2MzM5MDc0OTIzNDU4NTQ3NDYzODcwNzU3OTg5MzMxNzk4OTI2MjM4MjUxMTM2NTYzNjM2MjIyOTQwNDkwMzY3MjQ2OTg0OTU2NTE5MTAzODcwNDE0MDM5NzM2MDE2MDY5MzA2NjQ0NjQzODI4OTgxMTE3OTM3NzYyNDAzODY1Mjc1MDU5MjEyOTY2NzIxOTU3MzM0MTM2ODEyMDI0OTE0MzA4MzAxMzk5MzM4NzMyOTIzNTA0MjA5MDM5ODMxMTc5NjU1NTkyNjg0MjMyMTIzMTI2Mjc4ODQzNDMyOTUwMTk1Mjg3MzE4ODI3NTM2MTMwNDQ3NzM3MTgwMjk3MDE0ODEzNDg3NDQyOTg2NjQ1NzQyNjEyMzE5NzQxNDY2MDMyNTg5OTU0NzYwNjE4MDU0MDUxMjAzMTE1NTAxNDcxNDExMzg3NzU0NDk5MzAwNTU4MTc5NjM5NDAxOTM0NTAzMTMyMDEzMjAzOTg2NzkyMTEzMDAzNTkwODg1NTc3NjgyMzU2NDY3MjA5NTUwNjQxODQxMDYyNTkzNDYyODIwODg3NzgxNDYyODM3ODkzODcxNDM4MzM3Mjc5MTcwMTExMTQ5MTU4NDMzNDE0ODI1NTkyNjcyODU2MzM5OTM4NTgyODg2NzM3OTIwMjc1MzI0MjEwMTUzMjE5MjI2OTYiXSxbImFnZSIsIjkxNTg1ODk3NDkwNzE0ODA3OTY2MDYzOTg5MjE1NTMxNDkyOTQwMDI5NDcyMTM4MjgwNjcxNjcyMjQ0NjY5MDc5NzIyNTQyMDU0NTU3NjY0MTcxMDI1NzM1NjQ4NTIwMTM4ODQ4ODAxNzIyMTc4MTcxMTA5NTc0MTMyNTExMzM1MDEwNTc5NzExMzcyODM5MjI3MDExOTg4MTUyMTEwMzI4MTE5MjkyMjI4NjM3MDU4MDQ3NzYwODYwOTQ0NTY3MzQxMjY4MTY4Mjk3NjE5MDM2ODEwMjYwODM2NDI1NDkwMzU3NjE4NzM4NTYxNTY2MTUxODQ3MzIxNzM1MjQ5ODk1MDU5NTY2OTQxODI5MjE0Nzc0MTA0NzYyNTQwMjcyMjk2NjE1NTE3NjUwMDcyNDQyMTI0NjY5MDEzMTc1ODAyMDk5MDQxMzk3MzE5ODQ0OTA2MDgwOTYxNTcyMTcwNjg2NzgzNDM1Mjg2MDUyMzE5ODY3ODExMDE5MjAxMDYwODM2OTM3Mzc0MzM0NDM5MTQxMDAzMTI3NTcyNjgzNTgwODI0OTkwOTg3MjE5MzU4NzkzOTM2NTU4Nzk3MjI0MDQzNTM1ODA5NzMyNzgxMjE1NzEwNjI1MjQzODYwNTk4MTk0MjU2MjAwODkwOTA3ODAzMDcyMTAzNzc3MzkwODk4MDczOTgyNjY3Njc1ODg0MjI3MjU0Mzc2OTI5Mjg3ODQyNDE0MTE0MjcwNDQwMTEzNDUxNjk4NzE5Nzc5NjQyNTI4MDA4NDM3Mzk5NjI0NTE3OTM4Nzg5MDc3ODE5ODA0MDY5MzcxOTM0NzExMTIyNTQyODU0OTg4MDA0Mjc4NDkwMjAxNTk2NjE0MjUwODc3NDYxMDczNjc3NTUzNzYxMTMyMTA5Nzg3NTQ2ODE1ODk5Njc2NCJdLFsibmFtZSIsIjYyNzgwNTIwMTM3MzI3NTUzMDc3MDg4NTE4NDg1NDYyMTA0NjEzMjEyNzY3ODUwMzYwNTc3NDQ4MDUxNTk5MTMxMTM1NTI2NzQ3Nzc2NzMzMDg1MDMwODcyMDE1OTM2MTI2NzE0MTIxMDgxMzg3ODU2MTkwMTkzMzI3ODY3OTE0NTEzODM2NTQ1OTY4Mjg1NTc5ODEyODMxMDI4ODc2Nzg1NzI3OTQ2MTEwNzg5Mzc0MjcyODgzMzkyOTgwNDkwODk3NDkwMTc5MDQ0ODM0NTgwMzQ2ODY4NDI2ODc0ODU4NTY1OTg4NTUyMDcwNjI1NDczNjM4MDM3Njc5NTU1NTk2MzE5MTc3Nzc5OTcxMTIxMjQzMjgyMTIyOTQ2NjY0ODMxOTgxMTg3MzQ3MzcyMjkxMjYwOTM3MzkzNDA1ODk5OTI0NjM4MzE3ODI5MDczODMxMjI4ODc1Njg5MTcyMTg4NjIyMDI5NzcxNzM5MTQ5NDY2Mzg3NTM5NjkyNDQ5NDU5MjczNDI5NzM5MjMzNjkyNTEzNDA5OTkyNDgxNTQ4ODk0NjAzNjM3MTYzNjA4MTM0MTAzMTk3Nzc3NTM4OTYwMDcyMjcyMzYyNzM4NDM1MTM3MDcyNzIxMjExMDYxNTg4MDE3ODczODg3MTEwNDA2OTk1NDQ4ODIwMDEzMDA5MjgyMzk0OTczMDMwMDI5MTY3NjQ5NzY1OTI1MTUxMzY4NTg5OTkyNzMyMDE1ODAwNjAzNzYxOTI3MTg3MDM4MDkxNDY3MDE1MjA3MzIwNDczMDM0NDA3MDIyNDA0NjQ4MTI0NTk2NjQwNjU1NjY1MTIzMzY5Njc0ODI2NDE3MjE2ODUxNTM4Njc1NTM3NzAwOTg4MTQzNzE1NTE3NzMwMTM4NjA4NzkxMjcyMzM0MDUyMzY4OCJdXX0sIm5vbmNlIjoiNDE0MzQ4Njg0NDk2OTAxNjkyMjI2OTY0In0=", }, "mime-type": "application/json", }, ], - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "DXubCT3ahg6N7aASVFVei1GNUTecne8m3iRWjVNiAw31", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3000", }, - "~thread": Object { + "~thread": { "thid": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2023-03-18T18:54:01.134Z", }, }, - "8788182f-1397-4265-9cea-10831b55f2df": Object { + "8788182f-1397-4265-9cea-10831b55f2df": { "id": "8788182f-1397-4265-9cea-10831b55f2df", - "tags": Object { + "tags": { "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", "messageId": "c5fc78be-b355-4411-86f3-3d97482b9841", "messageName": "offer-credential", @@ -159,86 +159,86 @@ Object { "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", "createdAt": "2023-03-18T18:54:00.025Z", "id": "8788182f-1397-4265-9cea-10831b55f2df", - "message": Object { + "message": { "@id": "c5fc78be-b355-4411-86f3-3d97482b9841", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "John", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "99", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiODUxODAxMDMyNzEzNDg5NzYxOTg5MzAzNjMzMDkzOTEyOTExMDUxNjI0OTQ0OTYzMTgzNzM2MDY3NDkwOTc2MDYxODEwMDgxODkxMzQiLCJ4el9jYXAiOiI4NDk0NDg4MjQzNTk2NTkwOTc2MjQzMjc0NDg4ODk2Mjc1NTcyODAyMTQ1ODE5NDQzNTE0NzQxMzk1NDI1NjM5MzQwMTczMDIzMTQ5NzI3MDY5NzMzMzQwODgzMTU4MzQ1NTYzOTA5OTcxNDMzNTg1MjMwODAxNTYyMTM0NjczNjM1ODg5NTA3Njg5ODQwOTgyODU5Mzg1NjA1MTc1NTkxNDYxOTkyMDExNzU2Mzg1MTI3MTQ3ODgxNDMwODEzNjYxNzY0MDU5MzE0ODk4MTc2NzQzMTQ5MjYzMDMwMDQ1NzMwMDMzMzI2NzgyMzg1OTY0NjcxMzg1ODQ2MzcxNjQ5MzQxMTg2MDM5NjE4MTQwOTIwMDUxMzg1MDAwNTYxMDcyMTc5NTEyMzc5Nzk0OTU4NjE1ODIyODI2OTExNzIwNTQyNTE0MTQ1NDc5MTAxOTUyMzM4MDMwMDY1MDk5NjcxOTU2OTMxMzE2NjE5MjM0NTQ0NTE5NTQ1ODQ1MzA4MzgxMjQyNTM0NDcyOTc3NjY0MjAwMjc2MTMyOTgxODE1ODAzNTIxOTExMzk4ODkxMjE0NjE1NzA1MDM2ODM2ODU1NDU1NzY4ODg4MTUxNDgzODAyNDcyODQyMzczNzE0MTI0NTYwMzIyNTI3NDE4MTEwNzYyMjgyNzY4NzMyNTIzMDQyMDA3MDY2OTk2ODIxMTQwMzE1NDg0NzI4NTM2NzIwNDI3MDg5MTI2NDk1NTAzMjc0ODQ4MDM3MjUzOTM3NjI3MDU2ODUzMTQ4NjE5NDA4NDYxOTI5NzEzMjM4MjEwNDc4MjcyMTIxNTUwNjQzODc4ODM1NDYwMzY1OTIwMjE3NTk5NDYyNDUzMDMyNDQ4MjYyMTM3NjE5ODY0OTU4MzA1MDE3MjA4OTYwNDc1MTQxODgwMTMiLCJ4cl9jYXAiOltbIm5hbWUiLCI1MDcyNzU2NDE2NDA2ODIxNzU1OTc0MzUxMTg0NjE1NjA4NDY2NTk3Mzk0NzA2MTY1NDg2ODAzMjc3MjMyMzQyOTk4MDA0MzY0OTU0MTczMzc0NDIwOTc5NTkwMDcyODgxNDgxNDA0MTg2OTExODg5NzQ4MTgzMzQ1OTk5NzQ0NzgxMTQ1MTMwNzEyNDIzODY0Nzc1MzQzNjAzNTk2NDM3Mzg4OTgzNTExNDAzODA0NjEyNjU1MDE5NzQ4MTI5NDk3ODY2NTcwMDQyMjcwNDQxNDQ5MjYwODY0NzgyMzI5MjAxNDEzMTc5ODU3NzA0MjM5OTMyMTg4NTc4NzE3MDczNzM3NjUyNzY5MzY5NDg4OTgxNzg2NDQwNTExODAzMjMzNDMxNzA4NDk4MTU2NTA0OTUzNzkzNjU2NjQ2NzMyNTU4MzQwNDI2MDI1MjA3NTk0OTIwMDY4OTc2OTQ4Nzg2OTUxNzM3MDIwNDQ0NTA5NzYyMDQ2MzIzNzA0MDQ3MjU1ODU3NDE5ODE3MDc5NTI3NDgzNTE1NDY2NTAyMDkzOTY1NDMzMzk3MjQ1MzA4MjQ5MDgyMTQ4Mjc4NDA1MzI5Njg1Mjc0MDYwNjk0MzI0MTI2ODgxMjkyMDIyMjY1ODczMjk5MDU0NDU1OTA5NzkyNjUwNjAyMTk0NjUzMjYxMDk0ODYwOTc2NzA4ODE1ODgwMjExMTY0MTkwMDM0NjY0MzI2MDc3NjcwNzkyMDE4NTE2MzMzNDI3NjkwODYwMjIxODEwMzk5MDgxMjc5NjAwNTYzMjk3MjI0NjM0MDM0NjcxNTIwODE5MzU3NzQ0Njk2NzU1Njg1NDI2NjIzMzAwMjQ3MDUwODE4NTQ2MDM2NjA0NjMxNjcyNzE5MjI0NDA4NTE2NDM4NTgxMDM5Njk4NzI0MSJdLFsibWFzdGVyX3NlY3JldCIsIjU2MzYzNTgyMDQ5Mjg4OTY1OTg1MDA4NzgyMzU0NjgyNjMwNDkxMzQ3MTM1NDIxNTAyMDEyMTIwMzI4MDI4ODIyMjUyMzg4NjgwNTMwNTgxMTcwMTgwNDU1MTcyNTc3ODkyMTEyMTY1OTM0Mjk5NjUyNzAxNDExMzUyNDkzMzkyODU0ODI4NzMyMDQzMDI0MDI0MzM0MzMzNzc0NjEyOTEzOTUyMjAzNjM1NDk2MDQ0ODMzMDI5NDE2NjUwOTU5NjE0ODgzNTUwOTMxNzgzNTA5MzE1Nzg4MDEyODQ0MzAwMDQwMDE5MTY5MTc3NTI1OTgxMTU3OTkwNjQzMDcyMjQyNzcxMjU0MTYyNzMxOTU4NzI2Nzc1NjYwMjkxODIzMDcyNDk1Mzg0NzM5MTcwODc4ODMxNzkxMjQzMjEzMjU5MzA5ODQxNjU3MjUwOTg1NzMxMjEyNzE2MDM2MDY3MDUxNjM2NzA0MjA1NDEzMDk2MDU3MTA2NTM2MTI2ODUyNDU0NzcwMzQzMTMwMTczMjAwNjEzMDIxOTE4MzgzMDQxOTU4MTkwOTE2NzQ0NjU4NTI0ODA1NjM4Mzk2OTY3OTA3MzIwNjY1MDU1MzcwMjY0NjAxMDczMjc5NDMyNjM5MjM3Njc1NTA0OTg1NzQyNTI4NjYwMTAyMDEzNzIxMzA2MTE4MTg0NDk1MTEyNDQ2NDYyNDc2NTkwMjYxODkxMjA0OTQxOTA4MjMyNzMzNDA3MTg4MDA3NzE2NTA2OTUzMDY0Nzc5NDk5ODExNzI0ODI5NjcyNjY2NzIyNjIzOTAxMTc1OTk0NTIyNjkwMjk1ODI0MDgyNzY5NjQ0NDYxOTAxMDk2NzI3MTE5NzAzMjUzNzI4NjY3MTU1MzA5MDYzNDUyNDY2MDY3NzU5NzIwOTgyNDA3MiJdLFsiYWdlIiwiMTM2NTQxMjE0MjM5MTcyNDQxNzQ1MjU3MjcyMDI3MTA4NDYwMzU0MjgxMTA2OTA2MzYwNDIwMDE0NjUyMDIxMDgyNDEzODM2ODEyMjk3NjY3ODk2MTYzNDkzMjM4NDIxNDI4NjMyNTMxODE0ODk4NzkwMDg4OTg2NjgyMTE2OTAyMzc4NDgwNTE4OTUxNDExNzg1OTk3NTk5MDMyNDYxNjExNjIyMDUyNjMzMDQ5ODYxMzc5MTQzNzI4MTM5MTUyMDkyMzI0ODc3MjMxMTYwNTgzNzA5NjE0MzA1NzQ1MjA5MjQwNjU2MDU4NjY3OTMwODEzNzYyNDY5MDc2ODc5MTk1Nzg0Nzg4NTE2NjI3MjgxMDY0NjE3MzgzMDc4Njc5MTkwODIwMzQwNTgwNDY2MjU3ODU3NjA1MTc2MTg4NTI3OTMxMDI4MTMzNTY5Njc0Mzg2ODAwMTA2MDE2MDg1Nzc0OTcyMzI1NTAyNDA2MTY0OTY0MjU2OTUxNDI3ODAxMTQzNTQxMzUzMzI0Nzg0MzA5OTY4MjIyOTU1NDk4Njk3NTAwMDUwMzc0MDg0NjIwNzQ4MTk0NzIyMTI2NjE2OTY3OTY3Mzc1NTM3Nzc5NTc4NTMwMDIxODExNTA2MTIxNjcxMDUwNDgzNTM2MjA3Njc3MTg5NDQwNjEwNzk0NTcyNzI5MTgzMzAyMjM1MDkxMDg4NTU2ODc5NTg3OTE3MDMzMzQyODcyMzg2NDQ5MTQ0NzgwMDYyNjc4MzA3NzE4MzU1MjQ5MTUxNjc5MDA1MzkxNDA5NDE4OTQxMjEzNDkxMjQyMjg2NTAwODcyMzQxNDI3Nzk1MjQ1ODYzODE2MDY2NDY3NDkxOTg4OTU3MDEwNDIxNDA3NDkyMDUxOTc0NTMwNjIxOTk1ODU0ODczNTM5Mjk3MyJdXX0sIm5vbmNlIjoiNzk3NjAzMjE3NzA5MzM1MzAwMTcwODI4In0=", }, "mime-type": "application/json", }, ], - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3000", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2023-03-18T18:54:00.025Z", }, }, - "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3": Object { + "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3": { "id": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", - "tags": Object { + "tags": { "connectionId": undefined, - "credentialIds": Array [], + "credentialIds": [], "state": "offer-received", "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, "type": "CredentialRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:54:01.133Z", - "credentials": Array [], + "credentials": [], "id": "93a327ad-2bf3-4ec4-b01c-bdd58ba2f6e3", - "metadata": Object {}, + "metadata": {}, "protocolVersion": "v2", "state": "offer-received", "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:53:44.041Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.4", "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3": Object { + "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3": { "id": "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3", - "tags": Object { + "tags": { "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", "messageId": "578fc144-1e01-418c-b564-1523eb1e95b8", "messageName": "issue-credential", @@ -250,47 +250,47 @@ Object { "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", "createdAt": "2023-03-18T18:54:01.369Z", "id": "b71d455b-9437-4ef8-b4f3-b6a0dd6bbfb3", - "message": Object { + "message": { "@id": "578fc144-1e01-418c-b564-1523eb1e95b8", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJhZ2UiOnsicmF3IjoiOTkiLCJlbmNvZGVkIjoiOTkifSwibmFtZSI6eyJyYXciOiJKb2huIiwiZW5jb2RlZCI6Ijc2MzU1NzEzOTAzNTYxODY1ODY2NzQxMjkyOTg4NzQ2MTkxOTcyNTIzMDE1MDk4Nzg5NDU4MjQwMDc3NDc4ODI2NTEzMTE0NzQzMjU4In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjMyMTIwMDQ1ODc4MzIxMjcyMzA1ODI5MTc3NzMzMTIwNzE1OTY5NDEyNjkwNjUyNDQ4OTc0MTA4NzEzNjU0ODc3NTg2MzIzMTI3ODk2IiwiYSI6IjIyMjY0MTYwNjIwODcwNDUyNTExMjcyMzE1MzMzMDA0MjQzMzY3NTM2NzM3NDMwNjM1NjExMjcwMDkwOTE4NDMwNzc0ODEzMjAzNjQwNjMxMjIyNDczMzk0MjQ4MTgzMDIzMjIyNzExNDUwMzQxMDcxOTQyNDQwMDgwMjY2Nzk1Mzg5Mzg5Njc1NjYwOTUzNTQyMDE4OTA3NjQ3NzI4OTQ4NjY1MzA2Njg0NjExNDU1NTI5NzM5OTY1NDcyMjQ2NDQxMzE1NzAxMzM1ODc1MDY3MjExMDk3NzcyOTgwMjU1NDIxMDMzMTI1MjAyMTQzNDk3NjMyOTAyMjM1NDAyMzU5OTA1MzY5MzE4MjI1NTc4MjUxNjY4NTYzNzc1NTY0MDM2MjUxNzE0Mzk3MTEzNjQ3OTg0MjcxMTE5MTU2NDQ3NjI1OTk1NjE5MjAwMDk4MTgzNzY1NjkzMTg1ODEzNjA1NDU3OTQwMzE0MDU2MDkzMTI2MzQ3OTU5MzYwODIyMzg0OTEzODg3Mjg3ODI2NjkyNDIyNDMyNDUwMDA5OTYxNjQ2MjMzNTE3MjY3NDU1OTkyMjA3MTE3Mzk5NzU1NjY3MTA3MzM1NTQ0MzEwNDQwNDE1NDE5NTk5NTA1OTgxMzkwMjk5NDUxNzQyODg4NDg0MTc0NTU5MDA5NDgwNjU5MDk2Nzg2ODI2MDgxNzc3MzcwNTk1MTU3OTg5NjQ1MDYxNDI2OTA2ODM2MDk5NTU5MDQ0MDI4ODM2MzYwOTM2MDkwOTkxNjA1OTU0NTM2OTQxMjgxODQwNzk2MjkxODc0ODk2NDEzNTM5IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDExMzE1MTE0OTUxODg0MDQ5ODkyNjAwNTY3NTgzMTc4ODkwMyIsInYiOiI2NTE5ODc4MzYyODQ5NzExNDY4NDkyNDM1OTM3MDU4NzMzOTYxMTkxNjA3NjI4MzUzNjkxMDg1MzM5MDMwNDU0OTkyODc0ODYyODkyNDg4ODYzNTA3MDQ1MTM1MzA4ODI1NDA2NzYwMTQwNDQzNzM0NDYzODE5NTM2MzE0NzcxMTQ3MDk4MjU2ODMzNTc2MjIwNDI5ODQyNjc3NzMwMzQwODYwNjE2NTcxNzc5NjU4OTIxNDY4Mjc0NTUwOTc5NjYyMDkxNzEwNDU5MDk2MDgzMzYzNTc1Mjc0MjQzNzIyMzIzOTIxMjY5MDYyMjE0NjQyNzQyMTI0MzQ4MTY0MDUxNzE3MTk5MTkzODY3NTM3NTEzNjYzOTY1ODQzMDI5MjAxODA0OTE2MTEzNzMxODYzOTUzNjQ5MDkwNDgzNzMyMTkxNTQ2MTEwMjAxNTg0NzMxODg4NTE5NjA2MjE1OTkyNTgxNzk2MDg2NzUzOTE5NzUxMjkwMDI3MDI4NzczMTAwOTc5ODI5MzQ5NzA0MTUyMDEzNjg2MzU1MzM1MjIyNjU5MDY2NzE0NDQ2NDc4NzY3MTE5NDE4MjY3OTg5NTAyNzc4MjMzNzM3MjM4MjU1MTQxNzQyMjk4NTU3MDY2NzA2MTM0NzYwMjQwMzY3OTMzMzc5NzYzMTc5MTI1NTI4MDQwMzkxNjQwNTIyNTM5NjE5NTU0NTE0NTk4OTUxNTg0NjA3MjYwNzk1NzE1MDMyMjM4NTQ3ODMyMzA0NTY2MzQ4NjYzMTc0NzQwMDE2MzQ2NTU2MTM1ODc4MzgxNTYzODQ2NzU0MzQzMjk0NTIzNjc0NDI3NjQxNjAxNjAwNjE2NzI3NjEyMzc0MzI2NzY4ODA5NjAyNTE5MTAzOTk3NDY4OTg1NTg3Nzg4MjI3Njc5MzQ4NTgwNzk1OTkyOTkxNzMzMDg5MTUyMTg2MDg4OTU2MTg2MTQ0OTkyMDI5OTI2OTUxOTU0OTQyNjYwMDUxOTM0MDc5NzkxODI1NzA2MTExNzg0MDU2NDM2OTA2MDgxMDQ2MDQ5ODI0ODE1NDE0MTc5NTMzMDA2ODE4NzQ3NzgwNTQ5In0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjEwMTUyMDI0OTk1MTUxMzcyOTgwNzI5NDM1MTQ5MzgyMzQxMDQ3MDIzNjI2NjA4MDc3ODg1NzQ2Mjg4MTE3MjI2OTAyNzM4OTY5OTEwMTU0NjgzNzM2MzI4MzQ2MDg0MjcwMDA3MTY2NTg3ODI5MjE2MzEzNDc4MDk3Njc0MTI0ODU2ODIyMjA4NzI0Nzk1NTE0ODgzOTYwMzE5NDc5OTg4NzAzNDUyNjI4NjYxMDc3MTg3OTIyMzA1NDc5MDE2NzQzOTk0NzYwMzE5NzI1OTExODk0MjM2NDMxMDkxMTIyNTUxNTU0NzgwODg0NjQ2MjE0MTUzMDUzMTM2NDMwMTk4OTA5MTM0OTk4OTM2NjY3MzI4ODI2MDcwNzEzMzk0NDg0NDI0ODUxNjkxMzUxNDc0NjAxMjIwODk2NTIyMDYzNDA5NzA4NDA1Njk2MzY5MjA0MzU0NzE1MDkxMzk2Mzc4Mzc3MzA0ODk3MzMwOTM0Mjc2NTQyNjE2NjAxNTk1ODI5NzgxOTg3NTMyNzkxMzIyNTgzOTE1Njk1OTY2MjM3MTc4Njg1NTMzNTE3MTQxNTAyNDE3MzQxMDIzMTA1MTczMjMwMTcwNzUzODYwMjgxNDAxODk4MDE5OTQwNjA2MzczOTYwMzYxNjA3NTE2NjgyMDg4MTc1NzU4ODA0Mzg4MTM5MTQ0MDkwMjg5MzI5NzMzNTQ1NDg4MjUyNjczNDIyODkzMzc1MzE5ODQ2OTMwOTIyNjIwNzAzMTEwMDgwODU5OTE4ODQ0MzgyOTQ3ODczMjAwNzA4MTY2MzA0NDk4ODk0MDA4NTMyIiwiYyI6IjIyNDQyNTM5MzYwMzYzNjQyODI1ODkxNTc5ODgzMDE5Mjc3Mjk0NTQ2MjUwMDEzNTM3MzI2OTY2NzM3MzE0NTUxMjEwMjU3MjU2NDU5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, "mime-type": "application/json", }, ], - "~please_ack": Object { - "on": Array [ + "~please_ack": { + "on": [ "RECEIPT", ], }, - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3000", }, - "~thread": Object { + "~thread": { "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2023-03-18T18:54:01.369Z", }, }, - "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64": Object { + "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64": { "id": "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64", - "tags": Object { + "tags": { "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", "messageId": "2a6a3dad-8838-489b-aeea-deef649b0dc1", "messageName": "request-credential", @@ -302,34 +302,34 @@ Object { "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "DidCommMessageRecord", - "value": Object { + "value": { "associatedRecordId": "2c250bf3-da8b-46ac-999d-509e4e6daafa", "createdAt": "2023-03-18T18:54:01.098Z", "id": "e1e7b5cb-9489-4cb5-9edd-77aa9b3edb64", - "message": Object { + "message": { "@id": "2a6a3dad-8838-489b-aeea-deef649b0dc1", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiY3F4ZW8ybzVIWmVjYWc3VHo5aTJXcTRqejQxTml4THJjZW5HV0s2QmJKVyIsImNyZWRfZGVmX2lkIjoiQTRDWVBBU0pZUlpSdDk4WVdyYWMzSDozOkNMOjcyODI2NTpUQUciLCJibGluZGVkX21zIjp7InUiOiI3OTE4NzUwNzgzMzQxMjU3ODU3OTUzMjc2OTU5MjcxOTcyMDQwMjQxMTU1MzcyODEwOTQ2NTEwMjk5MDA1ODEyMTcwNjkwMjIzMTQ2ODU0NDk1MTI1NjQ1MTg3ODQxNzk0NjA3OTUwOTQ1OTM3MDYxMjk1ODgwMjIxMzE2NTg1MTIyNDY1Mzk1MjAwNDQ3MDIwNzAxNDA0NjEwMDc4MzkyMjA4NzkxMjk5NzYwMzM4OTIxNDMzMDc1Njk2ODU0NTY3MTIzNjYxNTYwNDMwNDE3NzQwMzc5MzA4NDQzODcyOTU1NzAwNTk1MTg2NzcxODY3MzM5NzQ5NDgzODYxNTQ2NTE2MTU4NTM5MjkyNDQzNTQ3OTg3MDUwMzE4OTAyOTI5OTgyNzMzMDk1ODk4MDIyMjg2OTk1OTQwMjkzMTg3NTg5NDgwNTgwNTAwNjM0NzAyNjQxNDM0MTgxNjIwMTU4OTU3MzUyNTE1OTA4NDE2MjI4MDQ0NDA2OTU4MTk1MDg4Mjc0ODI4Njc3OTQxMDgxOTczOTg3NjU1MDEzNDUxMzA4ODQyMjYyMzY4MTQzOTExNjIxMTE0NzYyNTk3Nzg1MDczMTM4MDg3NTQ2MDIyMzc1NjQxODQ5ODI2OTg2MjYwMDE5NTAzNzE3OTk0NjM3MzIyNDExNTgzNTY0MTQ1NjcwMTM5OTQ1MjQxOTU2Njk2MDQ3MTQzNjA4NjQ0MDM5OTg2NTYwODUyMzA1MTczMjUxMTUyMDIzODI5NjI3MzQyODM2NDI3MjkwNDQ5NTA3OTY0Nzk4MTQ2NjkzOTUxMDkwNzUwOTAyNiIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6Ijk4MjIzNzMzMDcwMDk1NjM0MzU5MTE2MDEyOTgzMDcyMjc5MjcxMzk1MTg0NjA0NDcxNDQ5NzA1Nzg0MTEyNDAyODMwNzIyMTUxMzIxIiwidl9kYXNoX2NhcCI6IjU5ODA0MTY4ODAxODk1NDAzMjkwMzczMTA3NzA4MzUwODc1NjY4MDcyNDY3ODAyOTcyOTIyNjUzNDE5ODU2MjMyNTIzNDI4OTUxODQ4NjEyNDE1MjM5Nzk3Mjk5ODY2MzIxNjU5NDQ1MTM1NzQ4NzU2MDY1NjgyOTY5MjY4ODI5MTYyMDA0NjQ4NzYwMzg4NTg4NDkyNjg1NDI1MTg1MDU2OTAxOTkxMjcwMzYwMDk3MDc5NjEyNTYxMzY4NzU1OTcwMjY5MjI4MDYzMjMyODU0NzI0NDkyOTA5Njg5MDMyOTg4NjYyMjk5Mzg3MDU2NDEwNjU5MDU3MDUwNjE0MDQ2NzE1NjA0NTgyMzM2NTg4MjMxMjI3MTEzMDEzMDQxMTA0NTU2NzM1MDE3ODUwNzUzODcwNjc2ODYxNDA4MjA0NzkzMDIzNTYwMDEwNTEzODAwNzA4MjAyNjAyNjQ0Mjg2NzI4NjAyOTk5MzU5MDcwODQxMTQ5MTAzMjA5MzY0ODkyMzMzMDYwMDgzMTA5NDIzOTQ5NzE4NTk5NjEzMzk2NjIyMjc4MTExMzk5ODU0MTcyMjMyNTQzOTk1Njc5NDk3Mjk1Nzc1NzA0MjA0MTQxOTU2NDI1MDc4NjYzMzgwMDA1Nzc2ODY2MTcxNTY4OTU1NjE4NjAwMTA2NzkxMjIyNDkyODA2NzI1ODU1NDY2Nzk4OTEzMTc2NDcxMDY3MTk5ODQ2ODEwNDI5MDIzMDc3ODI3OTc1OTIzMDIzNjU3MTg0NjkwNzE0MjkxNDk0MDc5MTM1NzYyOTUxNTc0MjMzNjMwMjExNDQ1Njc3NzE1Mzg3Mzc1NjkyMjAzODE3NDczNDk5NDExNzE5MzIwMjczNzExOTIzMzM3MzYzMTAyOTkwMDcyMjE2MjYzMzUxMzMxNTk4ODk1OTU3MzU1MDc1NTEzODE0NTUwNzkyMCIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiNTY0MTY1NjA2NjM0MTUwODgzMjg2MjM1NDM1MjUyODQ3MjAxMTk4NTQyNDAxMjYzNTY2MDQ2MTA3OTU3NzI4NTQ0MTMwMjgzNjUyNjQzOTI0NDc2NTU2NTIzNzg0ODI1OTgyMzMwMzc4NDI4OTM0MDkxNDcwNDM0OTAwMTM3NzkwNDkxMjM4NTA4ODk2ODQxMzkwNjQ4MTA1NzY5ODYzMzI1OTAzODI1Mjk1MjU2OTQ0NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMzE1MDIwOTY0MDY4NjgyMDQ0Mzc4MjEifQ==", }, "mime-type": "application/json", }, ], - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "cqxeo2o5HZecag7Tz9i2Wq4jz41NixLrcenGWK6BbJW", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3001", }, - "~thread": Object { + "~thread": { "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2023-03-18T18:54:01.099Z", }, @@ -338,28 +338,28 @@ Object { `; exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the schema and credential definition, and create link secret records for issuers 1`] = ` -Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { +{ + "1-4e4f-41d9-94c4-f49351b811f1": { "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "isDefault": true, "linkSecretId": "Wallet: 0.3 Update AnonCreds - Issuer", }, "type": "AnonCredsLinkSecretRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "isDefault": true, }, "id": "1-4e4f-41d9-94c4-f49351b811f1", "linkSecretId": "Wallet: 0.3 Update AnonCreds - Issuer", - "metadata": Object {}, + "metadata": {}, "updatedAt": "2023-03-19T22:50:20.522Z", "value": undefined, }, }, - "1545e17d-fc88-4020-a1f7-e6dbcf1e5266": Object { + "1545e17d-fc88-4020-a1f7-e6dbcf1e5266": { "id": "1545e17d-fc88-4020-a1f7-e6dbcf1e5266", - "tags": Object { + "tags": { "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222", "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", "methodName": "indy", @@ -367,16 +367,16 @@ Object { "tag": "TAG2222", }, "type": "AnonCredsCredentialDefinitionRecord", - "value": Object { - "credentialDefinition": Object { + "value": { + "credentialDefinition": { "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", "tag": "TAG2222", "type": "CL", - "value": Object { - "primary": Object { + "value": { + "primary": { "n": "92672464557302826159958381706610232890780336783477671819498833000372263812875113518039840314305532823865676182383425212337361529127538393953888294696727490398569250059424479369124987018050461872589017845243006613503064725987487445193151580781503573638936354603906684667833347097853363102011613363551325414493877438329911648643160153986822516630519571283817404410939266429143144325310144873915523634615108054232698216677813083664813055224968248142239446186423096615162232894052206134565411335121805318762068246410255953720296084525738290155785653879950387998340378428740625858243516259978797729172915362664095388670853", - "r": Object { + "r": { "age": "66774168049579501626527407565561158517617240253618394664527561632035323705337586053746273530704030779131642005263474574499533256973752287111528352278167213322154697290967283640418150957238004730763043665983334023181560033670971095508406493073727137576662898702804435263291473328275724172150330235410304531103984478435316648590218258879268883696376276091511367418038567366131461327869666106899795056026111553656932251156588986604454718398629113510266779047268855074155849278155719183039926867214509122089958991364786653941718444527779068428328047815224843863247382688134945397530917090461254004883032104714157971400208", "height": "36770374391380149834988196363447736840005566975684817148359676140020826239618728242171844190597784913998189387814084045750250841733745991085876913508447852492274928778550079342017977247125002133117906534740912461625630470754160325262589990928728689070499835994964192507742581994860212500470412940278375419595406129858839275229421691764136274418279944569154327695608011398611897919792595046386574831604431186160019573221025054141054966299987505071844770166968281403659227192031982497703452822527121064221030191938050276126255137769594174387744686048921264418842943478063585931864099188919773279516048122408000535396365", "master_secret": "26619502892062275386286102324954654427871501074061444846499515284182097331967223335934051936866595058991987589854477281430063143491959604612779394547177027208671151839864660333634457188140162529133121090987235146837242477233778516233683361556079466930407338673047472758762971774183683006400366713364299999136369605402942210978218705656266115751492424192940375368169431001551131077280268253962541139755004287154221749191778445668471756569604156885298127934116907544590473960073154419342138695278066485640775060389330807300193554886282756714343171543381166744147102049996134009291163457413551838522312496539196521595692", @@ -386,7 +386,7 @@ Object { "s": "14126994029068124564262196574803727042317991235159231485233854758856355239996741822278406673337232628669751727662479515044513565209261235580848666630891738643990084502393352476512637677170660741636200618878417433799077613673205726221908822955109963272016538705991333626487531499501561952303907487494079241110050020874027756313402672435051524680914533743665605349121374703526870439925807395782970618162620991315112088226807823652545755186406850860290372739405126488851340032404507898084409367889215777693868794728141508635105180827151292046483128114528214465463152927678575672993454367871685772245405671312263615738674", "z": "90415953543044389740703639345387867170174070770040351538453902580989033567810029650534915348296084212079064544906463014824475317557221991571331212308335167828473551776349999211544897426382305096215624787217055491736755052175278235595298571339706430785816901931128536495808042995635624112114867111658850659510246291844949806165980806847525704751270260070165853067310918184720602183083989806069386048683955313982129380729637761521928446431397104973906871109766946008926113644488012281655650467201044142029022180536946134328567554182760495139058952910079169456941591963348364521142012653606596379566245637724637892435425", }, - "revocation": Object { + "revocation": { "g": "1 1864FF219549D1BC1E492955305FC5EED27C114580F206532D2F5D983A1DD3BD 1 0414758D7B6B254A9CA81E1084721A97CA312497C21BB9B16096636C59F9D105 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", "g_dash": "1 2327DA248E721E3935D81C5579DD3707882FFB962B518D37FB1112D96CC63611 1 164989452135CF5D840A20EE354DBF26BEEC74DE7FD53672E55224BEE0228128 1 0634D5E85C210319BFD2535AFD8F7F79590B2F5CC61AF794218CC50B43FBB8C6 1 0A63F1C0FC2C4540156C7A2E2A2DF1DDF99879C25B4F622933707DD6074A0F1B 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", "h": "1 0A031B1932CDFEE76C448CA0B13A7DDC81615036DA17B81DB2E5DFC7D1F6CD6F 1 06F46C9CC7D32A11C7D2A308D4C71BEE42B3BD9DD54141284D92D64D3AC2CE04 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", @@ -403,14 +403,14 @@ Object { }, "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728266/TAG2222", "id": "1545e17d-fc88-4020-a1f7-e6dbcf1e5266", - "metadata": Object {}, + "metadata": {}, "methodName": "indy", "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "598dbcc3-5272-4503-9c67-b0cb69a9d3d6": Object { + "598dbcc3-5272-4503-9c67-b0cb69a9d3d6": { "id": "598dbcc3-5272-4503-9c67-b0cb69a9d3d6", - "tags": Object { + "tags": { "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", "messageId": "2a6a3dad-8838-489b-aeea-deef649b0dc1", "messageName": "request-credential", @@ -422,42 +422,42 @@ Object { "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", "createdAt": "2023-03-18T18:54:01.126Z", "id": "598dbcc3-5272-4503-9c67-b0cb69a9d3d6", - "message": Object { + "message": { "@id": "2a6a3dad-8838-489b-aeea-deef649b0dc1", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiY3F4ZW8ybzVIWmVjYWc3VHo5aTJXcTRqejQxTml4THJjZW5HV0s2QmJKVyIsImNyZWRfZGVmX2lkIjoiQTRDWVBBU0pZUlpSdDk4WVdyYWMzSDozOkNMOjcyODI2NTpUQUciLCJibGluZGVkX21zIjp7InUiOiI3OTE4NzUwNzgzMzQxMjU3ODU3OTUzMjc2OTU5MjcxOTcyMDQwMjQxMTU1MzcyODEwOTQ2NTEwMjk5MDA1ODEyMTcwNjkwMjIzMTQ2ODU0NDk1MTI1NjQ1MTg3ODQxNzk0NjA3OTUwOTQ1OTM3MDYxMjk1ODgwMjIxMzE2NTg1MTIyNDY1Mzk1MjAwNDQ3MDIwNzAxNDA0NjEwMDc4MzkyMjA4NzkxMjk5NzYwMzM4OTIxNDMzMDc1Njk2ODU0NTY3MTIzNjYxNTYwNDMwNDE3NzQwMzc5MzA4NDQzODcyOTU1NzAwNTk1MTg2NzcxODY3MzM5NzQ5NDgzODYxNTQ2NTE2MTU4NTM5MjkyNDQzNTQ3OTg3MDUwMzE4OTAyOTI5OTgyNzMzMDk1ODk4MDIyMjg2OTk1OTQwMjkzMTg3NTg5NDgwNTgwNTAwNjM0NzAyNjQxNDM0MTgxNjIwMTU4OTU3MzUyNTE1OTA4NDE2MjI4MDQ0NDA2OTU4MTk1MDg4Mjc0ODI4Njc3OTQxMDgxOTczOTg3NjU1MDEzNDUxMzA4ODQyMjYyMzY4MTQzOTExNjIxMTE0NzYyNTk3Nzg1MDczMTM4MDg3NTQ2MDIyMzc1NjQxODQ5ODI2OTg2MjYwMDE5NTAzNzE3OTk0NjM3MzIyNDExNTgzNTY0MTQ1NjcwMTM5OTQ1MjQxOTU2Njk2MDQ3MTQzNjA4NjQ0MDM5OTg2NTYwODUyMzA1MTczMjUxMTUyMDIzODI5NjI3MzQyODM2NDI3MjkwNDQ5NTA3OTY0Nzk4MTQ2NjkzOTUxMDkwNzUwOTAyNiIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6Ijk4MjIzNzMzMDcwMDk1NjM0MzU5MTE2MDEyOTgzMDcyMjc5MjcxMzk1MTg0NjA0NDcxNDQ5NzA1Nzg0MTEyNDAyODMwNzIyMTUxMzIxIiwidl9kYXNoX2NhcCI6IjU5ODA0MTY4ODAxODk1NDAzMjkwMzczMTA3NzA4MzUwODc1NjY4MDcyNDY3ODAyOTcyOTIyNjUzNDE5ODU2MjMyNTIzNDI4OTUxODQ4NjEyNDE1MjM5Nzk3Mjk5ODY2MzIxNjU5NDQ1MTM1NzQ4NzU2MDY1NjgyOTY5MjY4ODI5MTYyMDA0NjQ4NzYwMzg4NTg4NDkyNjg1NDI1MTg1MDU2OTAxOTkxMjcwMzYwMDk3MDc5NjEyNTYxMzY4NzU1OTcwMjY5MjI4MDYzMjMyODU0NzI0NDkyOTA5Njg5MDMyOTg4NjYyMjk5Mzg3MDU2NDEwNjU5MDU3MDUwNjE0MDQ2NzE1NjA0NTgyMzM2NTg4MjMxMjI3MTEzMDEzMDQxMTA0NTU2NzM1MDE3ODUwNzUzODcwNjc2ODYxNDA4MjA0NzkzMDIzNTYwMDEwNTEzODAwNzA4MjAyNjAyNjQ0Mjg2NzI4NjAyOTk5MzU5MDcwODQxMTQ5MTAzMjA5MzY0ODkyMzMzMDYwMDgzMTA5NDIzOTQ5NzE4NTk5NjEzMzk2NjIyMjc4MTExMzk5ODU0MTcyMjMyNTQzOTk1Njc5NDk3Mjk1Nzc1NzA0MjA0MTQxOTU2NDI1MDc4NjYzMzgwMDA1Nzc2ODY2MTcxNTY4OTU1NjE4NjAwMTA2NzkxMjIyNDkyODA2NzI1ODU1NDY2Nzk4OTEzMTc2NDcxMDY3MTk5ODQ2ODEwNDI5MDIzMDc3ODI3OTc1OTIzMDIzNjU3MTg0NjkwNzE0MjkxNDk0MDc5MTM1NzYyOTUxNTc0MjMzNjMwMjExNDQ1Njc3NzE1Mzg3Mzc1NjkyMjAzODE3NDczNDk5NDExNzE5MzIwMjczNzExOTIzMzM3MzYzMTAyOTkwMDcyMjE2MjYzMzUxMzMxNTk4ODk1OTU3MzU1MDc1NTEzODE0NTUwNzkyMCIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiNTY0MTY1NjA2NjM0MTUwODgzMjg2MjM1NDM1MjUyODQ3MjAxMTk4NTQyNDAxMjYzNTY2MDQ2MTA3OTU3NzI4NTQ0MTMwMjgzNjUyNjQzOTI0NDc2NTU2NTIzNzg0ODI1OTgyMzMwMzc4NDI4OTM0MDkxNDcwNDM0OTAwMTM3NzkwNDkxMjM4NTA4ODk2ODQxMzkwNjQ4MTA1NzY5ODYzMzI1OTAzODI1Mjk1MjU2OTQ0NSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIxMzE1MDIwOTY0MDY4NjgyMDQ0Mzc4MjEifQ==", }, "mime-type": "application/json", }, ], - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "cqxeo2o5HZecag7Tz9i2Wq4jz41NixLrcenGWK6BbJW", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3001", }, - "~thread": Object { + "~thread": { "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2023-03-18T18:54:01.126Z", }, }, - "6ef35f59-a732-42f0-9c5e-4540cd3a672f": Object { + "6ef35f59-a732-42f0-9c5e-4540cd3a672f": { "id": "6ef35f59-a732-42f0-9c5e-4540cd3a672f", - "tags": Object { + "tags": { "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG", "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", "methodName": "indy", @@ -465,16 +465,16 @@ Object { "tag": "TAG", }, "type": "AnonCredsCredentialDefinitionRecord", - "value": Object { - "credentialDefinition": Object { + "value": { + "credentialDefinition": { "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", "tag": "TAG", "type": "CL", - "value": Object { - "primary": Object { + "value": { + "primary": { "n": "92212366077388130017820454980772482128748816766820141476572599854614095851660955000471493059368591899172871902601780138917819366396362308478329294184309858890996528496805316851980442998603067852135492500241351106196875782591605768921500179261268030423733287913264566336690041275292095018304899931956463465418485815424864260174164039300668997079647515281912887296402163314193409758676035183692610399804909476026418386307889108672419432084350222061008099663029495600327790438170442656903258282723208685959709427842790363181237326817713760262728130215152068903053780106153722598661062532884431955981726066921637468626277", - "r": Object { + "r": { "age": "12830581846716232289919923091802380953776468678758115385731032778424701987000173171859986490394782070339145726689704906636521504338663443469452098276346339448054923530423862972901740020260863939784049655599141309168321131841197392728580317478651190091260391159517458959241170623799027865010022955890184958710784660242539198197998462816406524943537217991903198815091955260278449922637325465043293444707204707128649276474679898162587929569212222042385297095967670138838722149998051089657830225229881876437390119475653879155105350339634203813849831587911926503279160004910687478611349149984784835918594248713746244647783", "master_secret": "61760181601132349837705650289020474131050187135887129471275844481815813236212130783118399756778708344638568886652376797607377320325668612002653752234977886335615451602379984880071434500085608574636210148262041392898193694256008614118948399335181637372037261847305940365423773073896368876304671332779131812342778821167205383614143093932646167069176375555949468490333033638790088487176980785886865670928635382374747549737473235069853277820515331625504955674335885563904945632728269515723913822149934246500994026445014344664596837782532383727917670585587931554459150014400148586199456993200824425072825041491149065115358", "name": "26931653629593338073547610164492146524581067674323312766422801723649824593245481234130445257275008372300577748467390938672361842062002005882497002927312107798057743381013725196864084323240188855871993429346248168719358184490582297236588103100736704037766893167139178159330117766371806271005063205199099350905918805615139883380562348264630567225617537443345104841331985857206740142310735949731954114795552226430346325242557801443933408634628778255674180716568613268278944764455783252702248656985033565125477742417595184280107251126994232013125430027211388949790163391384834400043466265407965987657397646084753620067162", @@ -487,55 +487,55 @@ Object { }, "credentialDefinitionId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/CLAIM_DEF/728265/TAG", "id": "6ef35f59-a732-42f0-9c5e-4540cd3a672f", - "metadata": Object {}, + "metadata": {}, "methodName": "indy", "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:53:43.140Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.4", "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "a56d83c5-2427-4f06-9a90-585623cf854a": Object { + "a56d83c5-2427-4f06-9a90-585623cf854a": { "id": "a56d83c5-2427-4f06-9a90-585623cf854a", - "tags": Object { + "tags": { "connectionId": undefined, - "credentialIds": Array [], + "credentialIds": [], "state": "offer-sent", "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, "type": "CredentialRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:53:59.859Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "John", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "99", }, - Object { + { "mime-type": "text/plain", "name": "height", "value": "180", }, ], - "credentials": Array [], + "credentials": [], "id": "a56d83c5-2427-4f06-9a90-585623cf854a", - "metadata": Object { - "_anoncreds/credential": Object { + "metadata": { + "_anoncreds/credential": { "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728266:TAG2222", "schemaId": "A4CYPASJYRZRt98YWrac3H:2:AnotherSchema:5.12", }, @@ -546,9 +546,9 @@ Object { "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf": Object { + "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf": { "id": "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf", - "tags": Object { + "tags": { "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", "messageId": "578fc144-1e01-418c-b564-1523eb1e95b8", "messageName": "issue-credential", @@ -560,70 +560,70 @@ Object { "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "DidCommMessageRecord", - "value": Object { + "value": { "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", "createdAt": "2023-03-18T18:54:01.192Z", "id": "b88030f7-dc33-4e7e-bf4d-cdfaa6e51ebf", - "message": Object { + "message": { "@id": "578fc144-1e01-418c-b564-1523eb1e95b8", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJhZ2UiOnsicmF3IjoiOTkiLCJlbmNvZGVkIjoiOTkifSwibmFtZSI6eyJyYXciOiJKb2huIiwiZW5jb2RlZCI6Ijc2MzU1NzEzOTAzNTYxODY1ODY2NzQxMjkyOTg4NzQ2MTkxOTcyNTIzMDE1MDk4Nzg5NDU4MjQwMDc3NDc4ODI2NTEzMTE0NzQzMjU4In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjMyMTIwMDQ1ODc4MzIxMjcyMzA1ODI5MTc3NzMzMTIwNzE1OTY5NDEyNjkwNjUyNDQ4OTc0MTA4NzEzNjU0ODc3NTg2MzIzMTI3ODk2IiwiYSI6IjIyMjY0MTYwNjIwODcwNDUyNTExMjcyMzE1MzMzMDA0MjQzMzY3NTM2NzM3NDMwNjM1NjExMjcwMDkwOTE4NDMwNzc0ODEzMjAzNjQwNjMxMjIyNDczMzk0MjQ4MTgzMDIzMjIyNzExNDUwMzQxMDcxOTQyNDQwMDgwMjY2Nzk1Mzg5Mzg5Njc1NjYwOTUzNTQyMDE4OTA3NjQ3NzI4OTQ4NjY1MzA2Njg0NjExNDU1NTI5NzM5OTY1NDcyMjQ2NDQxMzE1NzAxMzM1ODc1MDY3MjExMDk3NzcyOTgwMjU1NDIxMDMzMTI1MjAyMTQzNDk3NjMyOTAyMjM1NDAyMzU5OTA1MzY5MzE4MjI1NTc4MjUxNjY4NTYzNzc1NTY0MDM2MjUxNzE0Mzk3MTEzNjQ3OTg0MjcxMTE5MTU2NDQ3NjI1OTk1NjE5MjAwMDk4MTgzNzY1NjkzMTg1ODEzNjA1NDU3OTQwMzE0MDU2MDkzMTI2MzQ3OTU5MzYwODIyMzg0OTEzODg3Mjg3ODI2NjkyNDIyNDMyNDUwMDA5OTYxNjQ2MjMzNTE3MjY3NDU1OTkyMjA3MTE3Mzk5NzU1NjY3MTA3MzM1NTQ0MzEwNDQwNDE1NDE5NTk5NTA1OTgxMzkwMjk5NDUxNzQyODg4NDg0MTc0NTU5MDA5NDgwNjU5MDk2Nzg2ODI2MDgxNzc3MzcwNTk1MTU3OTg5NjQ1MDYxNDI2OTA2ODM2MDk5NTU5MDQ0MDI4ODM2MzYwOTM2MDkwOTkxNjA1OTU0NTM2OTQxMjgxODQwNzk2MjkxODc0ODk2NDEzNTM5IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDExMzE1MTE0OTUxODg0MDQ5ODkyNjAwNTY3NTgzMTc4ODkwMyIsInYiOiI2NTE5ODc4MzYyODQ5NzExNDY4NDkyNDM1OTM3MDU4NzMzOTYxMTkxNjA3NjI4MzUzNjkxMDg1MzM5MDMwNDU0OTkyODc0ODYyODkyNDg4ODYzNTA3MDQ1MTM1MzA4ODI1NDA2NzYwMTQwNDQzNzM0NDYzODE5NTM2MzE0NzcxMTQ3MDk4MjU2ODMzNTc2MjIwNDI5ODQyNjc3NzMwMzQwODYwNjE2NTcxNzc5NjU4OTIxNDY4Mjc0NTUwOTc5NjYyMDkxNzEwNDU5MDk2MDgzMzYzNTc1Mjc0MjQzNzIyMzIzOTIxMjY5MDYyMjE0NjQyNzQyMTI0MzQ4MTY0MDUxNzE3MTk5MTkzODY3NTM3NTEzNjYzOTY1ODQzMDI5MjAxODA0OTE2MTEzNzMxODYzOTUzNjQ5MDkwNDgzNzMyMTkxNTQ2MTEwMjAxNTg0NzMxODg4NTE5NjA2MjE1OTkyNTgxNzk2MDg2NzUzOTE5NzUxMjkwMDI3MDI4NzczMTAwOTc5ODI5MzQ5NzA0MTUyMDEzNjg2MzU1MzM1MjIyNjU5MDY2NzE0NDQ2NDc4NzY3MTE5NDE4MjY3OTg5NTAyNzc4MjMzNzM3MjM4MjU1MTQxNzQyMjk4NTU3MDY2NzA2MTM0NzYwMjQwMzY3OTMzMzc5NzYzMTc5MTI1NTI4MDQwMzkxNjQwNTIyNTM5NjE5NTU0NTE0NTk4OTUxNTg0NjA3MjYwNzk1NzE1MDMyMjM4NTQ3ODMyMzA0NTY2MzQ4NjYzMTc0NzQwMDE2MzQ2NTU2MTM1ODc4MzgxNTYzODQ2NzU0MzQzMjk0NTIzNjc0NDI3NjQxNjAxNjAwNjE2NzI3NjEyMzc0MzI2NzY4ODA5NjAyNTE5MTAzOTk3NDY4OTg1NTg3Nzg4MjI3Njc5MzQ4NTgwNzk1OTkyOTkxNzMzMDg5MTUyMTg2MDg4OTU2MTg2MTQ0OTkyMDI5OTI2OTUxOTU0OTQyNjYwMDUxOTM0MDc5NzkxODI1NzA2MTExNzg0MDU2NDM2OTA2MDgxMDQ2MDQ5ODI0ODE1NDE0MTc5NTMzMDA2ODE4NzQ3NzgwNTQ5In0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjEwMTUyMDI0OTk1MTUxMzcyOTgwNzI5NDM1MTQ5MzgyMzQxMDQ3MDIzNjI2NjA4MDc3ODg1NzQ2Mjg4MTE3MjI2OTAyNzM4OTY5OTEwMTU0NjgzNzM2MzI4MzQ2MDg0MjcwMDA3MTY2NTg3ODI5MjE2MzEzNDc4MDk3Njc0MTI0ODU2ODIyMjA4NzI0Nzk1NTE0ODgzOTYwMzE5NDc5OTg4NzAzNDUyNjI4NjYxMDc3MTg3OTIyMzA1NDc5MDE2NzQzOTk0NzYwMzE5NzI1OTExODk0MjM2NDMxMDkxMTIyNTUxNTU0NzgwODg0NjQ2MjE0MTUzMDUzMTM2NDMwMTk4OTA5MTM0OTk4OTM2NjY3MzI4ODI2MDcwNzEzMzk0NDg0NDI0ODUxNjkxMzUxNDc0NjAxMjIwODk2NTIyMDYzNDA5NzA4NDA1Njk2MzY5MjA0MzU0NzE1MDkxMzk2Mzc4Mzc3MzA0ODk3MzMwOTM0Mjc2NTQyNjE2NjAxNTk1ODI5NzgxOTg3NTMyNzkxMzIyNTgzOTE1Njk1OTY2MjM3MTc4Njg1NTMzNTE3MTQxNTAyNDE3MzQxMDIzMTA1MTczMjMwMTcwNzUzODYwMjgxNDAxODk4MDE5OTQwNjA2MzczOTYwMzYxNjA3NTE2NjgyMDg4MTc1NzU4ODA0Mzg4MTM5MTQ0MDkwMjg5MzI5NzMzNTQ1NDg4MjUyNjczNDIyODkzMzc1MzE5ODQ2OTMwOTIyNjIwNzAzMTEwMDgwODU5OTE4ODQ0MzgyOTQ3ODczMjAwNzA4MTY2MzA0NDk4ODk0MDA4NTMyIiwiYyI6IjIyNDQyNTM5MzYwMzYzNjQyODI1ODkxNTc5ODgzMDE5Mjc3Mjk0NTQ2MjUwMDEzNTM3MzI2OTY2NzM3MzE0NTUxMjEwMjU3MjU2NDU5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, "mime-type": "application/json", }, ], - "~please_ack": Object { - "on": Array [ + "~please_ack": { + "on": [ "RECEIPT", ], }, - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3000", }, - "~thread": Object { + "~thread": { "thid": "c5fc78be-b355-4411-86f3-3d97482b9841", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2023-03-18T18:54:01.192Z", }, }, - "be76cfbf-111b-4332-b1fe-7a1fea272188": Object { + "be76cfbf-111b-4332-b1fe-7a1fea272188": { "id": "be76cfbf-111b-4332-b1fe-7a1fea272188", - "tags": Object { + "tags": { "connectionId": undefined, - "credentialIds": Array [], + "credentialIds": [], "state": "done", "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "CredentialRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:53:59.068Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "John", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "99", }, ], - "credentials": Array [], + "credentials": [], "id": "be76cfbf-111b-4332-b1fe-7a1fea272188", - "metadata": Object { - "_anoncreds/credential": Object { + "metadata": { + "_anoncreds/credential": { "credentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG", "schemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0", }, @@ -634,9 +634,9 @@ Object { "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "d7353d4a-24fc-405f-9bf5-f99fae726349": Object { + "d7353d4a-24fc-405f-9bf5-f99fae726349": { "id": "d7353d4a-24fc-405f-9bf5-f99fae726349", - "tags": Object { + "tags": { "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", "messageId": "c5fc78be-b355-4411-86f3-3d97482b9841", "messageName": "offer-credential", @@ -648,51 +648,51 @@ Object { "threadId": "c5fc78be-b355-4411-86f3-3d97482b9841", }, "type": "DidCommMessageRecord", - "value": Object { + "value": { "associatedRecordId": "be76cfbf-111b-4332-b1fe-7a1fea272188", "createdAt": "2023-03-18T18:53:59.857Z", "id": "d7353d4a-24fc-405f-9bf5-f99fae726349", - "message": Object { + "message": { "@id": "c5fc78be-b355-4411-86f3-3d97482b9841", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "name": "name", "value": "John", }, - Object { + { "name": "age", "value": "99", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6VGVzdCBTY2hlbWE6NS4wIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY1OlRBRyIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiODUxODAxMDMyNzEzNDg5NzYxOTg5MzAzNjMzMDkzOTEyOTExMDUxNjI0OTQ0OTYzMTgzNzM2MDY3NDkwOTc2MDYxODEwMDgxODkxMzQiLCJ4el9jYXAiOiI4NDk0NDg4MjQzNTk2NTkwOTc2MjQzMjc0NDg4ODk2Mjc1NTcyODAyMTQ1ODE5NDQzNTE0NzQxMzk1NDI1NjM5MzQwMTczMDIzMTQ5NzI3MDY5NzMzMzQwODgzMTU4MzQ1NTYzOTA5OTcxNDMzNTg1MjMwODAxNTYyMTM0NjczNjM1ODg5NTA3Njg5ODQwOTgyODU5Mzg1NjA1MTc1NTkxNDYxOTkyMDExNzU2Mzg1MTI3MTQ3ODgxNDMwODEzNjYxNzY0MDU5MzE0ODk4MTc2NzQzMTQ5MjYzMDMwMDQ1NzMwMDMzMzI2NzgyMzg1OTY0NjcxMzg1ODQ2MzcxNjQ5MzQxMTg2MDM5NjE4MTQwOTIwMDUxMzg1MDAwNTYxMDcyMTc5NTEyMzc5Nzk0OTU4NjE1ODIyODI2OTExNzIwNTQyNTE0MTQ1NDc5MTAxOTUyMzM4MDMwMDY1MDk5NjcxOTU2OTMxMzE2NjE5MjM0NTQ0NTE5NTQ1ODQ1MzA4MzgxMjQyNTM0NDcyOTc3NjY0MjAwMjc2MTMyOTgxODE1ODAzNTIxOTExMzk4ODkxMjE0NjE1NzA1MDM2ODM2ODU1NDU1NzY4ODg4MTUxNDgzODAyNDcyODQyMzczNzE0MTI0NTYwMzIyNTI3NDE4MTEwNzYyMjgyNzY4NzMyNTIzMDQyMDA3MDY2OTk2ODIxMTQwMzE1NDg0NzI4NTM2NzIwNDI3MDg5MTI2NDk1NTAzMjc0ODQ4MDM3MjUzOTM3NjI3MDU2ODUzMTQ4NjE5NDA4NDYxOTI5NzEzMjM4MjEwNDc4MjcyMTIxNTUwNjQzODc4ODM1NDYwMzY1OTIwMjE3NTk5NDYyNDUzMDMyNDQ4MjYyMTM3NjE5ODY0OTU4MzA1MDE3MjA4OTYwNDc1MTQxODgwMTMiLCJ4cl9jYXAiOltbIm5hbWUiLCI1MDcyNzU2NDE2NDA2ODIxNzU1OTc0MzUxMTg0NjE1NjA4NDY2NTk3Mzk0NzA2MTY1NDg2ODAzMjc3MjMyMzQyOTk4MDA0MzY0OTU0MTczMzc0NDIwOTc5NTkwMDcyODgxNDgxNDA0MTg2OTExODg5NzQ4MTgzMzQ1OTk5NzQ0NzgxMTQ1MTMwNzEyNDIzODY0Nzc1MzQzNjAzNTk2NDM3Mzg4OTgzNTExNDAzODA0NjEyNjU1MDE5NzQ4MTI5NDk3ODY2NTcwMDQyMjcwNDQxNDQ5MjYwODY0NzgyMzI5MjAxNDEzMTc5ODU3NzA0MjM5OTMyMTg4NTc4NzE3MDczNzM3NjUyNzY5MzY5NDg4OTgxNzg2NDQwNTExODAzMjMzNDMxNzA4NDk4MTU2NTA0OTUzNzkzNjU2NjQ2NzMyNTU4MzQwNDI2MDI1MjA3NTk0OTIwMDY4OTc2OTQ4Nzg2OTUxNzM3MDIwNDQ0NTA5NzYyMDQ2MzIzNzA0MDQ3MjU1ODU3NDE5ODE3MDc5NTI3NDgzNTE1NDY2NTAyMDkzOTY1NDMzMzk3MjQ1MzA4MjQ5MDgyMTQ4Mjc4NDA1MzI5Njg1Mjc0MDYwNjk0MzI0MTI2ODgxMjkyMDIyMjY1ODczMjk5MDU0NDU1OTA5NzkyNjUwNjAyMTk0NjUzMjYxMDk0ODYwOTc2NzA4ODE1ODgwMjExMTY0MTkwMDM0NjY0MzI2MDc3NjcwNzkyMDE4NTE2MzMzNDI3NjkwODYwMjIxODEwMzk5MDgxMjc5NjAwNTYzMjk3MjI0NjM0MDM0NjcxNTIwODE5MzU3NzQ0Njk2NzU1Njg1NDI2NjIzMzAwMjQ3MDUwODE4NTQ2MDM2NjA0NjMxNjcyNzE5MjI0NDA4NTE2NDM4NTgxMDM5Njk4NzI0MSJdLFsibWFzdGVyX3NlY3JldCIsIjU2MzYzNTgyMDQ5Mjg4OTY1OTg1MDA4NzgyMzU0NjgyNjMwNDkxMzQ3MTM1NDIxNTAyMDEyMTIwMzI4MDI4ODIyMjUyMzg4NjgwNTMwNTgxMTcwMTgwNDU1MTcyNTc3ODkyMTEyMTY1OTM0Mjk5NjUyNzAxNDExMzUyNDkzMzkyODU0ODI4NzMyMDQzMDI0MDI0MzM0MzMzNzc0NjEyOTEzOTUyMjAzNjM1NDk2MDQ0ODMzMDI5NDE2NjUwOTU5NjE0ODgzNTUwOTMxNzgzNTA5MzE1Nzg4MDEyODQ0MzAwMDQwMDE5MTY5MTc3NTI1OTgxMTU3OTkwNjQzMDcyMjQyNzcxMjU0MTYyNzMxOTU4NzI2Nzc1NjYwMjkxODIzMDcyNDk1Mzg0NzM5MTcwODc4ODMxNzkxMjQzMjEzMjU5MzA5ODQxNjU3MjUwOTg1NzMxMjEyNzE2MDM2MDY3MDUxNjM2NzA0MjA1NDEzMDk2MDU3MTA2NTM2MTI2ODUyNDU0NzcwMzQzMTMwMTczMjAwNjEzMDIxOTE4MzgzMDQxOTU4MTkwOTE2NzQ0NjU4NTI0ODA1NjM4Mzk2OTY3OTA3MzIwNjY1MDU1MzcwMjY0NjAxMDczMjc5NDMyNjM5MjM3Njc1NTA0OTg1NzQyNTI4NjYwMTAyMDEzNzIxMzA2MTE4MTg0NDk1MTEyNDQ2NDYyNDc2NTkwMjYxODkxMjA0OTQxOTA4MjMyNzMzNDA3MTg4MDA3NzE2NTA2OTUzMDY0Nzc5NDk5ODExNzI0ODI5NjcyNjY2NzIyNjIzOTAxMTc1OTk0NTIyNjkwMjk1ODI0MDgyNzY5NjQ0NDYxOTAxMDk2NzI3MTE5NzAzMjUzNzI4NjY3MTU1MzA5MDYzNDUyNDY2MDY3NzU5NzIwOTgyNDA3MiJdLFsiYWdlIiwiMTM2NTQxMjE0MjM5MTcyNDQxNzQ1MjU3MjcyMDI3MTA4NDYwMzU0MjgxMTA2OTA2MzYwNDIwMDE0NjUyMDIxMDgyNDEzODM2ODEyMjk3NjY3ODk2MTYzNDkzMjM4NDIxNDI4NjMyNTMxODE0ODk4NzkwMDg4OTg2NjgyMTE2OTAyMzc4NDgwNTE4OTUxNDExNzg1OTk3NTk5MDMyNDYxNjExNjIyMDUyNjMzMDQ5ODYxMzc5MTQzNzI4MTM5MTUyMDkyMzI0ODc3MjMxMTYwNTgzNzA5NjE0MzA1NzQ1MjA5MjQwNjU2MDU4NjY3OTMwODEzNzYyNDY5MDc2ODc5MTk1Nzg0Nzg4NTE2NjI3MjgxMDY0NjE3MzgzMDc4Njc5MTkwODIwMzQwNTgwNDY2MjU3ODU3NjA1MTc2MTg4NTI3OTMxMDI4MTMzNTY5Njc0Mzg2ODAwMTA2MDE2MDg1Nzc0OTcyMzI1NTAyNDA2MTY0OTY0MjU2OTUxNDI3ODAxMTQzNTQxMzUzMzI0Nzg0MzA5OTY4MjIyOTU1NDk4Njk3NTAwMDUwMzc0MDg0NjIwNzQ4MTk0NzIyMTI2NjE2OTY3OTY3Mzc1NTM3Nzc5NTc4NTMwMDIxODExNTA2MTIxNjcxMDUwNDgzNTM2MjA3Njc3MTg5NDQwNjEwNzk0NTcyNzI5MTgzMzAyMjM1MDkxMDg4NTU2ODc5NTg3OTE3MDMzMzQyODcyMzg2NDQ5MTQ0NzgwMDYyNjc4MzA3NzE4MzU1MjQ5MTUxNjc5MDA1MzkxNDA5NDE4OTQxMjEzNDkxMjQyMjg2NTAwODcyMzQxNDI3Nzk1MjQ1ODYzODE2MDY2NDY3NDkxOTg4OTU3MDEwNDIxNDA3NDkyMDUxOTc0NTMwNjIxOTk1ODU0ODczNTM5Mjk3MyJdXX0sIm5vbmNlIjoiNzk3NjAzMjE3NzA5MzM1MzAwMTcwODI4In0=", }, "mime-type": "application/json", }, ], - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "GVbwqUMqzxKaEVWjn1aPBfjJpYQHVejinpx8GCeEuQjW", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3000", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2023-03-18T18:54:00.011Z", }, }, - "de4c170b-b277-4220-b9dc-7e645ff4f041": Object { + "de4c170b-b277-4220-b9dc-7e645ff4f041": { "id": "de4c170b-b277-4220-b9dc-7e645ff4f041", - "tags": Object { + "tags": { "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", "methodName": "indy", "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", @@ -701,12 +701,12 @@ Object { "schemaVersion": "5.12", }, "type": "AnonCredsSchemaRecord", - "value": Object { + "value": { "id": "de4c170b-b277-4220-b9dc-7e645ff4f041", - "metadata": Object {}, + "metadata": {}, "methodName": "indy", - "schema": Object { - "attrNames": Array [ + "schema": { + "attrNames": [ "name", "height", "age", @@ -719,9 +719,9 @@ Object { "updatedAt": "2023-03-19T22:50:20.522Z", }, }, - "e531476a-8147-44db-9e3f-2c8f97fa8f94": Object { + "e531476a-8147-44db-9e3f-2c8f97fa8f94": { "id": "e531476a-8147-44db-9e3f-2c8f97fa8f94", - "tags": Object { + "tags": { "associatedRecordId": "a56d83c5-2427-4f06-9a90-585623cf854a", "messageId": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", "messageName": "offer-credential", @@ -733,64 +733,64 @@ Object { "threadId": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, "type": "DidCommMessageRecord", - "value": Object { + "value": { "associatedRecordId": "a56d83c5-2427-4f06-9a90-585623cf854a", "createdAt": "2023-03-18T18:54:00.005Z", "id": "e531476a-8147-44db-9e3f-2c8f97fa8f94", - "message": Object { + "message": { "@id": "4d2c80b7-4a25-42ac-b8cf-a68b1374b9b7", "@type": "https://didcomm.org/issue-credential/2.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/2.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "name": "name", "value": "John", }, - Object { + { "name": "age", "value": "99", }, - Object { + { "name": "height", "value": "180", }, ], }, - "formats": Array [ - Object { + "formats": [ + { "attach_id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", "format": "hlindy/cred-abstract@v2.0", }, ], - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "8430ddb8-b0c3-4074-8ded-f4dcfe80303d", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjI6QW5vdGhlclNjaGVtYTo1LjEyIiwiY3JlZF9kZWZfaWQiOiJBNENZUEFTSllSWlJ0OThZV3JhYzNIOjM6Q0w6NzI4MjY2OlRBRzIyMjIiLCJrZXlfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjQwMTQ0MTA0NDg3MjM0NDU2MTc1NzYwMDc1NzMxNjUyNjg1MTk0MjE5MTk5NDk3NDczNTM4NjU4ODM3OTIyODMzNTEzMDg0Nzk2MDQ5IiwieHpfY2FwIjoiMzgxOTQyMjM1Mzc3MzYwODEyNjY0MDA4MjYzNDQxMDg2MDMwOTMyMjAxNzgzMjM3ODQxODQ5NDg3ODk2ODg1MTYwODY2MTY1MDM3NzI2MTIxNjU0MjcwOTg5NDY3NjAzNDExOTAzODk4MzUwMDAzNDIwODg3MzI4NTUwMTY2MTI1ODMyMjAxOTQzMTkwNzAxMDU4NTAwMDE5ODM1NjA1ODczNDYzOTkwODg3NzQ0NjY3MzU0MjM2Njc3MzcyODg0ODQyNjE5NTEwMTUwOTA2MjI1OTMzMjc1ODEyNjg2NDg3NTg5NjY3ODI3MjAwODcwOTQ0OTIyMjk5MzI3OTI4MDQ1MTk1OTIwMDI3NTc0MDQwNDA4ODU5MzAwMzY1MDYwODc3Nzg2ODkwOTE1MDU5NTA2ODc1OTI0NzE2OTI1MDM2MTc4Njg2NDE5NTYyMzcwODI4MTMzODY2Nzg3NzkyMDcwNjAyNDQzNTkzMTk2NzEzNzcyNDM2NTYzODI0MzkwMDIyNzg4MjU2MzA4NjU4OTc0OTEzMTk1ODYxODUwMTQ3ODE1Mjg5NzQwOTA4NDk1MjQ3NTAyNjYyNDc3NzQ2NTI5ODA3Mzg0OTgxODI5MDc3NTQ4OTI2NzExMDkzNzQ5MjM1ODU4NjUwNDc5NzE5NDI4MzUwMzAwNzUyNjQ0OTg1MTQ5MTMxNjA1NjUzMDIxMDYxNzkwMjY3MzQyNTY4NTkyNTY2MTQ0MDM5NzY4OTg0NTMyNDMzNzk0MzUzNjQ2Nzg1MjA3NDgzOTk2ODQ0OTcxNTgzNzY3NDQ5ODYyODgxMjMxMjI1MzM4MzAzMTQ4NzA0ODczMDEzNDM3MDgyNzY1MTk4OTY2MzE5NTM0OTkyNjk4MzMzMDQ0MDI3MjIyNTYyNTIzNzk3ODk5Mjk2MTQ1NDU5IiwieHJfY2FwIjpbWyJtYXN0ZXJfc2VjcmV0IiwiOTE5MTc5NzQ4MTE5NTg5MTY3Njc5MjQxODk5NzY0ODIwNTk0MDc5OTQxNzIzOTgzOTYyNzQ1MTczODM0NDQxMDQ3MjU4MDcyOTE4OTUzNjIzOTQ4MDMyNzI2NDgyNzI2MTEwOTk2Mjk3MDU3NTYwNjcwNzAxOTU1MTkxNDc0NjM0MzQ0ODMxMzg3NTk4NzI2MzMxMjc0NjI4NDU3Njk5NzczMDA1NDMwMDIxNzMwMzg4MzcwMTEyMjc3MzI2MzU4OTgwMTA3ODIzNzUzODc3MTU0NjIwMDkzMjE5MjYyNjAxNDM2NzMyNTgzNDI4Nzc4NDA4OTc0NTQyNzkzMDk0NTQ5MTczOTA3MzQ3OTUxNTc1NjM5NzU2NDg5MTA0Mzk0MTY3NzExMzY1MjM3OTI1MjAwNjk4OTg5NTI5MTQ3OTIzNTYzNDMyODgyMzgwMTg0NzU0NzkzODMwMTE3MTQ1MDAwMTI0NDYxNjkzOTcxMDQ5MjgzNDk1NTE4MDQxMDc5ODUyMzAwMjk0NDM1MjYzOTIwNDU0NTU3MzUxNDQ1MDM3NDI4MDg3OTk2Mzg2NjY3NjU3Nzk5OTYyNzQzNzIyNzA3NzczOTEzMzc0NzIxODUyNTQ3MjkwMTY5MjI5NTAzMTQxOTMwODYzNTk4NTExNjc4NDEyMDE0MzE2MDM2MzYxMzczNDcwOTQwMDEyODcwMDgwMDA2MzE0NzYxNzYzNzUyNzYwODk5MTQ3NzA1MTA0NzQyNjAxNjkxMzMxNjkzMDIwMjg2MjA2NzQ2NzE0MzI3NjU2MjA2NTMzMjk3NDg4MjU2NTM2NTQ3MzY4MjM2OTQ2MDM5NzAzMzc0OTMzNTE0NTc2NDg2NjQyNTY4MjgyNTY2MjMyNDU1NTU5MDY4MzE3NzU5NDM0ODU4NTI3MDg2NjQ0Il0sWyJoZWlnaHQiLCI5MjMwMzkyNDc1NjI4ODc1MjA4OTM0NjM0NzE4MjYzNzA4MDIzOTI1MDU0NjY2NDgzMzgxMzIyMzc3MDg1MjMxMjU4MTM4MzgwOTU1NTk3NDQxNTEyOTYwNDA2MjI3MjUwODgyNjA3NjExMDkwODk3MTM1NDcxNzAwMDIzNDcwOTM2ODg4MDE3NDY5Nzk0ODYzNDk4NzUyNTI3Njc3MjMwMTEwNzg0ODQzNzI0NDUyNTUzODYyOTA2MzM5MDc0OTIzNDU4NTQ3NDYzODcwNzU3OTg5MzMxNzk4OTI2MjM4MjUxMTM2NTYzNjM2MjIyOTQwNDkwMzY3MjQ2OTg0OTU2NTE5MTAzODcwNDE0MDM5NzM2MDE2MDY5MzA2NjQ0NjQzODI4OTgxMTE3OTM3NzYyNDAzODY1Mjc1MDU5MjEyOTY2NzIxOTU3MzM0MTM2ODEyMDI0OTE0MzA4MzAxMzk5MzM4NzMyOTIzNTA0MjA5MDM5ODMxMTc5NjU1NTkyNjg0MjMyMTIzMTI2Mjc4ODQzNDMyOTUwMTk1Mjg3MzE4ODI3NTM2MTMwNDQ3NzM3MTgwMjk3MDE0ODEzNDg3NDQyOTg2NjQ1NzQyNjEyMzE5NzQxNDY2MDMyNTg5OTU0NzYwNjE4MDU0MDUxMjAzMTE1NTAxNDcxNDExMzg3NzU0NDk5MzAwNTU4MTc5NjM5NDAxOTM0NTAzMTMyMDEzMjAzOTg2NzkyMTEzMDAzNTkwODg1NTc3NjgyMzU2NDY3MjA5NTUwNjQxODQxMDYyNTkzNDYyODIwODg3NzgxNDYyODM3ODkzODcxNDM4MzM3Mjc5MTcwMTExMTQ5MTU4NDMzNDE0ODI1NTkyNjcyODU2MzM5OTM4NTgyODg2NzM3OTIwMjc1MzI0MjEwMTUzMjE5MjI2OTYiXSxbImFnZSIsIjkxNTg1ODk3NDkwNzE0ODA3OTY2MDYzOTg5MjE1NTMxNDkyOTQwMDI5NDcyMTM4MjgwNjcxNjcyMjQ0NjY5MDc5NzIyNTQyMDU0NTU3NjY0MTcxMDI1NzM1NjQ4NTIwMTM4ODQ4ODAxNzIyMTc4MTcxMTA5NTc0MTMyNTExMzM1MDEwNTc5NzExMzcyODM5MjI3MDExOTg4MTUyMTEwMzI4MTE5MjkyMjI4NjM3MDU4MDQ3NzYwODYwOTQ0NTY3MzQxMjY4MTY4Mjk3NjE5MDM2ODEwMjYwODM2NDI1NDkwMzU3NjE4NzM4NTYxNTY2MTUxODQ3MzIxNzM1MjQ5ODk1MDU5NTY2OTQxODI5MjE0Nzc0MTA0NzYyNTQwMjcyMjk2NjE1NTE3NjUwMDcyNDQyMTI0NjY5MDEzMTc1ODAyMDk5MDQxMzk3MzE5ODQ0OTA2MDgwOTYxNTcyMTcwNjg2NzgzNDM1Mjg2MDUyMzE5ODY3ODExMDE5MjAxMDYwODM2OTM3Mzc0MzM0NDM5MTQxMDAzMTI3NTcyNjgzNTgwODI0OTkwOTg3MjE5MzU4NzkzOTM2NTU4Nzk3MjI0MDQzNTM1ODA5NzMyNzgxMjE1NzEwNjI1MjQzODYwNTk4MTk0MjU2MjAwODkwOTA3ODAzMDcyMTAzNzc3MzkwODk4MDczOTgyNjY3Njc1ODg0MjI3MjU0Mzc2OTI5Mjg3ODQyNDE0MTE0MjcwNDQwMTEzNDUxNjk4NzE5Nzc5NjQyNTI4MDA4NDM3Mzk5NjI0NTE3OTM4Nzg5MDc3ODE5ODA0MDY5MzcxOTM0NzExMTIyNTQyODU0OTg4MDA0Mjc4NDkwMjAxNTk2NjE0MjUwODc3NDYxMDczNjc3NTUzNzYxMTMyMTA5Nzg3NTQ2ODE1ODk5Njc2NCJdLFsibmFtZSIsIjYyNzgwNTIwMTM3MzI3NTUzMDc3MDg4NTE4NDg1NDYyMTA0NjEzMjEyNzY3ODUwMzYwNTc3NDQ4MDUxNTk5MTMxMTM1NTI2NzQ3Nzc2NzMzMDg1MDMwODcyMDE1OTM2MTI2NzE0MTIxMDgxMzg3ODU2MTkwMTkzMzI3ODY3OTE0NTEzODM2NTQ1OTY4Mjg1NTc5ODEyODMxMDI4ODc2Nzg1NzI3OTQ2MTEwNzg5Mzc0MjcyODgzMzkyOTgwNDkwODk3NDkwMTc5MDQ0ODM0NTgwMzQ2ODY4NDI2ODc0ODU4NTY1OTg4NTUyMDcwNjI1NDczNjM4MDM3Njc5NTU1NTk2MzE5MTc3Nzc5OTcxMTIxMjQzMjgyMTIyOTQ2NjY0ODMxOTgxMTg3MzQ3MzcyMjkxMjYwOTM3MzkzNDA1ODk5OTI0NjM4MzE3ODI5MDczODMxMjI4ODc1Njg5MTcyMTg4NjIyMDI5NzcxNzM5MTQ5NDY2Mzg3NTM5NjkyNDQ5NDU5MjczNDI5NzM5MjMzNjkyNTEzNDA5OTkyNDgxNTQ4ODk0NjAzNjM3MTYzNjA4MTM0MTAzMTk3Nzc3NTM4OTYwMDcyMjcyMzYyNzM4NDM1MTM3MDcyNzIxMjExMDYxNTg4MDE3ODczODg3MTEwNDA2OTk1NDQ4ODIwMDEzMDA5MjgyMzk0OTczMDMwMDI5MTY3NjQ5NzY1OTI1MTUxMzY4NTg5OTkyNzMyMDE1ODAwNjAzNzYxOTI3MTg3MDM4MDkxNDY3MDE1MjA3MzIwNDczMDM0NDA3MDIyNDA0NjQ4MTI0NTk2NjQwNjU1NjY1MTIzMzY5Njc0ODI2NDE3MjE2ODUxNTM4Njc1NTM3NzAwOTg4MTQzNzE1NTE3NzMwMTM4NjA4NzkxMjcyMzM0MDUyMzY4OCJdXX0sIm5vbmNlIjoiNDE0MzQ4Njg0NDk2OTAxNjkyMjI2OTY0In0=", }, "mime-type": "application/json", }, ], - "~service": Object { - "recipientKeys": Array [ + "~service": { + "recipientKeys": [ "DXubCT3ahg6N7aASVFVei1GNUTecne8m3iRWjVNiAw31", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://localhost:3000", }, - "~thread": Object { + "~thread": { "thid": "f9f79a46-a4d8-4ee7-9745-1b9cdf03676b", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2023-03-18T18:54:00.014Z", }, }, - "fcdba9cd-3132-4e46-9677-f78c5a146cf0": Object { + "fcdba9cd-3132-4e46-9677-f78c5a146cf0": { "id": "fcdba9cd-3132-4e46-9677-f78c5a146cf0", - "tags": Object { + "tags": { "issuerId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H", "methodName": "indy", "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", @@ -799,12 +799,12 @@ Object { "schemaVersion": "5.0", }, "type": "AnonCredsSchemaRecord", - "value": Object { + "value": { "id": "fcdba9cd-3132-4e46-9677-f78c5a146cf0", - "metadata": Object {}, + "metadata": {}, "methodName": "indy", - "schema": Object { - "attrNames": Array [ + "schema": { + "attrNames": [ "name", "age", ], diff --git a/packages/askar/jest.config.ts b/packages/askar/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/askar/jest.config.ts +++ b/packages/askar/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/askar/package.json b/packages/askar/package.json index 57cd37143f..3e7661e31c 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -27,8 +27,8 @@ "@aries-framework/core": "0.3.3", "@hyperledger/aries-askar-shared": "^0.1.0-dev.6", "bn.js": "^5.2.1", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", + "class-transformer": "0.5.1", + "class-validator": "0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, @@ -36,7 +36,7 @@ "@types/bn.js": "^5.1.0", "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", "reflect-metadata": "^0.1.13", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/bbs-signatures/jest.config.ts b/packages/bbs-signatures/jest.config.ts index 55c67d70a6..8641cf4d67 100644 --- a/packages/bbs-signatures/jest.config.ts +++ b/packages/bbs-signatures/jest.config.ts @@ -6,7 +6,7 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, + displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/bbs-signatures/package.json b/packages/bbs-signatures/package.json index f63fed535d..8f94c602b6 100644 --- a/packages/bbs-signatures/package.json +++ b/packages/bbs-signatures/package.json @@ -35,8 +35,8 @@ "devDependencies": { "@aries-framework/node": "*", "reflect-metadata": "^0.1.13", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" }, "peerDependenciesMeta": { "@animo-id/react-native-bbs-signatures": { diff --git a/packages/core/jest.config.ts b/packages/core/jest.config.ts index 22e2708f18..7b6ec7f1c5 100644 --- a/packages/core/jest.config.ts +++ b/packages/core/jest.config.ts @@ -8,7 +8,6 @@ process.env.TZ = 'GMT' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/core/package.json b/packages/core/package.json index 7b3dd8da9b..5e6ebb6d28 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -31,36 +31,36 @@ "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", "@types/node-fetch": "^2.5.10", - "@types/ws": "^7.4.6", + "@types/ws": "^8.5.4", "abort-controller": "^3.0.0", "borc": "^3.0.0", "buffer": "^6.0.3", "class-transformer": "0.5.1", - "class-validator": "0.13.1", - "did-resolver": "^3.1.3", + "class-validator": "0.14.0", + "did-resolver": "^4.1.0", "lru_map": "^0.4.1", - "luxon": "^1.27.0", + "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.7.0", - "uuid": "^8.3.2", + "uuid": "^9.0.0", "varint": "^6.0.0", - "web-did-resolver": "^2.0.8" + "web-did-resolver": "^2.0.21" }, "devDependencies": { "@types/bn.js": "^5.1.0", "@types/events": "^3.0.0", - "@types/luxon": "^1.27.0", + "@types/luxon": "^3.2.0", "@types/object-inspect": "^1.8.0", - "@types/uuid": "^8.3.0", + "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", "nock": "^13.3.0", "node-fetch": "^2.0", - "rimraf": "^4.0.7", - "tslog": "^3.2.0", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "tslog": "^4.8.2", + "typescript": "~4.9.5" } } diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 6df70279be..0b66feb17b 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -1,10 +1,10 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`UpdateAssistant | v0.1 - v0.2 should correctly update credential records and create didcomm records 1`] = ` -Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { +{ + "1-4e4f-41d9-94c4-f49351b811f1": { "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "messageId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "messageName": "offer-credential", @@ -16,52 +16,52 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "1-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "10-4e4f-41d9-94c4-f49351b811f1": Object { + "10-4e4f-41d9-94c4-f49351b811f1": { "id": "10-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "messageId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "messageName": "offer-credential", @@ -73,52 +73,52 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "createdAt": "2022-01-21T22:50:20.522Z", "id": "10-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "11-4e4f-41d9-94c4-f49351b811f1": Object { + "11-4e4f-41d9-94c4-f49351b811f1": { "id": "11-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "messageId": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "messageName": "request-credential", @@ -130,35 +130,35 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "createdAt": "2022-01-21T22:50:20.522Z", "id": "11-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "12-4e4f-41d9-94c4-f49351b811f1": Object { + "12-4e4f-41d9-94c4-f49351b811f1": { "id": "12-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "messageId": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "messageName": "issue-credential", @@ -170,36 +170,36 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "createdAt": "2022-01-21T22:50:20.522Z", "id": "12-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "2-4e4f-41d9-94c4-f49351b811f1": Object { + "2-4e4f-41d9-94c4-f49351b811f1": { "id": "2-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "messageId": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "messageName": "request-credential", @@ -211,35 +211,35 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "2-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "3-4e4f-41d9-94c4-f49351b811f1": Object { + "3-4e4f-41d9-94c4-f49351b811f1": { "id": "3-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "messageId": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "messageName": "issue-credential", @@ -251,36 +251,36 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "3-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "4-4e4f-41d9-94c4-f49351b811f1": Object { + "4-4e4f-41d9-94c4-f49351b811f1": { "id": "4-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "messageId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "messageName": "offer-credential", @@ -292,52 +292,52 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "createdAt": "2022-01-21T22:50:20.522Z", "id": "4-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "5-4e4f-41d9-94c4-f49351b811f1": Object { + "5-4e4f-41d9-94c4-f49351b811f1": { "id": "5-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "messageId": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "messageName": "request-credential", @@ -349,66 +349,66 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "createdAt": "2022-01-21T22:50:20.522Z", "id": "5-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { + "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": { "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "tags": Object { + "tags": { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", - "credentialIds": Array [], + "credentialIds": [], "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "createdAt": "2022-03-21T22:50:20.522Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -419,53 +419,53 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": Object { + "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": { "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", - "tags": Object { + "tags": { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialIds": Array [ + "credentialIds": [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "createdAt": "2022-03-21T22:50:20.535Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", "credentialRecordType": "indy", }, ], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", "vr_prime": null, }, @@ -479,9 +479,9 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "6-4e4f-41d9-94c4-f49351b811f1": Object { + "6-4e4f-41d9-94c4-f49351b811f1": { "id": "6-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "messageId": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "messageName": "issue-credential", @@ -493,36 +493,36 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "createdAt": "2022-01-21T22:50:20.522Z", "id": "6-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7-4e4f-41d9-94c4-f49351b811f1": Object { + "7-4e4f-41d9-94c4-f49351b811f1": { "id": "7-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "messageId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "messageName": "offer-credential", @@ -534,52 +534,52 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "7-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "8-4e4f-41d9-94c4-f49351b811f1": Object { + "8-4e4f-41d9-94c4-f49351b811f1": { "id": "8-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "messageId": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "messageName": "request-credential", @@ -591,35 +591,35 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "8-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "9-4e4f-41d9-94c4-f49351b811f1": Object { + "9-4e4f-41d9-94c4-f49351b811f1": { "id": "9-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "messageId": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "messageName": "issue-credential", @@ -631,79 +631,79 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "9-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": Object { + "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": { "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "tags": Object { + "tags": { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", - "credentialIds": Array [], + "credentialIds": [], "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "createdAt": "2022-03-21T22:50:20.740Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -714,53 +714,53 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": Object { + "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": { "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", - "tags": Object { + "tags": { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialIds": Array [ + "credentialIds": [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "createdAt": "2022-03-21T22:50:20.746Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", "credentialRecordType": "indy", }, ], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", "vr_prime": null, }, @@ -778,12 +778,12 @@ Object { `; exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection record and create the did and oob records 1`] = ` -Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { +{ + "1-4e4f-41d9-94c4-f49351b811f1": { "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "d56fd7af-852e-458e-b750-7a4f4e53d6e6", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", ], "role": "receiver", @@ -791,9 +791,9 @@ Object { "threadId": "d56fd7af-852e-458e-b750-7a4f4e53d6e6", }, "type": "OutOfBandRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", ], }, @@ -802,30 +802,30 @@ Object { "createdAt": "2022-04-30T13:02:21.577Z", "id": "1-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "d56fd7af-852e-458e-b750-7a4f4e53d6e6", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet2", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "did-communication", }, @@ -845,11 +845,11 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "2-4e4f-41d9-94c4-f49351b811f1": Object { + "2-4e4f-41d9-94c4-f49351b811f1": { "id": "2-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "d939d371-3155-4d9c-87d1-46447f624f44", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", ], "role": "sender", @@ -857,9 +857,9 @@ Object { "threadId": "d939d371-3155-4d9c-87d1-46447f624f44", }, "type": "OutOfBandRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", ], }, @@ -868,30 +868,30 @@ Object { "createdAt": "2022-04-30T13:02:21.608Z", "id": "2-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "d939d371-3155-4d9c-87d1-46447f624f44", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "did-communication", }, @@ -911,11 +911,11 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "3-4e4f-41d9-94c4-f49351b811f1": Object { + "3-4e4f-41d9-94c4-f49351b811f1": { "id": "3-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "21ef606f-b25b-48c6-bafa-e79193732413", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", ], "role": "sender", @@ -923,9 +923,9 @@ Object { "threadId": "21ef606f-b25b-48c6-bafa-e79193732413", }, "type": "OutOfBandRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", ], }, @@ -934,30 +934,30 @@ Object { "createdAt": "2022-04-30T13:02:21.628Z", "id": "3-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "21ef606f-b25b-48c6-bafa-e79193732413", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "did-communication", }, @@ -977,11 +977,11 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "4-4e4f-41d9-94c4-f49351b811f1": Object { + "4-4e4f-41d9-94c4-f49351b811f1": { "id": "4-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "08eb8d8b-67cf-4ce2-9aca-c7d260a5c143", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkmod8vp2nURVktVC5ceQeyr2VUz26iu2ZANLNVg9pMawa", ], "role": "receiver", @@ -989,9 +989,9 @@ Object { "threadId": "08eb8d8b-67cf-4ce2-9aca-c7d260a5c143", }, "type": "OutOfBandRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6Mkmod8vp2nURVktVC5ceQeyr2VUz26iu2ZANLNVg9pMawa", ], }, @@ -1000,30 +1000,30 @@ Object { "createdAt": "2022-04-30T13:02:21.635Z", "id": "4-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "08eb8d8b-67cf-4ce2-9aca-c7d260a5c143", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet2", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6Mkmod8vp2nURVktVC5ceQeyr2VUz26iu2ZANLNVg9pMawa", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "did-communication", }, @@ -1043,11 +1043,11 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "5-4e4f-41d9-94c4-f49351b811f1": Object { + "5-4e4f-41d9-94c4-f49351b811f1": { "id": "5-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "cc67fb5e-1414-4ba6-9030-7456ccd2aaea", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", ], "role": "receiver", @@ -1055,9 +1055,9 @@ Object { "threadId": "cc67fb5e-1414-4ba6-9030-7456ccd2aaea", }, "type": "OutOfBandRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", ], }, @@ -1066,30 +1066,30 @@ Object { "createdAt": "2022-04-30T13:02:21.641Z", "id": "5-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "cc67fb5e-1414-4ba6-9030-7456ccd2aaea", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet2", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "did-communication", }, @@ -1109,11 +1109,11 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "6-4e4f-41d9-94c4-f49351b811f1": Object { + "6-4e4f-41d9-94c4-f49351b811f1": { "id": "6-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "f0ca03d8-2e11-4ff2-a5fc-e0137a434b7e", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", ], "role": "sender", @@ -1121,36 +1121,36 @@ Object { "threadId": "f0ca03d8-2e11-4ff2-a5fc-e0137a434b7e", }, "type": "OutOfBandRecord", - "value": Object { + "value": { "alias": undefined, "autoAcceptConnection": true, "createdAt": "2022-04-30T13:02:21.646Z", "id": "6-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "f0ca03d8-2e11-4ff2-a5fc-e0137a434b7e", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "did-communication", }, @@ -1170,11 +1170,11 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7-4e4f-41d9-94c4-f49351b811f1": Object { + "7-4e4f-41d9-94c4-f49351b811f1": { "id": "7-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "invitationId": "1f516e35-08d3-43d8-900c-99d5239f54da", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", ], "role": "sender", @@ -1182,9 +1182,9 @@ Object { "threadId": "1f516e35-08d3-43d8-900c-99d5239f54da", }, "type": "OutOfBandRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", ], }, @@ -1193,30 +1193,30 @@ Object { "createdAt": "2022-04-30T13:02:21.653Z", "id": "7-4e4f-41d9-94c4-f49351b811f1", "mediatorId": undefined, - "metadata": Object {}, - "outOfBandInvitation": Object { + "metadata": {}, + "outOfBandInvitation": { "@id": "1f516e35-08d3-43d8-900c-99d5239f54da", "@type": "https://didcomm.org/out-of-band/1.1/invitation", - "accept": Array [ + "accept": [ "didcomm/aip1", "didcomm/aip2;env=rfc19", ], "goal": undefined, "goal_code": undefined, - "handshake_protocols": Array [ + "handshake_protocols": [ "https://didcomm.org/connections/1.0", ], "imageUrl": undefined, "label": "Agent: PopulateWallet", "requests~attach": undefined, - "services": Array [ - Object { + "services": [ + { "accept": undefined, "id": "#inline", - "recipientKeys": Array [ + "recipientKeys": [ "did:key:z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "did-communication", }, @@ -1236,10 +1236,10 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7781341d-be29-441b-9b79-4a957d8c6d37": Object { + "7781341d-be29-441b-9b79-4a957d8c6d37": { "id": "7781341d-be29-441b-9b79-4a957d8c6d37", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3QxdHNwMTVjbkREN3dCQ0ZnZWhpUjJTeEhYMWFQeHQ0c3VlRTI0dHdIOUJkI3o2TWt0MXRzcDE1Y25ERDd3QkNGZ2VoaVIyU3hIWDFhUHh0NHN1ZUUyNHR3SDlCZCJdLCJyIjpbXX0", "invitationKey": "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", @@ -1253,14 +1253,14 @@ Object { "verkey": "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", }, "type": "ConnectionRecord", - "value": Object { + "value": { "autoAcceptConnection": false, - "connectionTypes": Array [], + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.628Z", "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "id": "7781341d-be29-441b-9b79-4a957d8c6d37", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3QxdHNwMTVjbkREN3dCQ0ZnZWhpUjJTeEhYMWFQeHQ0c3VlRTI0dHdIOUJkI3o2TWt0MXRzcDE1Y25ERDd3QkNGZ2VoaVIyU3hIWDFhUHh0NHN1ZUUyNHR3SDlCZCJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "3-4e4f-41d9-94c4-f49351b811f1", "role": "responder", "state": "request-received", @@ -1270,10 +1270,10 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "8f4908ee-15ad-4058-9106-eda26eae735c": Object { + "8f4908ee-15ad-4058-9106-eda26eae735c": { "id": "8f4908ee-15ad-4058-9106-eda26eae735c", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2ZpUE1QeENRZVNEWkdNa0N2bTFZMnJCb1BzbXc0WkhNdjcxalh0Y1dSUmlNI3o2TWtmaVBNUHhDUWVTRFpHTWtDdm0xWTJyQm9Qc213NFpITXY3MWpYdGNXUlJpTSJdLCJyIjpbXX0", "invitationKey": "2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy", @@ -1287,14 +1287,14 @@ Object { "verkey": "HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp", }, "type": "ConnectionRecord", - "value": Object { + "value": { "alias": "connection alias", - "connectionTypes": Array [], + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.577Z", "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "id": "8f4908ee-15ad-4058-9106-eda26eae735c", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2ZpUE1QeENRZVNEWkdNa0N2bTFZMnJCb1BzbXc0WkhNdjcxalh0Y1dSUmlNI3o2TWtmaVBNUHhDUWVTRFpHTWtDdm0xWTJyQm9Qc213NFpITXY3MWpYdGNXUlJpTSJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "1-4e4f-41d9-94c4-f49351b811f1", "role": "requester", "state": "completed", @@ -1304,10 +1304,10 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "9383d8e5-c002-4aae-8300-4a21384c919e": Object { + "9383d8e5-c002-4aae-8300-4a21384c919e": { "id": "9383d8e5-c002-4aae-8300-4a21384c919e", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3RDWkFRTkd2V2I0V0hBandCcVB0WGhaZERZb3JiU0prR1c5dmoxdWh3MUhEI3o2TWt0Q1pBUU5HdldiNFdIQWp3QnFQdFhoWmREWW9yYlNKa0dXOXZqMXVodzFIRCJdLCJyIjpbXX0", "invitationKey": "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", @@ -1321,13 +1321,13 @@ Object { "verkey": "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", }, "type": "ConnectionRecord", - "value": Object { - "connectionTypes": Array [], + "value": { + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.608Z", "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "id": "9383d8e5-c002-4aae-8300-4a21384c919e", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3RDWkFRTkd2V2I0V0hBandCcVB0WGhaZERZb3JiU0prR1c5dmoxdWh3MUhEI3o2TWt0Q1pBUU5HdldiNFdIQWp3QnFQdFhoWmREWW9yYlNKa0dXOXZqMXVodzFIRCJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "2-4e4f-41d9-94c4-f49351b811f1", "role": "responder", "state": "completed", @@ -1337,22 +1337,22 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "b65c2ccd-277c-4140-9d87-c8dd30e7a98c": Object { + "b65c2ccd-277c-4140-9d87-c8dd30e7a98c": { "id": "b65c2ccd-277c-4140-9d87-c8dd30e7a98c", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VXVEVtSDFtVW82Vzk2elNXeUg2MTJoRkhvd1J6TkVzY1BZQkwyQ0NNeUMyI3o2TWt1V1RFbUgxbVVvNlc5NnpTV3lINjEyaEZIb3dSek5Fc2NQWUJMMkNDTXlDMiJdLCJyIjpbXX0", "invitationKey": "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", @@ -1365,24 +1365,24 @@ Object { "verkey": "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", }, "type": "ConnectionRecord", - "value": Object { + "value": { "autoAcceptConnection": true, - "connectionTypes": Array [], + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.653Z", "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "id": "b65c2ccd-277c-4140-9d87-c8dd30e7a98c", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VXVEVtSDFtVW82Vzk2elNXeUg2MTJoRkhvd1J6TkVzY1BZQkwyQ0NNeUMyI3o2TWt1V1RFbUgxbVVvNlc5NnpTV3lINjEyaEZIb3dSek5Fc2NQWUJMMkNDTXlDMiJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "7-4e4f-41d9-94c4-f49351b811f1", "role": "responder", "state": "invitation-sent", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "da518433-0e55-4b74-a05b-aa75c1095a99": Object { + "da518433-0e55-4b74-a05b-aa75c1095a99": { "id": "da518433-0e55-4b74-a05b-aa75c1095a99", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa28zMURORTNncU1SWmoxSk5odjJCSGIxY2FRc2hjZDluamdLa0VRWHNnRlJwI3o2TWtvMzFETkUzZ3FNUlpqMUpOaHYyQkhiMWNhUXNoY2Q5bmpnS2tFUVhzZ0ZScCJdLCJyIjpbXX0", "invitationKey": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", @@ -1396,14 +1396,14 @@ Object { "verkey": "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", }, "type": "ConnectionRecord", - "value": Object { + "value": { "autoAcceptConnection": true, - "connectionTypes": Array [], + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.646Z", "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "id": "da518433-0e55-4b74-a05b-aa75c1095a99", "invitationDid": "did:peer:2.SeyJzIjoicnhqczphbGljZSIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa28zMURORTNncU1SWmoxSk5odjJCSGIxY2FRc2hjZDluamdLa0VRWHNnRlJwI3o2TWtvMzFETkUzZ3FNUlpqMUpOaHYyQkhiMWNhUXNoY2Q5bmpnS2tFUVhzZ0ZScCJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "6-4e4f-41d9-94c4-f49351b811f1", "role": "responder", "state": "response-sent", @@ -1413,34 +1413,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT": Object { + "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT": { "id": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", - "tags": Object { + "tags": { "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "legacyUnqualifiedDid": "SDqTzbVuCowusqGBNbNDjH", "method": "peer", "methodSpecificIdentifier": "1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MktCZAQNGvWb4WHAjwBqPtXhZdDYorbSJkGW9vj1uhw1HD", ], }, "createdAt": "2022-04-30T13:02:21.608Z", "did": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#EkJ7p82V", ], "capabilityDelegation": undefined, @@ -1448,20 +1448,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1477,9 +1477,9 @@ Object { ], }, "id": "did:peer:1zQmP96nW6vbNjzwPt19z1NYqhnAfgnAFqfLHcktkmdUFzhT", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"SDqTzbVuCowusqGBNbNDjH#1\\",\\"controller\\":\\"SDqTzbVuCowusqGBNbNDjH\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq\\"}],\\"service\\":[{\\"id\\":\\"SDqTzbVuCowusqGBNbNDjH#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"SDqTzbVuCowusqGBNbNDjH#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"SDqTzbVuCowusqGBNbNDjH\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"SDqTzbVuCowusqGBNbNDjH#1","controller":"SDqTzbVuCowusqGBNbNDjH","type":"Ed25519VerificationKey2018","publicKeyBase58":"EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq"}],"service":[{"id":"SDqTzbVuCowusqGBNbNDjH#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["EkJ7p82VB3a3AfuEWGS3gc1dPyY1BZ4PaVEztjwh1nVq"],"routingKeys":[]}],"authentication":[{"publicKey":"SDqTzbVuCowusqGBNbNDjH#1","type":"Ed25519SignatureAuthentication2018"}],"id":"SDqTzbVuCowusqGBNbNDjH"}", "unqualifiedDid": "SDqTzbVuCowusqGBNbNDjH", }, }, @@ -1487,34 +1487,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56": Object { + "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56": { "id": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", - "tags": Object { + "tags": { "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "legacyUnqualifiedDid": "GkEeb96MGT94K1HyQQzpj1", "method": "peer", "methodSpecificIdentifier": "1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6Mko31DNE3gqMRZj1JNhv2BHb1caQshcd9njgKkEQXsgFRp", ], }, "createdAt": "2022-04-30T13:02:21.646Z", "did": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#9akAmyoF", ], "capabilityDelegation": undefined, @@ -1522,20 +1522,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1551,9 +1551,9 @@ Object { ], }, "id": "did:peer:1zQmPbGa8KDwyjcw9UgwCCgJMV7jU5kKCyvBuwFVc88WxA56", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"GkEeb96MGT94K1HyQQzpj1#1\\",\\"controller\\":\\"GkEeb96MGT94K1HyQQzpj1\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS\\"}],\\"service\\":[{\\"id\\":\\"GkEeb96MGT94K1HyQQzpj1#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"GkEeb96MGT94K1HyQQzpj1#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"GkEeb96MGT94K1HyQQzpj1\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"GkEeb96MGT94K1HyQQzpj1#1","controller":"GkEeb96MGT94K1HyQQzpj1","type":"Ed25519VerificationKey2018","publicKeyBase58":"9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS"}],"service":[{"id":"GkEeb96MGT94K1HyQQzpj1#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["9akAmyoFVow6cWTg2M4LSVTckqbrCjuS3fQpQ8Zrm2eS"],"routingKeys":[]}],"authentication":[{"publicKey":"GkEeb96MGT94K1HyQQzpj1#1","type":"Ed25519SignatureAuthentication2018"}],"id":"GkEeb96MGT94K1HyQQzpj1"}", "unqualifiedDid": "GkEeb96MGT94K1HyQQzpj1", }, }, @@ -1561,34 +1561,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ": Object { + "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ": { "id": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", - "tags": Object { + "tags": { "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "legacyUnqualifiedDid": "XajWZZmHGAWUvYCi7CApaG", "method": "peer", "methodSpecificIdentifier": "1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkw81EsWQioXYC9YJ7uKHCRh6LTN7sfD9sJbSPBGXmUpzC", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6Mkw81EsWQioXYC9YJ7uKHCRh6LTN7sfD9sJbSPBGXmUpzC", ], }, "createdAt": "2022-04-30T13:02:21.577Z", "did": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#HfkCHGAH", ], "capabilityDelegation": undefined, @@ -1596,20 +1596,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1625,9 +1625,9 @@ Object { ], }, "id": "did:peer:1zQmRAfQ6J5qk4qcbHyoStFVkhusazLT9xQcFhdC9dhhQ1cJ", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"XajWZZmHGAWUvYCi7CApaG#1\\",\\"controller\\":\\"XajWZZmHGAWUvYCi7CApaG\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp\\"}],\\"service\\":[{\\"id\\":\\"XajWZZmHGAWUvYCi7CApaG#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"XajWZZmHGAWUvYCi7CApaG#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"XajWZZmHGAWUvYCi7CApaG\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"XajWZZmHGAWUvYCi7CApaG#1","controller":"XajWZZmHGAWUvYCi7CApaG","type":"Ed25519VerificationKey2018","publicKeyBase58":"HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp"}],"service":[{"id":"XajWZZmHGAWUvYCi7CApaG#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["HfkCHGAHTz3j33TRDkKMabYLdnr2FKuWcaXTLzZkZcCp"],"routingKeys":[]}],"authentication":[{"publicKey":"XajWZZmHGAWUvYCi7CApaG#1","type":"Ed25519SignatureAuthentication2018"}],"id":"XajWZZmHGAWUvYCi7CApaG"}", "unqualifiedDid": "XajWZZmHGAWUvYCi7CApaG", }, }, @@ -1635,34 +1635,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb": Object { + "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb": { "id": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", - "tags": Object { + "tags": { "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "legacyUnqualifiedDid": "RtH4qxVPL1Dpmdv7GytjBv", "method": "peer", "methodSpecificIdentifier": "1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6Mkt1tsp15cnDD7wBCFgehiR2SxHX1aPxt4sueE24twH9Bd", ], }, "createdAt": "2022-04-30T13:02:21.628Z", "did": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#EZdqDkqB", ], "capabilityDelegation": undefined, @@ -1670,20 +1670,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1699,9 +1699,9 @@ Object { ], }, "id": "did:peer:1zQmSMBVNMDrh7fyE8bkAmk1ZatshjinpsEqPA3nx8JYjuKb", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"RtH4qxVPL1Dpmdv7GytjBv#1\\",\\"controller\\":\\"RtH4qxVPL1Dpmdv7GytjBv\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF\\"}],\\"service\\":[{\\"id\\":\\"RtH4qxVPL1Dpmdv7GytjBv#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"RtH4qxVPL1Dpmdv7GytjBv#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"RtH4qxVPL1Dpmdv7GytjBv\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"RtH4qxVPL1Dpmdv7GytjBv#1","controller":"RtH4qxVPL1Dpmdv7GytjBv","type":"Ed25519VerificationKey2018","publicKeyBase58":"EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF"}],"service":[{"id":"RtH4qxVPL1Dpmdv7GytjBv#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["EZdqDkqBSfiepgMZ15jsZvtxTwjiz5diBtjJBnvvMvQF"],"routingKeys":[]}],"authentication":[{"publicKey":"RtH4qxVPL1Dpmdv7GytjBv#1","type":"Ed25519SignatureAuthentication2018"}],"id":"RtH4qxVPL1Dpmdv7GytjBv"}", "unqualifiedDid": "RtH4qxVPL1Dpmdv7GytjBv", }, }, @@ -1709,34 +1709,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU": Object { + "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU": { "id": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", - "tags": Object { + "tags": { "did": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", "legacyUnqualifiedDid": "YUH4t3KMkEJiXgmqsncrY9", "method": "peer", "methodSpecificIdentifier": "1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkwc6efk75y4Y1agRx4NGpvtrpKxtKvMfgBEdQkHBwU8Xu", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6Mkwc6efk75y4Y1agRx4NGpvtrpKxtKvMfgBEdQkHBwU8Xu", ], }, "createdAt": "2022-04-30T13:02:21.608Z", "did": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#J9qc5Vre", ], "capabilityDelegation": undefined, @@ -1744,20 +1744,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1773,9 +1773,9 @@ Object { ], }, "id": "did:peer:1zQmXYj3nNwsF37WXXdb8XkCAtsTCBpJJbsLKPPGfi2PWCTU", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"YUH4t3KMkEJiXgmqsncrY9#1\\",\\"controller\\":\\"YUH4t3KMkEJiXgmqsncrY9\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX\\"}],\\"service\\":[{\\"id\\":\\"YUH4t3KMkEJiXgmqsncrY9#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"YUH4t3KMkEJiXgmqsncrY9#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"YUH4t3KMkEJiXgmqsncrY9\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"YUH4t3KMkEJiXgmqsncrY9#1","controller":"YUH4t3KMkEJiXgmqsncrY9","type":"Ed25519VerificationKey2018","publicKeyBase58":"J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX"}],"service":[{"id":"YUH4t3KMkEJiXgmqsncrY9#IndyAgentService","serviceEndpoint":"rxjs:faber","type":"IndyAgent","priority":0,"recipientKeys":["J9qc5VredX3YUBbFNoJz5oJpWPcUWURKVDiUv1DvYukX"],"routingKeys":[]}],"authentication":[{"publicKey":"YUH4t3KMkEJiXgmqsncrY9#1","type":"Ed25519SignatureAuthentication2018"}],"id":"YUH4t3KMkEJiXgmqsncrY9"}", "unqualifiedDid": "YUH4t3KMkEJiXgmqsncrY9", }, }, @@ -1783,34 +1783,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy": Object { + "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy": { "id": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", - "tags": Object { + "tags": { "did": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", "legacyUnqualifiedDid": "WSwJQMBHGZbQsq9LDBTWjX", "method": "peer", "methodSpecificIdentifier": "1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkvW9GxjjUdL9qpaj2qQW6YBhCjZY7Zkzrks3cgpJaRjxR", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkvW9GxjjUdL9qpaj2qQW6YBhCjZY7Zkzrks3cgpJaRjxR", ], }, "createdAt": "2022-04-20T13:02:21.646Z", "did": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#H3tENVV3", ], "capabilityDelegation": undefined, @@ -1818,20 +1818,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1847,9 +1847,9 @@ Object { ], }, "id": "did:peer:1zQmZ2tdw35SaLncSHhf9zBv3e9QmJmLErZRSLsDdYowPHXy", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"WSwJQMBHGZbQsq9LDBTWjX#1\\",\\"controller\\":\\"WSwJQMBHGZbQsq9LDBTWjX\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3\\"}],\\"service\\":[{\\"id\\":\\"WSwJQMBHGZbQsq9LDBTWjX#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"WSwJQMBHGZbQsq9LDBTWjX#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"WSwJQMBHGZbQsq9LDBTWjX\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"WSwJQMBHGZbQsq9LDBTWjX#1","controller":"WSwJQMBHGZbQsq9LDBTWjX","type":"Ed25519VerificationKey2018","publicKeyBase58":"H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3"}],"service":[{"id":"WSwJQMBHGZbQsq9LDBTWjX#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["H3tENVV3HnfNi5tL9qYFh69CuzGG9skW4r8grYLZWXB3"],"routingKeys":[]}],"authentication":[{"publicKey":"WSwJQMBHGZbQsq9LDBTWjX#1","type":"Ed25519SignatureAuthentication2018"}],"id":"WSwJQMBHGZbQsq9LDBTWjX"}", "unqualifiedDid": "WSwJQMBHGZbQsq9LDBTWjX", }, }, @@ -1857,34 +1857,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf": Object { + "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf": { "id": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", - "tags": Object { + "tags": { "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "legacyUnqualifiedDid": "TMnQftvJJJwoYogYkQgVjg", "method": "peer", "methodSpecificIdentifier": "1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MktpVtPC5j91aycGPT5pceiu8EGKDzM5RLwqAZBuCgxw4V", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MktpVtPC5j91aycGPT5pceiu8EGKDzM5RLwqAZBuCgxw4V", ], }, "createdAt": "2022-04-30T13:02:21.641Z", "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#FNEqnwqH", ], "capabilityDelegation": undefined, @@ -1892,20 +1892,20 @@ Object { "controller": undefined, "id": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1921,9 +1921,9 @@ Object { ], }, "id": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"TMnQftvJJJwoYogYkQgVjg#1\\",\\"controller\\":\\"TMnQftvJJJwoYogYkQgVjg\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7\\"}],\\"service\\":[{\\"id\\":\\"TMnQftvJJJwoYogYkQgVjg#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"TMnQftvJJJwoYogYkQgVjg#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"TMnQftvJJJwoYogYkQgVjg\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"TMnQftvJJJwoYogYkQgVjg#1","controller":"TMnQftvJJJwoYogYkQgVjg","type":"Ed25519VerificationKey2018","publicKeyBase58":"FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7"}],"service":[{"id":"TMnQftvJJJwoYogYkQgVjg#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7"],"routingKeys":[]}],"authentication":[{"publicKey":"TMnQftvJJJwoYogYkQgVjg#1","type":"Ed25519SignatureAuthentication2018"}],"id":"TMnQftvJJJwoYogYkQgVjg"}", "unqualifiedDid": "TMnQftvJJJwoYogYkQgVjg", }, }, @@ -1931,34 +1931,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga": Object { + "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga": { "id": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", - "tags": Object { + "tags": { "did": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", "legacyUnqualifiedDid": "YKc7qhYN1TckZAMUf7jgwc", "method": "peer", "methodSpecificIdentifier": "1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkwXNXTehVH7YijDmN1PtaXaSaCniTyaVepmY1EJgS15xq", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkwXNXTehVH7YijDmN1PtaXaSaCniTyaVepmY1EJgS15xq", ], }, "createdAt": "2022-04-30T13:02:21.646Z", "did": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#J57UsQT3", ], "capabilityDelegation": undefined, @@ -1966,20 +1966,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -1995,9 +1995,9 @@ Object { ], }, "id": "did:peer:1zQmadmBfngrYSWhYYxZ24fpW29iwhKhQ6CB6euLabbSK6ga", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"YKc7qhYN1TckZAMUf7jgwc#1\\",\\"controller\\":\\"YKc7qhYN1TckZAMUf7jgwc\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT\\"}],\\"service\\":[{\\"id\\":\\"YKc7qhYN1TckZAMUf7jgwc#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"YKc7qhYN1TckZAMUf7jgwc#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"YKc7qhYN1TckZAMUf7jgwc\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"YKc7qhYN1TckZAMUf7jgwc#1","controller":"YKc7qhYN1TckZAMUf7jgwc","type":"Ed25519VerificationKey2018","publicKeyBase58":"J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT"}],"service":[{"id":"YKc7qhYN1TckZAMUf7jgwc#IndyAgentService","serviceEndpoint":"rxjs:faber","type":"IndyAgent","priority":0,"recipientKeys":["J57UsQT3wa4FcivfKpvjgUtaPDScZhFJ8kd5Q2iR5sBT"],"routingKeys":[]}],"authentication":[{"publicKey":"YKc7qhYN1TckZAMUf7jgwc#1","type":"Ed25519SignatureAuthentication2018"}],"id":"YKc7qhYN1TckZAMUf7jgwc"}", "unqualifiedDid": "YKc7qhYN1TckZAMUf7jgwc", }, }, @@ -2005,34 +2005,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ": Object { + "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ": { "id": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", - "tags": Object { + "tags": { "did": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", "legacyUnqualifiedDid": "Ak15GBhMYpdS8XX3QDMv31", "method": "peer", "methodSpecificIdentifier": "1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkjmCrDWJVf8H2pCHcu11UDs4jb6FVu8nn5yQW24rrgez6", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkjmCrDWJVf8H2pCHcu11UDs4jb6FVu8nn5yQW24rrgez6", ], }, "createdAt": "2022-04-30T13:02:21.628Z", "did": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#6JwodG44", ], "capabilityDelegation": undefined, @@ -2040,20 +2040,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -2069,9 +2069,9 @@ Object { ], }, "id": "did:peer:1zQmc3BZoTinpVaG3oZ4PmRVN4JMdNZGCmPkS6smmTNLnvEZ", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"Ak15GBhMYpdS8XX3QDMv31#1\\",\\"controller\\":\\"Ak15GBhMYpdS8XX3QDMv31\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi\\"}],\\"service\\":[{\\"id\\":\\"Ak15GBhMYpdS8XX3QDMv31#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"Ak15GBhMYpdS8XX3QDMv31#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"Ak15GBhMYpdS8XX3QDMv31\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"Ak15GBhMYpdS8XX3QDMv31#1","controller":"Ak15GBhMYpdS8XX3QDMv31","type":"Ed25519VerificationKey2018","publicKeyBase58":"6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi"}],"service":[{"id":"Ak15GBhMYpdS8XX3QDMv31#IndyAgentService","serviceEndpoint":"rxjs:faber","type":"IndyAgent","priority":0,"recipientKeys":["6JwodG44KanZhhSvDS3dNmWjmWyeVFYRPxVaBntqmSCi"],"routingKeys":[]}],"authentication":[{"publicKey":"Ak15GBhMYpdS8XX3QDMv31#1","type":"Ed25519SignatureAuthentication2018"}],"id":"Ak15GBhMYpdS8XX3QDMv31"}", "unqualifiedDid": "Ak15GBhMYpdS8XX3QDMv31", }, }, @@ -2079,34 +2079,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui": Object { + "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui": { "id": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", - "tags": Object { + "tags": { "did": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", "legacyUnqualifiedDid": "9jTqUnV4k5ucxbyxumAaV7", "method": "peer", "methodSpecificIdentifier": "1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkjDJL4X7YGoH6gjamhZR2NzowPZqtJfX5kPuNuWiVdjMr", ], }, "createdAt": "2022-04-30T13:02:21.641Z", "did": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#5m3HUGs6", ], "capabilityDelegation": undefined, @@ -2114,20 +2114,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -2143,9 +2143,9 @@ Object { ], }, "id": "did:peer:1zQmcXZepLE55VGCMELEFjMd4nKrzp3GGyRR3r3MYermagui", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"9jTqUnV4k5ucxbyxumAaV7#1\\",\\"controller\\":\\"9jTqUnV4k5ucxbyxumAaV7\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU\\"}],\\"service\\":[{\\"id\\":\\"9jTqUnV4k5ucxbyxumAaV7#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"9jTqUnV4k5ucxbyxumAaV7#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"9jTqUnV4k5ucxbyxumAaV7\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"9jTqUnV4k5ucxbyxumAaV7#1","controller":"9jTqUnV4k5ucxbyxumAaV7","type":"Ed25519VerificationKey2018","publicKeyBase58":"5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU"}],"service":[{"id":"9jTqUnV4k5ucxbyxumAaV7#IndyAgentService","serviceEndpoint":"rxjs:faber","type":"IndyAgent","priority":0,"recipientKeys":["5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU"],"routingKeys":[]}],"authentication":[{"publicKey":"9jTqUnV4k5ucxbyxumAaV7#1","type":"Ed25519SignatureAuthentication2018"}],"id":"9jTqUnV4k5ucxbyxumAaV7"}", "unqualifiedDid": "9jTqUnV4k5ucxbyxumAaV7", }, }, @@ -2153,34 +2153,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw": Object { + "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw": { "id": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", - "tags": Object { + "tags": { "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "legacyUnqualifiedDid": "WewvCdyBi4HL8ogyGviYVS", "method": "peer", "methodSpecificIdentifier": "1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkvcgxQSsX5WA8vcBokLZ46znnhRBH6aKAGYnonEUfUnQV", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkvcgxQSsX5WA8vcBokLZ46znnhRBH6aKAGYnonEUfUnQV", ], }, "createdAt": "2022-04-30T13:02:21.635Z", "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#HARupCd5", ], "capabilityDelegation": undefined, @@ -2188,20 +2188,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -2217,9 +2217,9 @@ Object { ], }, "id": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"WewvCdyBi4HL8ogyGviYVS#1\\",\\"controller\\":\\"WewvCdyBi4HL8ogyGviYVS\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7\\"}],\\"service\\":[{\\"id\\":\\"WewvCdyBi4HL8ogyGviYVS#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"WewvCdyBi4HL8ogyGviYVS#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"WewvCdyBi4HL8ogyGviYVS\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"WewvCdyBi4HL8ogyGviYVS#1","controller":"WewvCdyBi4HL8ogyGviYVS","type":"Ed25519VerificationKey2018","publicKeyBase58":"HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7"}],"service":[{"id":"WewvCdyBi4HL8ogyGviYVS#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7"],"routingKeys":[]}],"authentication":[{"publicKey":"WewvCdyBi4HL8ogyGviYVS#1","type":"Ed25519SignatureAuthentication2018"}],"id":"WewvCdyBi4HL8ogyGviYVS"}", "unqualifiedDid": "WewvCdyBi4HL8ogyGviYVS", }, }, @@ -2227,34 +2227,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX": Object { + "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX": { "id": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", - "tags": Object { + "tags": { "did": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", "legacyUnqualifiedDid": "3KAjJWF5NjiDTUm6JpPBQD", "method": "peer", "methodSpecificIdentifier": "1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkfiPMPxCQeSDZGMkCvm1Y2rBoPsmw4ZHMv71jXtcWRRiM", ], }, "createdAt": "2022-04-30T13:02:21.577Z", "did": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#2G8Johwy", ], "capabilityDelegation": undefined, @@ -2262,20 +2262,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:faber", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -2291,9 +2291,9 @@ Object { ], }, "id": "did:peer:1zQmeHpGaZ48DnAP2k3KntXB1vmd8MgLEdcb4EQzqWJDHcbX", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"3KAjJWF5NjiDTUm6JpPBQD#1\\",\\"controller\\":\\"3KAjJWF5NjiDTUm6JpPBQD\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy\\"}],\\"service\\":[{\\"id\\":\\"3KAjJWF5NjiDTUm6JpPBQD#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:faber\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"3KAjJWF5NjiDTUm6JpPBQD#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"3KAjJWF5NjiDTUm6JpPBQD\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"3KAjJWF5NjiDTUm6JpPBQD#1","controller":"3KAjJWF5NjiDTUm6JpPBQD","type":"Ed25519VerificationKey2018","publicKeyBase58":"2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy"}],"service":[{"id":"3KAjJWF5NjiDTUm6JpPBQD#IndyAgentService","serviceEndpoint":"rxjs:faber","type":"IndyAgent","priority":0,"recipientKeys":["2G8JohwyJtj69ruWFC3hBkdoaJW5eg31E66ohceVWCvy"],"routingKeys":[]}],"authentication":[{"publicKey":"3KAjJWF5NjiDTUm6JpPBQD#1","type":"Ed25519SignatureAuthentication2018"}],"id":"3KAjJWF5NjiDTUm6JpPBQD"}", "unqualifiedDid": "3KAjJWF5NjiDTUm6JpPBQD", }, }, @@ -2301,34 +2301,34 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv": Object { + "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv": { "id": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", - "tags": Object { + "tags": { "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "legacyUnqualifiedDid": "Ud6AWCk6WrwfYKZUw5tJmt", "method": "peer", "methodSpecificIdentifier": "1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { - "recipientKeyFingerprints": Array [ + "value": { + "_tags": { + "recipientKeyFingerprints": [ "z6MkuWTEmH1mUo6W96zSWyH612hFHowRzNEscPYBL2CCMyC2", ], }, "createdAt": "2022-04-30T13:02:21.653Z", "did": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], "alsoKnownAs": undefined, "assertionMethod": undefined, - "authentication": Array [ + "authentication": [ "#G4CCB2mL", ], "capabilityDelegation": undefined, @@ -2336,20 +2336,20 @@ Object { "controller": undefined, "id": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", "keyAgreement": undefined, - "service": Array [ - Object { + "service": [ + { "id": "#IndyAgentService", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "rxjs:alice", "type": "IndyAgent", }, ], - "verificationMethod": Array [ - Object { + "verificationMethod": [ + { "blockchainAccountId": undefined, "controller": "#id", "ethereumAddress": undefined, @@ -2365,9 +2365,9 @@ Object { ], }, "id": "did:peer:1zQmfDAtfDZcK4trJBsvVTXrBx9uaLCHSUZH9X2LFaAd3JKv", - "metadata": Object { - "_internal/legacyDid": Object { - "didDocumentString": "{\\"@context\\":\\"https://w3id.org/did/v1\\",\\"publicKey\\":[{\\"id\\":\\"Ud6AWCk6WrwfYKZUw5tJmt#1\\",\\"controller\\":\\"Ud6AWCk6WrwfYKZUw5tJmt\\",\\"type\\":\\"Ed25519VerificationKey2018\\",\\"publicKeyBase58\\":\\"G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe\\"}],\\"service\\":[{\\"id\\":\\"Ud6AWCk6WrwfYKZUw5tJmt#IndyAgentService\\",\\"serviceEndpoint\\":\\"rxjs:alice\\",\\"type\\":\\"IndyAgent\\",\\"priority\\":0,\\"recipientKeys\\":[\\"G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe\\"],\\"routingKeys\\":[]}],\\"authentication\\":[{\\"publicKey\\":\\"Ud6AWCk6WrwfYKZUw5tJmt#1\\",\\"type\\":\\"Ed25519SignatureAuthentication2018\\"}],\\"id\\":\\"Ud6AWCk6WrwfYKZUw5tJmt\\"}", + "metadata": { + "_internal/legacyDid": { + "didDocumentString": "{"@context":"https://w3id.org/did/v1","publicKey":[{"id":"Ud6AWCk6WrwfYKZUw5tJmt#1","controller":"Ud6AWCk6WrwfYKZUw5tJmt","type":"Ed25519VerificationKey2018","publicKeyBase58":"G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe"}],"service":[{"id":"Ud6AWCk6WrwfYKZUw5tJmt#IndyAgentService","serviceEndpoint":"rxjs:alice","type":"IndyAgent","priority":0,"recipientKeys":["G4CCB2mL9Fc32c9jqQKF9w9FUEfaaUzWvNdFVkEBSkQe"],"routingKeys":[]}],"authentication":[{"publicKey":"Ud6AWCk6WrwfYKZUw5tJmt#1","type":"Ed25519SignatureAuthentication2018"}],"id":"Ud6AWCk6WrwfYKZUw5tJmt"}", "unqualifiedDid": "Ud6AWCk6WrwfYKZUw5tJmt", }, }, @@ -2375,10 +2375,10 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199": Object { + "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199": { "id": "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2pESkw0WDdZR29INmdqYW1oWlIyTnpvd1BacXRKZlg1a1B1TnVXaVZkak1yI3o2TWtqREpMNFg3WUdvSDZnamFtaFpSMk56b3dQWnF0SmZYNWtQdU51V2lWZGpNciJdLCJyIjpbXX0", "invitationKey": "5m3HUGs6wFndaEk51zTBXuFwZza2tnGj4NzT5EkUiWaU", @@ -2392,14 +2392,14 @@ Object { "verkey": "FNEqnwqHoU6WVmYkQFeosoaESjx8wCAzFpFdMdEg3iH7", }, "type": "ConnectionRecord", - "value": Object { + "value": { "autoAcceptConnection": false, - "connectionTypes": Array [], + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.641Z", "did": "did:peer:1zQma8LpnJ22GxQdyASV5jP6psacAGtJ6ytk4pVayYp4erRf", "id": "e3f9bc2b-f0a1-4a2c-ab81-2f0a3488c199", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa2pESkw0WDdZR29INmdqYW1oWlIyTnpvd1BacXRKZlg1a1B1TnVXaVZkak1yI3o2TWtqREpMNFg3WUdvSDZnamFtaFpSMk56b3dQWnF0SmZYNWtQdU51V2lWZGpNciJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "5-4e4f-41d9-94c4-f49351b811f1", "role": "requester", "state": "response-received", @@ -2409,10 +2409,10 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "ee88e2e1-e27e-46a6-a910-f87690109e32": Object { + "ee88e2e1-e27e-46a6-a910-f87690109e32": { "id": "ee88e2e1-e27e-46a6-a910-f87690109e32", - "tags": Object { - "connectionTypes": Array [], + "tags": { + "connectionTypes": [], "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa21vZDh2cDJuVVJWa3RWQzVjZVFleXIyVlV6MjZpdTJaQU5MTlZnOXBNYXdhI3o2TWttb2Q4dnAyblVSVmt0VkM1Y2VRZXlyMlZVejI2aXUyWkFOTE5WZzlwTWF3YSJdLCJyIjpbXX0", "invitationKey": "8MN6LZnM8t1HmzMNw5Sp8kUVfQkFK1nCUMRSfQBoSNAC", @@ -2425,13 +2425,13 @@ Object { "verkey": "HARupCd5jxffp7M74mbDFuEnsquRgh4oaXsswxWeZZd7", }, "type": "ConnectionRecord", - "value": Object { - "connectionTypes": Array [], + "value": { + "connectionTypes": [], "createdAt": "2022-04-30T13:02:21.635Z", "did": "did:peer:1zQmduuYkxRKJuVyvDqttdd9eDfBwDnF1DAU5FFQo4whx7Uw", "id": "ee88e2e1-e27e-46a6-a910-f87690109e32", "invitationDid": "did:peer:2.SeyJzIjoicnhqczpmYWJlciIsInQiOiJkaWQtY29tbXVuaWNhdGlvbiIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa21vZDh2cDJuVVJWa3RWQzVjZVFleXIyVlV6MjZpdTJaQU5MTlZnOXBNYXdhI3o2TWttb2Q4dnAyblVSVmt0VkM1Y2VRZXlyMlZVejI2aXUyWkFOTE5WZzlwTWF3YSJdLCJyIjpbXX0", - "metadata": Object {}, + "metadata": {}, "outOfBandId": "4-4e4f-41d9-94c4-f49351b811f1", "role": "requester", "state": "request-sent", @@ -2443,10 +2443,10 @@ Object { `; exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the credential records and create didcomm records with auto update 1`] = ` -Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { +{ + "1-4e4f-41d9-94c4-f49351b811f1": { "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "messageId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "messageName": "offer-credential", @@ -2458,52 +2458,52 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "1-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "10-4e4f-41d9-94c4-f49351b811f1": Object { + "10-4e4f-41d9-94c4-f49351b811f1": { "id": "10-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "messageId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "messageName": "offer-credential", @@ -2515,52 +2515,52 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "createdAt": "2022-01-21T22:50:20.522Z", "id": "10-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "11-4e4f-41d9-94c4-f49351b811f1": Object { + "11-4e4f-41d9-94c4-f49351b811f1": { "id": "11-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "messageId": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "messageName": "request-credential", @@ -2572,35 +2572,35 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "createdAt": "2022-01-21T22:50:20.522Z", "id": "11-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "12-4e4f-41d9-94c4-f49351b811f1": Object { + "12-4e4f-41d9-94c4-f49351b811f1": { "id": "12-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "messageId": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "messageName": "issue-credential", @@ -2612,36 +2612,36 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", "createdAt": "2022-01-21T22:50:20.522Z", "id": "12-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "2-4e4f-41d9-94c4-f49351b811f1": Object { + "2-4e4f-41d9-94c4-f49351b811f1": { "id": "2-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "messageId": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "messageName": "request-credential", @@ -2653,35 +2653,35 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "2-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "3-4e4f-41d9-94c4-f49351b811f1": Object { + "3-4e4f-41d9-94c4-f49351b811f1": { "id": "3-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "messageId": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "messageName": "issue-credential", @@ -2693,36 +2693,36 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "3-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "4-4e4f-41d9-94c4-f49351b811f1": Object { + "4-4e4f-41d9-94c4-f49351b811f1": { "id": "4-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "messageId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "messageName": "offer-credential", @@ -2734,52 +2734,52 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "createdAt": "2022-01-21T22:50:20.522Z", "id": "4-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiNTQzODYyOTczNzUxMjk1OTk2MTAzNjA3In0=", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "5-4e4f-41d9-94c4-f49351b811f1": Object { + "5-4e4f-41d9-94c4-f49351b811f1": { "id": "5-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "messageId": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "messageName": "request-credential", @@ -2791,66 +2791,66 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "createdAt": "2022-01-21T22:50:20.522Z", "id": "5-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "edba1c87-51d3-4c70-aff2-ab8016e1060e", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiRUg2OTVENjRRd2hWRmtyazFtcDQ5aiIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiOTcwNjA5MzQ1NDAxNzE0NDIxNjQzNDg0NDE0MTQwOTA0NzMzNTQ4NTA4NzY0OTk5MTgxNzY1ODI3MjM3ODg3NjQ2MzQzNDYyMTY0ODA3MTUxMjg1ODk2MDczODAyNDY1MDMwMTcxNTIyODE4ODY4Mjc2ODUwMzE0NzQzMjM3ODc3NDMyMDgxMTQwMzE5ODU5OTM5NjM0MTI4NzkzOTk4NDcwMTUzNTQ0NjgxNTM5NDg4NzEyNDE5MTk3NzcxODE1NjU5Nzg1NDE5NTA1ODEyODI4NzYxOTI4MzExODczNjA5NDYwMjExMzQ4OTAyNDk4NzYxNjc5OTIzNzA2NjUwMDIzODg4ODU4NzE1MTYxMTIyMzA5MTc1MTE3MTk0NDMwNTI1ODY5NjcwMDEzMTgxNTkzNjI4NDQzMjk2MDI0MDE5NTc4MzIzODQwNzk0OTQ0MjIyODE1MTQwNTM2Mjc3ODQwMjU2ODc2MDE1MzUwNDgzOTE2MjYzMDgyODM5NzI2NzAxNjg4NjcxNDY0MjA3MzQxMTgwMjg3Mjk0Njg4NDA3NzQ3MDk1NjA0NzQ3NzA3OTc2Nzk2MjU0MTQ2NDQ0NTY5NzQ4MTk4OTMwMjkyOTkzNjY1ODk2MTUyMDMyNzQwODY3OTgwMjczMTMxMDM3NjkwNDkzNDU1Mjc4NDc3MDc3NjE0OTU2NjgzNjgxNDc5NzY3Njg0MDI4MzU5NzE4NzM0ODEyNzcyMDIyOTIwODQ3NDIyNDYyOTAwMjczMTcwMTU2NzQyMzUyMDQ2NDYyODI4NzAxMTE2MzU0MTkwMDY5MDE0NTIwMSIsInVyIjpudWxsLCJoaWRkZW5fYXR0cmlidXRlcyI6WyJtYXN0ZXJfc2VjcmV0Il0sImNvbW1pdHRlZF9hdHRyaWJ1dGVzIjp7fX0sImJsaW5kZWRfbXNfY29ycmVjdG5lc3NfcHJvb2YiOnsiYyI6IjE4MzE5MTUyNjg0NDkyMTM0OTc4MzkzNDE3OTY5NjQ5MDIyNzMzNzQzMTQ5NTUwNzAwNzc5ODk0Nzg1MTg3MTA1OTkyNjk4Mzg5MjAzIiwidl9kYXNoX2NhcCI6IjQ0NzA4MzAwOTUyNzA3MjA3NjI3NjA3NzM4MTI2NDgxNzA3OTA1MDcwNjEyMzQ5OTIxNTAxNTYyOTA2MzgyMzE0MDE4MzQ4MTAxMTE4MDI4MDIyMjk5OTgyNTEwMjI5ODM1OTQxMzY1MTM4Njg0MTU1OTEyOTE3NzYwMjgwOTIyNDk1MjE1ODA2NTM3MzA5NDU5NDA3NjcxNDgyNDA0NDMwODU4MzU5ODU3MDgzNzg1Njk1MTYzMjkwMzEyNzMzNDAxNjY1NDk5MjUwMDQ0NzkwODk4OTA4NzIzNzE1OTc0MDYwNzgyNDEzODYzMTU0MTUxNjg2OTM3ODY4MDM5MDU1Nzc1MzA5MjQ0MTYzOTUxNzgwMTgxNDk5MDM5MDgyMjcxNzgzNTgzNTkxMDIyNjYwMDYyMDQ3MDQ2NTEyMTM0NDU5OTI1MTgyOTg2MTkxOTAwNTQwMDg4MjE3Mzc2NjM4MjEzNTI0MDUxNjcxNzg3ODY0ODQ2NzIxNjk5NjQzNDk0MTI2MjA3NTg2MjgwNjQ5OTc4ODE2ODEwMjM5OTAzMzU0NzIyOTI2NTUxODYyNTQwMjc4ODU2OTEyNDQ2MDUzMTg4MzI3Mjk4NDc0NjgzMjkwMjU4MjgwNjY0OTgyOTM2NTYyODcwNTIyODA2NzYwMjE1OTI0MDc3ODQ2NjA2NTM0NzI4MjkyODQ2MDQyOTk1NjgxMTQzOTQ5MTU0MTU0NTU2NzQzMDYzMzY1OTIzMzU2OTg1MjQ5ODI1Njk2NzE3MzE2MDk1NzE5MDU2MTE5NTE0NTYwODY0MzUyMDc4ODMyOTYzNjI3Mjk0Njk1ODQ0MTE5NDA1NTMzNTY5MTI2ODExODE3NDYyNjczMzM3OTg4MzA0MDcwMzk5ODYyNTk2ODMyNDk2OTU3NzA4Nzc0NzI5NzAyOTEyNjY3Njk0OTgwMjc3OTI5MDgzNiIsIm1fY2FwcyI6eyJtYXN0ZXJfc2VjcmV0IjoiMjIxNTAyNTExNTYzODg1MTM1NzIwNjg4MzE5Njk5NzE5ODAxNDI4NDgzMTI2MjY0NjI0NTE5OTA4MjM5NTM0MjQ3MDQ3NTIwODgyNDE4Mzk0ODMzNjUwODM2NTI0NDk2MzUzMDkwNzIzOTI1MDc2NDY2ODQ3NjIxNzE4MDA4MDAxNTQyNTMzOTk4NTU1MzA1MDYwNjUzMzkwMjc4MDc2MzE2Mjg5MzcwMzA2MDcyMDYxNCJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiI2OTgzNzA2MTYwMjM4ODM3MzA0OTgzNzUifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": Object { + "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a": { "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "tags": Object { + "tags": { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", - "credentialIds": Array [], + "credentialIds": [], "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "createdAt": "2022-03-21T22:50:20.522Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -2861,53 +2861,53 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": Object { + "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a": { "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", - "tags": Object { + "tags": { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialIds": Array [ + "credentialIds": [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "createdAt": "2022-03-21T22:50:20.535Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", "credentialRecordType": "indy", }, ], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", "vr_prime": null, }, @@ -2921,9 +2921,9 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "6-4e4f-41d9-94c4-f49351b811f1": Object { + "6-4e4f-41d9-94c4-f49351b811f1": { "id": "6-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "messageId": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "messageName": "issue-credential", @@ -2935,36 +2935,36 @@ Object { "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", "createdAt": "2022-01-21T22:50:20.522Z", "id": "6-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "a340a7b9-f4d4-4892-b7d2-1d3d40e4be48", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFnZSI6eyJyYXciOiIyNSIsImVuY29kZWQiOiIyNSJ9LCJkYXRlT2ZCaXJ0aCI6eyJyYXciOiIyMDIwLTAxLTAxIiwiZW5jb2RlZCI6IjQxMDYxMjkzNzgwNDYyMDc1NTE0MjA4MjIzODk3NTkwNDA3ODAwNzQ4NTQzMDE0OTkxNTg4NTIyNzIxMTkzODg2ODk4MTE5NzUzNjI0In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6IjEwMzE4MTcwNjEzMDMzODg1ODkxNDkzMzk2MjU5NDYyNDIxMzM3MjY3ODcyNzkyNzczNjgwMDQwMTgyNTE4NDM1MzEzMDYyNjE3MTcxMCIsImEiOiI3MTM3NDExNDU3NjI3MDE5MDcyNjY0ODMzNTQ5MjAzMDQyMTg0MDQ0OTUxNTU5MzYwMDczMzk1MjM4NjkwMzkwNjgxNzA3ODAyNzY0MTE2MzQ4NjE4NjgxOTgzNTM2MTczNTE3MzgxODc5NzQ0MzQyODIzMDkzNzc4MTk1NzYxNzgzMjQ3NDcxOTQ2NDgwODc0OTY2OTQyNjY0NzU4NjIwNzEyNzExODExNTY5NTc1NzMwNTg4NTQwNDI1MjEzNjY0OTg0OTQyOTIzODU5NzQ3MjIzNjA5ODQ1NjIwMjE5NTY1NzYxODY2OTMwMzI3OTYzNTIwNzE5MTg2NjMyNTQ4NzkzNDI3MTQ0NTY0NTAxNjE1NTg1MTI2NzkxNzM5Njg3ODc2MzIxMTQ1NTAzNDU1OTM0MzUxODc0MjQ3NjA0NzAwODYxMTgxNjY4NjUxNzQ5NTExMjY0MzExMDU2NjI5MjM4NjQwNDY2NzM4ODA0NjI0NzU1MzEzODgxMjQzMjkwNDM5ODI4MDE0NzY0MTQ3NDM1MzE3NTY3MjY3MzQ2NTQxMTY2ODIwNTI2MDkyMDAyMDE0NzY5NjE1MzI3Mjg4MTYwMzM0MDg1MTQ1MzQ0Mzc5MzAxMDg1NDc1MDEyNzUzNDIzNzkxMjI4NzM5NDE3MzA0OTM2NDUzNDEyMDYxNjY0MTUzNTM4MjM5MDA0OTUxMDgyODQxMTE1MTIyNDQ1MzkzNzc5Mzc5NDI5NjQzMjMxNDE2NDQzNDM4NDQ1NzI1NTAzMDc0MTM5MzEwOTc2MjkwMTQ2MTIwMDI2MDI1NTczNzIyNTUyOCIsImUiOiIyNTkzNDQ3MjMwNTUwNjIwNTk5MDcwMjU0OTE0ODA2OTc1NzE5MzgyNzc4ODk1MTUxNTIzMDYyNDk3Mjg1ODMxMDU2NjU4MDA3MTMzMDY3NTkxNDk5ODE2OTA1NTkxOTM5ODcxNDMwMTIzNjc5MTMyMDYyOTkzMjM4OTk2OTY5NDIyMTMyMzU5NTY3NDI5MzAxNTQ3NTU5NjM2MjQ0MTcyOTU5NjM1MjY1ODc1MTkyNjIwNTEiLCJ2IjoiODMxMTU0NzI2ODU4Mzg3ODc5NTU5NDUzNzcwNjg3MzIyMzE1NTgyMjYzMTk3NDUzMjMxNTI1ODIzNDIzNjkxMDk5Mzc2NTExNzI0ODAxMzA1MTU0NzY2Mjc0NjQ1OTMzMTAwOTIzMjIxOTgwNDkyNzE0NDQxMTY0NTYxNTIwMDIzMjYzMDYzMzQ4NjQ4NzkzMjkxMzEwNDc0NTU5NDIwMTkzMTA1MjE5NzMxNTAwMTc0ODg0NzQ0MDk1MjU3MDYyMjczODA2OTYzNjg5MjY3NzA1NTg4NTQ4MzU4NzQ2NDc1Mzc1MzAzMjI1OTI3MDkxMzA0NzQ2NTg5MzA1MDEzNjc1ODk0MzIxMDkzNjE0NzIxMjQwNDAzNDE5OTM5OTk0Mjg5NTU2MzY0MDExMTg2ODQ2NjMxNTA2OTU0NDg0NjM4NjgwMzEyMDA2Njk0MjcwOTkwNDU3NTk3NjAyNzc5MjUzMDc3MTg3NDgwNTg5NDMyNzU4ODgwMjY3NzA1NzMyMjg3Nzc0ODczOTI3MDExMTQ1MDE1NzgyNjE5NzI4NTAxNjI5MTE4ODE1ODM2NjU2OTMzMzcwMzgwNDk4NDk5MDE0MDEzNDI1NDMwMjMwODQzODc0OTk3NTg0NTY3NTA0Mzg3OTE4MjQxMzMxNTM1NDk5MTQxMjU1NzQzNjQ0MzgwNTQ4ODAxNDUwNDEyMzQzMTAxODc4Nzg3NzIzOTIxMDU5NjQyNDg2MjE3NjE0MzQyMDc0MTQ3ODk1ODA2MzQ1NTQ0Njk3NjI2MzcwMDY1MjYxMjQ3OTM2NzMwMzMyNTkyMjM3NDY1MjIyNTQ1MjE4ODc4MDk0ODk0NTE0ODU2ODUyNTU1NjI4MjIwNDg2MTU5MjcyMjIxMjM3MDkwMjc4NjUyNDM2MzcyNTE4MDgzOTUxNjAwNzI1ODA1MDYwNjExMTkyNzEyOTI3MjQ2MTUxMDU4NzU5NDk2MzQ3NjA0NDQwNDcxODcwODEyNzMwODE3MTU4NzQxNDQ1MDA0OTg2MTgyNDk5NDA4OTQxMzk2NjcwMzEyOTU0NDQ1MTk1NTM5MTM5MzIwMDI4MTI3NzMyMDQ0MSJ9LCJyX2NyZWRlbnRpYWwiOm51bGx9LCJzaWduYXR1cmVfY29ycmVjdG5lc3NfcHJvb2YiOnsic2UiOiIyOTQ0ODU2NTEyNDk3MjM2MTc2OTQ3OTMxNzM1Mjk0NDc4MTU5NTE2ODM4OTExNDEyMDE3MTI1NTAyNjc1ODM2Nzk1NTQ4OTczNDMyMTg2NjI0MTc2NjQwNzAxNjczOTk4MjI3NTEzMTA0OTU5OTgzMjk1NjI0MTA0MjkwNjgzMjU0OTI4NTg1MDkwMTI2Mjk5ODczMjY4NDYyODA5NTY4NjMxNDg3MDk2ODgzMDE0OTcwMTcyMzM0MzIwMTM4OTg5ODkzMzcyODIyODU2MTQxMTM5NTQ0MzQyMzExNDEzNDgyMDU0OTYyNjE5NTgzNjU5NjMwMzYxNTc3Nzg0MTQ4NjY3NTgwNjMxODc4NDIwNTgzMDk5OTM1NzE0NDYyMzE5Njg4NDE5MTM4NjY3Nzk2Nzc2MzQwMDk2MDIxMTgwMTU0ODU0Njg1MDEzNzY1MTM0ODMwNjA0MzEyMjI0MjIwNzA5MzQwODExMTI4MDczMDk3NjEyMDA0MTE4NjA0MDI3MTczNjExNTE2ODA0NTQxMzUzODA2OTkxMTg0MDY3MzY1MDE5Nzk2NDM2MDA3NDI0NTM5NzQ4ODQxMjU4NjA1MTUxNTQxNzQzMDk4MTE5NzI1Mzc4MTQ4MTgyNDQ2Njg3NDQ0MjE0ODk1NTE2NDc3MTM4Mjk1OTI1Mzc4Nzg1MTA3OTc5MTYxNTIyNTYyNDk2Njg2MTAzMjg3MDUxNDUyMTE3NTQzMzc4Mzc0MDM5Mjg3NzgxMjAwNzgxMTkyNDQyOTY5MDU1NjIwNTcyODg1NDM5NjU2MTU3Njk2Mzc5MTAyNDc2MTQ2IiwiYyI6IjI4NjYxMjE3ODQ5OTg3ODI4MTA2Nzk5MTAxOTIxNDcyNzgxNDMzNzgzMDE5Mzg0NzAwMDUzMjU4MTU5NDUxNjc5MjMwODI0NzA5NjIifSwicmV2X3JlZyI6bnVsbCwid2l0bmVzcyI6bnVsbH0=", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7-4e4f-41d9-94c4-f49351b811f1": Object { + "7-4e4f-41d9-94c4-f49351b811f1": { "id": "7-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "messageId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "messageName": "offer-credential", @@ -2976,52 +2976,52 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "7-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "@type": "https://didcomm.org/issue-credential/1.0/offer-credential", - "credential_preview": Object { + "credential_preview": { "@type": "https://didcomm.org/issue-credential/1.0/credential-preview", - "attributes": Array [ - Object { + "attributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], }, - "offers~attach": Array [ - Object { + "offers~attach": [ + { "@id": "libindy-cred-offer-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsImtleV9jb3JyZWN0bmVzc19wcm9vZiI6eyJjIjoiMTEzOTY4MTg4OTM2OTQ5MzcyNzU3NjU2NzI1MjU1MTQ3NDk1OTI5NTM0MjQ5NjU1MzY4NTMzMTY4OTIzMjU4NTA2OTUzOTk3MTI2MDEyIiwieHpfY2FwIjoiMjM5NDkwMjQ4MjE4MTExOTQ1MjIxOTQ1ODcyMTE4MjQzNzA3NjE5OTQ4MzQ0MjU1ODM5ODI4NTU3NjkyNTE3NDExNDMwNDgwNDgwMTkxMTMwMjM0OTg5ODk0NzIyNDE2Nzk1MzUzODAwMDk3NDUxMjI5NDE4MzQ0MjEyOTI3NDk1NjI2NTc4MTk2ODUxMTcwMTI0MDI1NDk1MDExMjc0NjU1NjQzNjkzNTE1ODczMjA5OTczMzgwNjA3MzQxNzQzMzIwNTY0NjkwMzcxOTgyNDIxNTQyMzQzMTMzNTMxOTcxMTk4NTA4NDk5MjYyNzUxNDMyODg0NzgzMzc1MTAzODI0OTE3NzEwODAxOTE3OTc1OTM2OTg4OTIwMzYyMzA5NjE4NTgxMzY0ODE5NTA5ODYxNTE3NjI2ODc3OTUzMzMzMTkzMDExMjA2NDA5NDQ1MzA0MzEwMjUzMTU5OTE1NzYxOTI1MDY1MTg3Mjk1OTg1MzU0NDY1NjY5MTMxNjgwMzc5MzY0ODk0Mzc3NTYxODcyMjcxNDY5Mjk1NzY4MTc4NjQ2NDMxODI3NjI1MTQ3Mzk4MDg1ODI3NTUzMDAzNjIyMDM1ODM1MDg2NzE1NjgyMDA5MzgyNjgxNDc3NDc4ODQ0MDEyNDQ5NTE2NjYwODMwNDMwODQ5ODMxNjAxNDk3MTk3MjczMTIzNjg1NTE0NDMwMjY5OTkxMzMzNDI1Nzk0NjAwMzc3Mzk3NDMwMjg0MjIyMjQ1OTgyMjI1NTE3MjQ4NzA5NTczMzEwNjM5NzQyNzc2NjMyMzM3MDM0Nzk4NDY3MTAwNDczNDUxNTMzMTg1NDg5NDU0NjUyNTgwMjcxNjgyMDQzOTc4MDY4NDc4MjM1MjM5NjMzMTk0MzE4NDcxNDM2MjMwOTg1NTQ1MzAyMDQwNiIsInhyX2NhcCI6W1siZGF0ZW9mYmlydGgiLCIyNjMyMjI0MDEyMDg0NjM3ODA0NjA4MDE5MTM2MzIyNDkzMTE1MzA1MTA2ODQ1MTA0NTE4MDE4MjY2MjU1NjMyNDM0MTMzODY0MTIwNzUxNDkyMDAyMTI4MzU3NTcwMTY4MjE0OTU3OTgzNTQ3Mjk0NTczOTExMzk3MTQwNDAxNDk4MzQ0NzE0NTA5MjEyNDA2NTYzNzgyOTc4ODUzNDgzMTM4NTA1NTA3OTcxNTY3MTgyOTQ1ODQ5ODI3MDU0Nzg4NjA3ODgwOTU5NDE1MTYwMjU0OTE2MDExODkyNTIzNjUzODA1NTk2MDQ3NjA5Mjg3ODA4ODg2MzcwOTM5NDA3Njc5NTE3MjUzOTUxOTUzNDgxNDkzOTMzNDI3NTA5OTUwNTY5NjUwMTc4MjQwNTg0ODI4NzgzOTAxOTA0MjI3MzE1OTEyNzQ0NDYyODc3NDI3Njg3MDEzMDkyNDY1NTc5NzY0OTk2MTc2NTU1NDg1Nzc2Njc0NTcxMjcwNjU4NjAyMTk3OTExNTYzMjY0MzEyNTgzNzMyNTE3NjI3MjY3MzQ1MzEwODcwMjk2NTk4NTEyMDc0NzczNDQyMTExNjY4NjM0MzYzNjkzMDkxNzQ2MjE1MDQwODg1NTcyMzAzMDU4MDUwNzc3Nzg4Mzc5NDY3MzMyNDUwOTY5NTg1NDE2NzU2NzA2MzcxMzMyOTk5MDg1NjM5MDU4Nzk4ODE4MjkwNzEyNDk2NDk0NTY1MzgxMjI0NzUyMTA2OTQyMTg2OTc3MzUwNzE2NjI0MTYxMjIwNjExMDY3MzcxNTM0NjYyNTc0MzY1NTM0MzEzNjAwMTQyODk2MDkwMDQ3MTI5NjQ3OTEzOTgzMjk0Mjc2MDI2OTA2MTA3NzM4MDM2MjI2MTA4NzU0MTE3OTIwMTg2NDM1MDkwOTU2Il0sWyJuYW1lIiwiMzE2Nzc0MzUwOTgzMjI4Nzg1MDcxNDg1NjYzOTM0MTYyNjQ1MzE2NjQzMTc5Njg2NTg2MTU2MjgwMjg3Njg5Nzg0NTI1Mjg5MzY0NDg5NDMxNjEwMTc1MzUwMDgzMzUzMTg3MTIwMTE1MTU1NjUxOTYzMjcyODM4MjcyMTgyNTI2MzUyNjMyMzI5MDY2NDIxMjM3OTM3NTc4MjY4MjkwNzU4MDgwNjI0MjE3MDM0NDU5MDUyMjY2Njk5NjQzMTg2MjkyNjcxNDk1ODEwMDU4Mjg5MzA1MjI4OTQ2MTQ2MTkzMDAzNDk0NDgwNzY0NDY5Nzc1NzM5Njg0NzcxNjMyNDIzNTM5MjE1MzIxMjc0NTQ4NDU5NzE4NTM3NTMzMzE0MjYwMjcwNzE0MTkzNjc4MTEzMDg5MDUxODI1MjA2MTAyMjQ0MTc3NzAwNDk3MTYxMDM2NjgwNDYzMDUxNDcxNDk3MTgzNzc3Nzc5Nzc2Nzc0MTUzNzQ1NjEwNzc3NzgyMDYyODA3NjY5MjE2ODA2NDgxNzAzNTU5NDk5MTAyNTc5ODAwNjgxNTQxMjg3MTk0MTAwNzg4MDMxNDE3Nzg5MzQyNjk2NTQyNTA3NjE5MTgzNDIyMTc0OTk5NzQ2ODM3NjY4NTg0Mzk0NzQxNzI5MDY2MjgwNjYyMzAyMDI3NTczMDgwMDY5NTE5MDgxOTA5OTA3MzQzODAxNzg3MjgyMTEyODY2NzkwNTI2MDIwMjk4NjM2ODY3Njg2NTE5MjQ0NTg2MTg1NzMxMTE0ODk1ODU3NjkzNTQxMTIwOTc2NTQ5MzYwNDE1MTkxMjI4ODA4MzE5NTcxOTU4NTkxNDc4NDYwNzMwMTg2NDQ4MTU3NjU3OTkyOTI4NTM2MjgxODU2NjAzNjU5NjM3OTE2NzE0OTk0NTU0NSJdLFsiYWdlIiwiMTY5MjgwMDQ2OTQyNDI3NDY1ODE5OTE3MzEyNTkzMzEyMzkyNDYzMDQxODc5MzIwMjM0NDU4NjQ4Mzg2MzI4MTE5MDQ5OTIyMzc1NjkxNzI0ODYzMDM3NDMyODU4OTQ4MTIyNzI2ODQ5NDU5NDcxODA2NTU4NzYyMzU4MTgzMzU3OTYwNDk3NTMyNjUzNzE4ODE0NzQ1NzY2ODc3OTI2NzU2NTQ3NjQwMjUzMTczMDYzMjY0Mzc0OTAzNTgwNjEwNDMxMTM2NTA2NjQ1NjE5NzYzNTE1Nzc3MTkyNjU4ODk0OTc1MDMyODAzNzM0MTE5MjM5NjcxMjgwOTQyOTkxMTg2MjYyNjYzNTM5NDU3ODc1NjY1NDcwMTAxMTEzOTUyOTY2MDQyMzU4NDQ4ODE1NTk5MDgzNTU4NTIyMDQ1OTI3NDI0NjI5Njk4MTgzMTUzNzUyMDA4MzM5NTI1NTYxMDI0ODg2MzUzOTc3NzA1ODE5Mjc1MzQzOTg3MzMzODMxNjU0NzA4ODI3NDI0NzMzNzcyNjI3MTA2OTgxNjE5NDY0MzUwNDU3NzE4NzM2MDA0NjEyNzQ0OTAyNDA5NjA0Njk4NzkzNzI0MTc1MDA4OTUzMDMyMDgxMTQ2OTE3MTM4ODc4NzQ4MDM0Mzg1NDQxNTIxMTU5ODM2NDIwMDEzNTQ1NTQyMDk2NDIwODA1MDYxMzI5MTkwNzczNzIzMDYxMjE2NDIzNjczNTM1MzU1OTc5MzY1Njg0MzM2NjEyOTU2NjkxNDA4NDQ5MjE4MjcwOTYyODUyMzQwMTQ2MDAwMDYzNzA3NzU5MzUxODM0MTQ2MDI4MTYyMTEyMzU4MzAzNDQ1OTcwMTg3MTk3OTQ5MDcwNzE4NzQ4OTI4NjM5MDkyMzY1MjExMjgyODY0NDE3OTcwOTg5OCJdLFsibWFzdGVyX3NlY3JldCIsIjk4NjY2MDAzNjA2Njc3MjkxODM1MjEwMzA1NDczNTA3NjU0NTM1OTgxNDYxODkyNjY5NjI5OTE1MzQ5NjU3NjY5MTI5NzAxNTUwNzUyNTU2NjMxMzU4MzU3NjEzNjg3OTgyNTQzNTcyNTc5ODEzOTgxMzI4MDM4NzY5OTcxMzAwNTE0NDI2NzAxNTM2ODE1ODI3MDgzNDY5MzEzOTQ0ODAzMzIzMDUzMzUyNDkxMTIwMDUwNzkyNzA4NTQzMTE2NTM1NjA2NTQyNDY3OTcxNTUxODA4MzQyOTk1OTM4NzQ2NDQ4NjMyNTY4NjU0NjA4MzI1NDk2NjM3Mzc5OTQ0NjA5MDU3Mjc2OTE0OTQxNzE4MzU2MTYzODYyMzI2MDc3MDUzNjIyMDI3OTE2MzIzNzAzMDE2MTc4NDQ3MDEwMTc1MjI1MTM0NjE3NTcxMTgzMjcyNzMwNjQxNzI3Mzk2MzM4ODk2NTUyNzM4NzUwMDA4MTQ3MDExNjkyNzIwNzI4NDY2MjcwNDQ2MDg4NjEyMDg2MDExMzg2NzQxOTMzODM4ODQ1NjkzMTM1NzcyODk0MDIwNTM4NTU3ODI1MjA3OTkxNDAwODIyNjg4OTgwNTg4MjgzMTY0MzAxNTY1NjAyNDAzNTI4MDE2MTczNTk5MTM4NzI5ODEyOTE2MTYxNDEwNjgyODU4MzU4MDE3ODI1ODUyMzY2OTgzMDQzMzM4ODY2MzI3MzM3MTE0NzcyMzM5NTUyNTYzNzU0NzQ0NTA5MTYzNzYzNjAyNTI0NjgxNTUyNjIyOTYwNjM4MzE2OTc0NjI4MjQyNjQ5NjYyMjQyMTkzODMxOTUyMDE0MTAxNTA3Njk2MjIxMDU5NDE5MTcyNzMwNjE2NDE2NzEwNTMzMzc2NjI4MjQ4NzkxMTUwNjMxMDMxOCJdXX0sIm5vbmNlIjoiMTE4MTE3NTM4MDU1MjM2NjMxNjAwNjM1NyJ9", }, "mime-type": "application/json", }, ], }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "8-4e4f-41d9-94c4-f49351b811f1": Object { + "8-4e4f-41d9-94c4-f49351b811f1": { "id": "8-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "messageId": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "messageName": "request-credential", @@ -3033,35 +3033,35 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "8-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "1284ae78-f3d3-4fed-a5ff-0e2aba968c3c", "@type": "https://didcomm.org/issue-credential/1.0/request-credential", - "requests~attach": Array [ - Object { + "requests~attach": [ + { "@id": "libindy-cred-request-0", - "data": Object { + "data": { "base64": "eyJwcm92ZXJfZGlkIjoiUVZveGd3d25WUGtBQlRMVmNtQ013TCIsImNyZWRfZGVmX2lkIjoiVEwxRWFQRkNaOFNpNWFVcnFTY0JEdDozOkNMOjY4MTpkZWZhdWx0IiwiYmxpbmRlZF9tcyI6eyJ1IjoiMTkwNzM1MzQyMjkwNjk4Mzk3NzgyNTUyMTM2NTA3NzAxMDA4NzYwMzcxMjg3NTQ1MzI0NDM2NDIyMTMwNzQ3MDI3NTk4NDM2NTM5Mzc0NzM0MjgxOTk4MDY5Mjg1OTg4MzAzMDE1MzAzMTYxNjExMTEzMTY2MzQxMzkyOTkzMTk0ODUxNzM5Njk4NzcxNDYzNzMzMDA4MjUxMzQ5NjM4OTkzMDE5NTk5MTY1NDUyOTk4OTA4OTY4MDE5NTUxODM2NDg1NzYxMzMxNTgxNTY0MzgxNTkxNjMwOTcyNTc5NTkyODMyNDk3MDI0NTMyMjQxNzk4MDMzMjI1NDg4NjA5NjEwNjEzNTU1NDMxODc3NDQzODk2ODUyMzA4NjIxNDA1NjI5NjA5MTg5Nzg2MjYzMTcyODU0MjA1MTI3ODMxNjc5NzM5NTkxODQyMTYwOTAyNjczMDE4Mzc0MzE5NjUyODA3Njc2MDQzNzc0ODcxMjQzMzkzNTIwNTkwODE5MDgxOTI4NzY3MjU5NDQ2OTIxNTM2MjU3MjQ2NjIxMjgzNjc2NDM1MDIwNzUwODI4NDI2NTM2MTU2ODA5NDgwMTU3MTQ0NDkxNTY2MTM0ODYzNjU4Mjg5ODIyMTE1NjI4MzMxNjMxMTQ3ODM1NzQ4MjAxMzkyNjY4NzQyMTQ5NTI1OTQ0OTc1NzY3NTYwMTQyNzQ5MTU3MTY2NzE0MDY0OTM2OTQ1MzEwMzEwMzU1NjgwNTcyNDgzNDgyNTYyMzk5Nzc0OTY5NTYwMTA1Njk2MzczMDU4MDMzODgyMTAwOTY2ODUwMTk5MjEzMzAiLCJ1ciI6bnVsbCwiaGlkZGVuX2F0dHJpYnV0ZXMiOlsibWFzdGVyX3NlY3JldCJdLCJjb21taXR0ZWRfYXR0cmlidXRlcyI6e319LCJibGluZGVkX21zX2NvcnJlY3RuZXNzX3Byb29mIjp7ImMiOiI1MDg3Mzk4NDExNzQ3Mzc5NjY5MDkyNzU5ODQ5OTEwMDI2OTYzMTk2NjExNjc5MzU5NDYwNDMxMjYyMDE4NzgyNzY4NTM2NTUzNzUwMiIsInZfZGFzaF9jYXAiOiIxODU0NzEwMDA5NDM4NTg5MTc4MzkzMjgzMDk1MzM5NDUzMDQ3OTkwOTYyMjE2NzEyNzk2ODkyMzcyMzA5NjU5NTU3MDY2MzQxMTMxOTY0Mjg5NjA3MTI5MDg4MjMxMDY0NTk5ODY4NDg4MTIzNDMwMzY5OTkxMjI1OTMxMTIyMjY4NjU5MDEwMDA0NDA1OTIzNTcyMzgyMzQzODczNjkxODg3NDQzMjQ5MTcwNTQwNDk4Nzk5MTkxOTIzMjc4NDU4MzcyMzk2NzIyMjM5NDE1NjY3ODIxMDQ4ODA0NDk1NDQ5ODQ3MjcwOTg1MDcwNzY3NjU2NDU4NDM0MTYzNTI3NDAyMzA1NTg5MTg4NzcyNDg4NzE1NjcwOTgxNTc0NzQxMTI1NzYxOTIxNTE3NzYyNzg1Nzk4MjU5MTQ0OTYzMDQyMjg0NzUwMDE5MjAwMjQ4NjgxNjkxOTE5Njg3MDA1MDA4MDUzMjYwODY5NzEyNTEwNzIwNDg5NDAwMjM0NDU3Njk2MDk4NjI0Nzk1MDUwMzQ2NzM2Mjg1MDE3MTU5Mjk1OTA0NTU1NTk0MzMxNzI4MTQ5MzgyODE5NTI2NTc3MDg3NjA0ODMwNDk3MTE0Mjc3MTkyMDU3ODk1MzYxNzI5NTE3NTgxNzg5ODEyMDY3MjcxNTU5MTMyNTI1MzEzNTc0MDEwNTM3NDMxMDY2NzUwNzAzMDgxMTQxNDIyODg5MzUxOTY0MDUyMDU0NjY0MTA0ODE0MzY1Njg3NTcxNjU5MTk3ODQxMjU5NjE3OTI4MDg4NjM0ODY1NTI2MjI4NTkyNTI2NTgwODgzMzIzNjEwNTc5NzU4ODgzMjgwNDcyNTA0OTQ0MjM2ODY5MTYyNzM3NzUwNTI0NTIyMjE5NTM4NjE4OTk2ODQzODU3MzU3MjUxODI5NTIyODgxOTA0NTAwMDU2MjU1NTMwNDgyIiwibV9jYXBzIjp7Im1hc3Rlcl9zZWNyZXQiOiIxMjM5OTQ3ODE4MDA1MTQ3MjQ5MjcyODIxNDI2OTgwNjQ2NTIwMjAwMTc0MzUwMzkzMzQ0NzU1NTU0NDAxNjg1NzQ2NzExMzkzMjEzMjA3NjI3ODI5MTczMjc2OTA2MDg4MDAxMDIxMTMxMzY4NzY4MjI0ODgxNTExMjEwNjY0NzA5OTAxOTQwODA0MzA0OTc1NTUyMzExNzAyMjU3MTYwOTAxNTE0MzIxNzYyNzM0ODUwMSJ9LCJyX2NhcHMiOnt9fSwibm9uY2UiOiIzNzM5ODQyNzAxNTA3ODY4NjQ0MzMxNjMifQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "sender", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "9-4e4f-41d9-94c4-f49351b811f1": Object { + "9-4e4f-41d9-94c4-f49351b811f1": { "id": "9-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "messageId": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "messageName": "issue-credential", @@ -3073,79 +3073,79 @@ Object { "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, "type": "DidCommMessageRecord", - "value": Object { - "_tags": Object {}, + "value": { + "_tags": {}, "associatedRecordId": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", "createdAt": "2022-01-21T22:50:20.522Z", "id": "9-4e4f-41d9-94c4-f49351b811f1", - "message": Object { + "message": { "@id": "d14cf505-4903-4dd9-95c2-a7dbc1c048b6", "@type": "https://didcomm.org/issue-credential/1.0/issue-credential", - "credentials~attach": Array [ - Object { + "credentials~attach": [ + { "@id": "libindy-cred-0", - "data": Object { + "data": { "base64": "eyJzY2hlbWFfaWQiOiJUTDFFYVBGQ1o4U2k1YVVycVNjQkR0OjI6c2NoZW1hLTgwZjdlZWM1LThlNWEtNDNjYS1hZDRkLTMyNzRmYjkzNjFiODoxLjAiLCJjcmVkX2RlZl9pZCI6IlRMMUVhUEZDWjhTaTVhVXJxU2NCRHQ6MzpDTDo2ODE6ZGVmYXVsdCIsInJldl9yZWdfaWQiOm51bGwsInZhbHVlcyI6eyJuYW1lIjp7InJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImRhdGVPZkJpcnRoIjp7InJhdyI6IjIwMjAtMDEtMDEiLCJlbmNvZGVkIjoiNDEwNjEyOTM3ODA0NjIwNzU1MTQyMDgyMjM4OTc1OTA0MDc4MDA3NDg1NDMwMTQ5OTE1ODg1MjI3MjExOTM4ODY4OTgxMTk3NTM2MjQifSwiYWdlIjp7InJhdyI6IjI1IiwiZW5jb2RlZCI6IjI1In19LCJzaWduYXR1cmUiOnsicF9jcmVkZW50aWFsIjp7Im1fMiI6Ijg5NjQzNDI5NjIzMzI5NjQ1MDM2NDU5MjU4NzU2Nzc5MDY3ODczOTc4MDg1ODc3MDM3MzU1NzMxMjk1MzA0NzY5ODAxNDM5MjI2MDI4IiwiYSI6IjUzOTQ3MDU1MTU0OTEyMTE2MzM0MzU2NTMyMjEyMTM5MjMxNjE2NTcwMzU3MjkwNzcyOTkxNjM4NTU1MjU2Mjc2NjgyODY5NDM2MTI5OTEzNzk2MzQwOTA1Nzk3Mzc2OTI5NDE0MTIwNzMwMjI5NjQzNjMwNTI2OTcyOTUxNDk1NjA5NDcwMDU0NzEzOTY1OTE5NTU3MzM3NDkwMjUyNTI1NTM5NzI4MjA0NTI2NTU0MDcyMDYyOTcxMTA0MTM3NTQ4NzEzMzIzNTUzMTYwOTEzODQ0MDk0MDczOTQ3MjM2OTQyNzgyODIzNDA2NDUyNzIzODY2NjMzMzc2MjI4Mzk2ODE5ODI0MTQ2MjgyNTI4NDIyOTIzMjM1NzEzNTI5MjQ3ODkxMTQzMDE4OTM3ODQ0ODM3NzQ2MDE4MTc5Mjk5ODI5ODQ1MTI4MTgxMDUxOTE4MjE0ODU5Mzg5MjIzOTEzNjUzMjE0MjIxMTk2NDI2OTA2NDM1NDYwNDQwNTgxNDkxNTg5MzMxMzIyMDU1MDU1NjE5NDY0OTEwNTc3OTcyODAyNDM1MDY3OTMxMDczOTI5OTgyOTQ2Njg4NDg5MTIwNTQ1MjA1MzQ0MjQ1NTIzNTExNDc5NjUzNjI5ODIxNTA4NTc3MjI2MzU5MjUyMDA5MjUyNTU2NDA5NTg0MDgwNDY5NDI4NzE1NDQ0MDkyOTA4NzAxNjMwMzE3NzI5MTE3NTYwMzg2NjAyNDA4OTE3Mzg2NjM4NzQ2MDY1NjU0NzQ2OTAxOTA1NjA4MjE5MzgzNzczNjg3NDcxODI3NjE2OTU5MDk3NDU4IiwiZSI6IjI1OTM0NDcyMzA1NTA2MjA1OTkwNzAyNTQ5MTQ4MDY5NzU3MTkzODI3Nzg4OTUxNTE1MjMwNjI0OTcyODU4MzEwNTY2NTgwMDcxMzMwNjc1OTE0OTk4MTY5MDU1OTE5Mzk4NzE0MzAxMjM2NzkxMzIwNjI5OTMyMzg5OTY5Njk0MjIxMzIzNTk1Njc0MjkzMDMwMzU1ODY2NDUxMDY0MDQ4Mzk4OTcyMjU0Nzk2ODY2NDA2OSIsInYiOiI4NzE4MTIwMTY2NjA3NTg4MjIxMDczMTE0NTgxNTcxMDA5NTEwNDUwMzkyNDk0MDM1MTg3MDk5MTQyNzA5NDcyMjYxMDAyMjg5MzI5MzcyMzk1MDUwMzgzOTA3ODUxODc1MjIxODU0NjI4ODc4OTcxNzQ0Njg3MDgxMDM4Mzk2Njc2MzgyMTI0NjEyNDY0NjQxMDQ4NDMxNDkwMTYzMDgwOTU0NTc3MjA3OTkxNjg3NzU0NjQ4MTYwNzM5MjQ2ODUyMjMxMjU2NTk0MTg4MTAxNDU2MjgzNjc3MTA4NTMwNjY1NDQwNTUwMDY0MjgxODI3NzUxNjA3NjM1ODE2MjczNDU3MTc4MzAyNjEyOTI5ODMyNDMzNDc3ODk0ODMzMDc2MDA5OTE4MTc1MzI4OTUzNjg1MjEwOTQ1ODg0MTQyMDg0Nzk4ODMzNzM2OTExNDcwMTkwOTYwMjI3MzAyMzI2NjQ2NjE2NjUwMjY4OTU3NDcyMTI3MTA2Mjk3NzQ0NDg3MDUzODY2NjI0NTk4Njg1MzA0MzA0OTMzNjAxNDczNjY4Njg1NDg5MzYyMzQ2NzE5ODA4MjgxNzYxNjc0ODQyMzE5NTY5Mzk1Nzk1NTQ5OTA4MjAyODI0MjgyOTc1MTI3MDA0MzM0NTkwNTYzMTI3NjU3NDM3MjQ2MDQ3OTUyOTk0OTIyODQ3MzcxMzY4NDM0OTE3MDM1Njc4ODA2NjM1OTQ0ODY4Njc5MDY1NDc5Njk1MDU5NzkyNzUyMzk5NDcwMzUzMDI3MjEyNTg2OTc1Mjk5MTk1NDcwNjY0NDMzMDIyNTQyODg4MzI4OTA0Mjg2NTIxMzM5Nzc2OTkxMDYzNzA1MTI2NjA4MDY4OTA0Mzg2MDc4NzA5NTE3NjU0OTE3MzI0NjExMzkzNTM4MDkyNTQ3NzQ0OTM2NTM1NDkwODcwNDU4NjQ3NjY2OTU3MjA5MDk4MDU2NzIwMjAzOTAxMjI3MjU2NDM5NTkwNTA0MzIwOTI3NTc1ODA2NjE0NzA4NTU3MjAxMTAxODczODc4NDg1NjM4MzQ2Nzk1NjE4NDQxOTQxMjQyODc5ODMyNjQ5ODEyIn0sInJfY3JlZGVudGlhbCI6bnVsbH0sInNpZ25hdHVyZV9jb3JyZWN0bmVzc19wcm9vZiI6eyJzZSI6IjIxOTkzMzcwNTI0MjIxNTM0MTM0MTc4MDM3MDIyMDEyMzE4NjQ2MDE4MTk0MDIwMjgxNzY4NTQ4OTUyNjM5ODg4MDE2NDQ1NTM2NTQ2MzczMzkwMDU5NjY1Nzg4OTc2MDE0OTUzMTU2MjA1MTA0ODU1NTM1NjEyODY5Mjg5NjgyOTQ1ODI4MDQxOTMxMzc1ODY4NjE4OTE0NjUwNTc5ODM2NzI0NDE0MDMxMjU3MjU2MzkxNjg2OTQ0NjQyMzg3NTIwNjExNDQ5ODM1NTgxNDMzMDMzOTQ4MTA4OTE0MzI2NzkyNDU5MjQ0Mzc0MTgyOTQ4MDQxODIzMTg3NjY3MjE2MDI5OTEwMTAzMTM1MjE4NjY5ODc5MDk5ODA0NTA0NjI1NTAzNDM2NTAxOTk5ODkwODIyNjcyMjYwNzc1NDIzMzIxNTQ0MDk0Mjk2NDI0Nzc2Njg2MDI1MzU2MjMwOTY0OTQyMzc3NTY0NDUwMDk4NTgyMzg2Nzg0ODc3OTQwNTI2ODg0MzgzOTcxOTE4OTE3ODIyOTkzMDIzMDU2NjU1NTg2NDI3MDAwMzQ2OTcwMTI1MjA2ODg0NTkyNzkwMDU4NTAyMzkxMzUwODIyNjg1NDYyMzY0MzE5NTMwOTI0MjM4Mzg2MjkwMDI5MTQ1ODAzNTgxODA2MDQ1MTYzNDMzNTQ2OTAwMzQzNDg0MTg2NTEyMjIzODYwODY3NTI3ODExOTkyOTQwMjMxMzgzOTM4MjI0NjEwMTk0NDc1NjczMDYyMDI3MDgwOTI5NDYzMjU0NjIxMjI1NTg3MDg0NTUyODEyMDgxMDE3IiwiYyI6Ijg2NzE3OTAzNTAxODI5MzU5NDk4MjE3NDU5NjE3NjgyNTM4NzIxNzQwMjE5MDM5MDUzNjQzNTE3NDU0Nzc2NTUzMzA3MzU1OTkxMjE5In0sInJldl9yZWciOm51bGwsIndpdG5lc3MiOm51bGx9", }, "mime-type": "application/json", }, ], - "~please_ack": Object {}, - "~thread": Object { + "~please_ack": {}, + "~thread": { "thid": "578e73da-c3be-43d4-949b-7aadfd5a6eae", }, }, - "metadata": Object {}, + "metadata": {}, "role": "receiver", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": Object { + "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7": { "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "tags": Object { + "tags": { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", - "credentialIds": Array [], + "credentialIds": [], "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "createdAt": "2022-03-21T22:50:20.740Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -3156,53 +3156,53 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": Object { + "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c": { "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", - "tags": Object { + "tags": { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialIds": Array [ + "credentialIds": [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", }, "type": "CredentialRecord", - "value": Object { + "value": { "autoAcceptCredential": "contentApproved", "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "createdAt": "2022-03-21T22:50:20.746Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", "credentialRecordType": "indy", }, ], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", "vr_prime": null, }, @@ -3220,26 +3220,26 @@ Object { `; exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the role in the mediation record: allMediator 1`] = ` -Object { - "0b47db94-c0fa-4476-87cf-a5f664440412": Object { +{ + "0b47db94-c0fa-4476-87cf-a5f664440412": { "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "tags": Object { + "tags": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", "createdAt": "2022-03-21T22:50:17.157Z", "endpoint": "rxjs:alice", "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3247,25 +3247,25 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { + "7f14c1ec-514c-49b2-a00b-04af7e600060": { "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "tags": Object { + "tags": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", "createdAt": "2022-03-21T22:50:17.126Z", "endpoint": "rxjs:alice", "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3273,59 +3273,59 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "802ef124-36b7-490f-b152-e9d090ddf073": Object { + "802ef124-36b7-490f-b152-e9d090ddf073": { "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "tags": Object { + "tags": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", "createdAt": "2022-03-21T22:50:17.161Z", "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { + "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": { "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "tags": Object { + "tags": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", "createdAt": "2022-03-21T22:50:17.132Z", "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", "updatedAt": "2022-01-21T22:50:20.522Z", @@ -3335,26 +3335,26 @@ Object { `; exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the role in the mediation record: allRecipient 1`] = ` -Object { - "0b47db94-c0fa-4476-87cf-a5f664440412": Object { +{ + "0b47db94-c0fa-4476-87cf-a5f664440412": { "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "tags": Object { + "tags": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", - "recipientKeys": Array [], + "recipientKeys": [], "role": "RECIPIENT", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", "createdAt": "2022-03-21T22:50:17.157Z", "endpoint": "rxjs:alice", "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "RECIPIENT", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3362,25 +3362,25 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { + "7f14c1ec-514c-49b2-a00b-04af7e600060": { "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "tags": Object { + "tags": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", - "recipientKeys": Array [], + "recipientKeys": [], "role": "RECIPIENT", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", "createdAt": "2022-03-21T22:50:17.126Z", "endpoint": "rxjs:alice", "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "RECIPIENT", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3388,59 +3388,59 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "802ef124-36b7-490f-b152-e9d090ddf073": Object { + "802ef124-36b7-490f-b152-e9d090ddf073": { "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "tags": Object { + "tags": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", - "recipientKeys": Array [], + "recipientKeys": [], "role": "RECIPIENT", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", "createdAt": "2022-03-21T22:50:17.161Z", "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "RECIPIENT", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { + "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": { "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "tags": Object { + "tags": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", - "recipientKeys": Array [], + "recipientKeys": [], "role": "RECIPIENT", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", "createdAt": "2022-03-21T22:50:17.132Z", "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "RECIPIENT", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", "updatedAt": "2022-01-21T22:50:20.522Z", @@ -3450,26 +3450,26 @@ Object { `; exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the role in the mediation record: doNotChange 1`] = ` -Object { - "0b47db94-c0fa-4476-87cf-a5f664440412": Object { +{ + "0b47db94-c0fa-4476-87cf-a5f664440412": { "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "tags": Object { + "tags": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", "createdAt": "2022-03-21T22:50:17.157Z", "endpoint": "rxjs:alice", "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3477,25 +3477,25 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { + "7f14c1ec-514c-49b2-a00b-04af7e600060": { "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "tags": Object { + "tags": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", "createdAt": "2022-03-21T22:50:17.126Z", "endpoint": "rxjs:alice", "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3503,59 +3503,59 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "802ef124-36b7-490f-b152-e9d090ddf073": Object { + "802ef124-36b7-490f-b152-e9d090ddf073": { "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "tags": Object { + "tags": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", "createdAt": "2022-03-21T22:50:17.161Z", "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { + "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": { "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "tags": Object { + "tags": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", "createdAt": "2022-03-21T22:50:17.132Z", "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", "updatedAt": "2022-01-21T22:50:20.522Z", @@ -3565,26 +3565,26 @@ Object { `; exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the role in the mediation record: recipientIfEndpoint 1`] = ` -Object { - "0b47db94-c0fa-4476-87cf-a5f664440412": Object { +{ + "0b47db94-c0fa-4476-87cf-a5f664440412": { "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "tags": Object { + "tags": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", - "recipientKeys": Array [], + "recipientKeys": [], "role": "RECIPIENT", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "88e2093e-97b9-4665-aff0-ffdcb4afee60", "createdAt": "2022-03-21T22:50:17.157Z", "endpoint": "rxjs:alice", "id": "0b47db94-c0fa-4476-87cf-a5f664440412", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "RECIPIENT", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3592,25 +3592,25 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7f14c1ec-514c-49b2-a00b-04af7e600060": Object { + "7f14c1ec-514c-49b2-a00b-04af7e600060": { "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "tags": Object { + "tags": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", - "recipientKeys": Array [], + "recipientKeys": [], "role": "RECIPIENT", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "85a78484-105d-4844-8c01-9f9877362708", "createdAt": "2022-03-21T22:50:17.126Z", "endpoint": "rxjs:alice", "id": "7f14c1ec-514c-49b2-a00b-04af7e600060", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "RECIPIENT", - "routingKeys": Array [ + "routingKeys": [ "D86mPByntjjYMuoaVwxotLY8RMwyRSkRkUL3XPrpwcDu", ], "state": "granted", @@ -3618,59 +3618,59 @@ Object { "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "802ef124-36b7-490f-b152-e9d090ddf073": Object { + "802ef124-36b7-490f-b152-e9d090ddf073": { "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "tags": Object { + "tags": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "ca5b148a-250f-46b0-8537-ae88014d8bd7", "createdAt": "2022-03-21T22:50:17.161Z", "id": "802ef124-36b7-490f-b152-e9d090ddf073", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "e9aeea8f-2c7a-4fd0-9353-f8b5b76094e7", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-01-21T22:50:20.522Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.2", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": Object { + "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd": { "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "tags": Object { + "tags": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", - "recipientKeys": Array [], + "recipientKeys": [], "role": "MEDIATOR", "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", }, "type": "MediationRecord", - "value": Object { + "value": { "connectionId": "dbb3367e-55aa-4c03-b10a-d1fc34392bea", "createdAt": "2022-03-21T22:50:17.132Z", "id": "a29b39fb-f030-41ac-b6e1-ed7f3f6a05cd", - "metadata": Object {}, - "recipientKeys": Array [], + "metadata": {}, + "recipientKeys": [], "role": "MEDIATOR", - "routingKeys": Array [], + "routingKeys": [], "state": "granted", "threadId": "a401880b-8129-4ed9-bcaa-57d0e38026cd", "updatedAt": "2022-01-21T22:50:20.522Z", diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap index daefd3a533..c4767da0c1 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.2.test.ts.snap @@ -1,43 +1,43 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update proof records and create didcomm records 1`] = ` -Object { - "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": Object { +{ + "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": { "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.261Z", "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "requestMessage": Object { + "requestMessage": { "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", }, "mime-type": "application/json", @@ -48,64 +48,64 @@ Object { "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "72c96cd1-1f26-4bf3-8a00-5c00926859a8": Object { + "72c96cd1-1f26-4bf3-8a00-5c00926859a8": { "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.208Z", "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", "isVerified": true, - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "4185f336-f307-4022-a27d-78d1271586f6", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "proposalMessage": Object { + "proposalMessage": { "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", - "presentation_proposal": Object { + "presentation_proposal": { "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", - "attributes": Array [ - Object { + "attributes": [ + { "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", "name": "name", "value": "Alice", }, ], - "predicates": Array [], + "predicates": [], }, }, - "requestMessage": Object { + "requestMessage": { "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, @@ -113,55 +113,55 @@ Object { "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.4", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "ea840186-3c77-45f4-a2e6-349811ad8994": Object { + "ea840186-3c77-45f4-a2e6-349811ad8994": { "id": "ea840186-3c77-45f4-a2e6-349811ad8994", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.261Z", "id": "ea840186-3c77-45f4-a2e6-349811ad8994", "isVerified": true, - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "requestMessage": Object { + "requestMessage": { "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", }, "mime-type": "application/json", @@ -172,63 +172,63 @@ Object { "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "ec02ba64-63e3-46bc-b2a4-9d549d642d30": Object { + "ec02ba64-63e3-46bc-b2a4-9d549d642d30": { "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.208Z", "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "4185f336-f307-4022-a27d-78d1271586f6", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "proposalMessage": Object { + "proposalMessage": { "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", - "presentation_proposal": Object { + "presentation_proposal": { "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", - "attributes": Array [ - Object { + "attributes": [ + { "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", "name": "name", "value": "Alice", }, ], - "predicates": Array [], + "predicates": [], }, }, - "requestMessage": Object { + "requestMessage": { "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, @@ -240,36 +240,36 @@ Object { `; exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the did records 1`] = ` -Object { - "1-4e4f-41d9-94c4-f49351b811f1": Object { +{ + "1-4e4f-41d9-94c4-f49351b811f1": { "id": "1-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkpg6nDPuYdLMuzNEjaa2Xprh3J2MC1WtNakWUEFqC4wvU", ], "role": "created", }, "createdAt": "2022-12-27T13:51:21.344Z", "did": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#57a05508-1d1c-474c-8c68-1afcf3188720", "publicKeyBase58": "BDqjd9f7HnsSssQ2u14gym93UT5Lbde1tjbYPysB9j96", @@ -277,22 +277,22 @@ Object { }, ], "id": "did:peer:1zQmWxKCTkKYQvzdUshMWy7b8vy3Y7uLtB9hp6BbQhKEtQCd", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#59d2ce6f-c9fc-49c6-a8f6-eab14820b028", "publicKeyBase58": "avivQP6GvWj6cBbxbXSSZnZTA4tGsvQ5DB9FXm45tZt", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#57a05508-1d1c-474c-8c68-1afcf3188720", ], - "routingKeys": Array [ + "routingKeys": [ "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop", ], "serviceEndpoint": "ws://ssi.mediator.com", @@ -301,40 +301,40 @@ Object { ], }, "id": "1-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "created", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "2-4e4f-41d9-94c4-f49351b811f1": Object { + "2-4e4f-41d9-94c4-f49351b811f1": { "id": "2-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkuLL6bQykGpposTrQhfYceQRR4JDzvdm133xpwb7Dv1oz", ], "role": "received", }, "createdAt": "2022-12-27T13:51:51.414Z", "did": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#6173438e-09a5-4b1e-895a-0563f5a169b7", "publicKeyBase58": "Ft541AjJwHLLky1i26amoJsREix9WkWeM33u7K9Czo2c", @@ -342,62 +342,62 @@ Object { }, ], "id": "did:peer:1zQmavZzrLPRYRGd5CgyKV4GBZG43eGzjic9FHXc7jLHY14W", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#a0448ee3-093a-4b16-bc59-8bf8559d60a5", "publicKeyBase58": "JCfZJ72mtgGE9xuekJKV6yoAGzLgQCNkdHKkct8uaNKb", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#6173438e-09a5-4b1e-895a-0563f5a169b7", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://ssi.verifier.com", "type": "did-communication", }, ], }, "id": "2-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "received", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "3-4e4f-41d9-94c4-f49351b811f1": Object { + "3-4e4f-41d9-94c4-f49351b811f1": { "id": "3-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkv6Kd744JcBL5P9gi5Ddtehxn1gSMTgM9kbTdfQ9soB8G", ], "role": "created", }, "createdAt": "2022-12-27T13:50:32.815Z", "did": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", "publicKeyBase58": "Ge4aWoosGdqcGer1Peg3ocQnC7AW3o6o4aYhq8BrsxLt", @@ -405,62 +405,62 @@ Object { }, ], "id": "did:peer:1zQmZqQYzwqsYjj7z8kixxDXf9nux8TAsqj2izSpX1oCrd7q", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#c30ea91b-5f49-461f-b0bd-046f947ef668", "publicKeyBase58": "HKBdBGRK8uxgCwge2QHPBzVuuayEQFhC2LM3g1fzMFGE", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#d9d97b3e-5615-4e60-9366-a8ab1ec1a84d", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "didcomm:transport/queue", "type": "did-communication", }, ], }, "id": "3-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "created", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "4-4e4f-41d9-94c4-f49351b811f1": Object { + "4-4e4f-41d9-94c4-f49351b811f1": { "id": "4-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkvcuHjVcNRBM78E3xWy7MnVqQV9hAvTawBsPmg3bR1DaE", ], "role": "created", }, "createdAt": "2022-12-27T13:51:50.193Z", "did": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#22219a28-b52a-4024-bc0f-62d3969131fd", "publicKeyBase58": "HAeF9FMw5dre1jDFqQ9WwQHQfaRKWaLaVrUqqmdQ5znr", @@ -468,22 +468,22 @@ Object { }, ], "id": "did:peer:1zQmRnGYmYBdSinH2953ZgfHWEpqp5W6kJmmYBgRaCs4Yudx", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#0f9535d1-9c9c-4491-be1e-1628f365b513", "publicKeyBase58": "FztF8HCahTeL9gYHoHnDFo6HruwnKB19ZtbHFbLndAmE", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#22219a28-b52a-4024-bc0f-62d3969131fd", ], - "routingKeys": Array [ + "routingKeys": [ "did:key:z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop#z6MkqhwtVT5V6kwsNkErPNgSWuRGd4QLFQ324FMfw5oWGHop", ], "serviceEndpoint": "ws://ssi.mediator.com", @@ -492,40 +492,40 @@ Object { ], }, "id": "4-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "created", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "5-4e4f-41d9-94c4-f49351b811f1": Object { + "5-4e4f-41d9-94c4-f49351b811f1": { "id": "5-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkp1wyxzqoUrZ3TvnerfZBq48o1XLDSPH8ibFALkNABSgh", ], "role": "received", }, "createdAt": "2022-12-27T13:50:44.957Z", "did": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", "publicKeyBase58": "AZgwNkbN9K4aMRwxB6bLyxaoBx4N2W2n2aLEWUQ9GDuK", @@ -533,62 +533,62 @@ Object { }, ], "id": "did:peer:1zQmb9oo8fn8AZ7rWh1DAfTbM3mnPp9Hq2bJFdHiqjnZX2Ce", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#bf888dc5-0f54-4e71-9058-c43bebfc7d01", "publicKeyBase58": "3Q8wpgxdCVdJfznYERZR1r9eVnCy7oxpjzGVucDLF2tG", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#075b7e8b-a7bd-41e1-9b01-044f1ccab1a6", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "ws://ssi.mediator.com", "type": "did-communication", }, ], }, "id": "5-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "received", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "6-4e4f-41d9-94c4-f49351b811f1": Object { + "6-4e4f-41d9-94c4-f49351b811f1": { "id": "6-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MkrQzQqPtNjJHGLGjWC7H3mjMSNA36bCBTYJeoR44JoFvH", ], "role": "received", }, "createdAt": "2022-12-27T13:50:34.057Z", "did": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", "publicKeyBase58": "CxjNF9dwPknoDmtoWYKCvdoSYamFBJw6rHjsan6Ht38u", @@ -596,62 +596,62 @@ Object { }, ], "id": "did:peer:1zQmSzC6uhWYcxtkr2fBepr66kjvSHuRsSfbmei7nrGj7HfN", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#c0917f9f-1102-4f13-800c-ff7b522999eb", "publicKeyBase58": "Eso3A6AmL5qiWr9syqHx8NgdQ8EMkYcitmrW5G7bX9uU", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#b6d349fb-93fb-4298-b57a-3f2fecffccd8", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "ws://ssi.issuer.com", "type": "did-communication", }, ], }, "id": "6-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "received", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "7-4e4f-41d9-94c4-f49351b811f1": Object { + "7-4e4f-41d9-94c4-f49351b811f1": { "id": "7-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM", ], "role": "received", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6Mkt4ohp4uT74HdENeGVGAyVFTerCzzuDXP8kpU3yo6SqqM", ], "role": "received", }, "createdAt": "2022-12-27T13:51:22.817Z", "did": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#5ea98568-dfcd-4614-9495-ba95ec2665d3", "publicKeyBase58": "EcYfDpf1mWoA7soZohD8e9uf2dj9VLH2SjuYDhq5Xd3y", @@ -659,62 +659,62 @@ Object { }, ], "id": "did:peer:1zQmPaCELen1JWMWmhVZS16nDmrAC9yGKQcJCcBs5spXakA3", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#506e5ead-ddbc-44ef-848b-6593a692a916", "publicKeyBase58": "EeQHhb6CqWGrQR9PfWpS1L8CedsbK2mZfPdaxaHN4s8b", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#5ea98568-dfcd-4614-9495-ba95ec2665d3", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "http://ssi.verifier.com", "type": "did-communication", }, ], }, "id": "7-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "received", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "8-4e4f-41d9-94c4-f49351b811f1": Object { + "8-4e4f-41d9-94c4-f49351b811f1": { "id": "8-4e4f-41d9-94c4-f49351b811f1", - "tags": Object { + "tags": { "did": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", "legacyUnqualifiedDid": undefined, "method": "peer", "methodSpecificIdentifier": "1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma", ], "role": "created", }, "type": "DidRecord", - "value": Object { - "_tags": Object { + "value": { + "_tags": { "method": "peer", - "recipientKeyFingerprints": Array [ + "recipientKeyFingerprints": [ "z6MktdgEKUiH5kt5t2dKAH14WzFYhzj3JFobUf2PFFQAg2ma", ], "role": "created", }, "createdAt": "2022-12-27T13:50:43.937Z", "did": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "didDocument": Object { - "@context": Array [ + "didDocument": { + "@context": [ "https://w3id.org/did/v1", ], - "authentication": Array [ - Object { + "authentication": [ + { "controller": "#id", "id": "#12b8b7d4-87b9-4638-a929-f98df2f1f566", "publicKeyBase58": "FBRBjETqkDPcmXncUi3DfthYtRTBtNZEne7TQyS9kozC", @@ -722,41 +722,41 @@ Object { }, ], "id": "did:peer:1zQmUo1HoiciJS628w4SHweg4Pzs4bZLM4KLZgroSmUwBw7S", - "keyAgreement": Array [ - Object { + "keyAgreement": [ + { "controller": "#id", "id": "#77b9cf84-2441-419f-b295-945d06e29edc", "publicKeyBase58": "EgsQArrmCUru9MxR1RNNiomnMFz6E3ia2GfjVvoCjAWY", "type": "X25519KeyAgreementKey2019", }, ], - "service": Array [ - Object { + "service": [ + { "id": "#inline-0", "priority": 0, - "recipientKeys": Array [ + "recipientKeys": [ "#12b8b7d4-87b9-4638-a929-f98df2f1f566", ], - "routingKeys": Array [], + "routingKeys": [], "serviceEndpoint": "didcomm:transport/queue", "type": "did-communication", }, ], }, "id": "8-4e4f-41d9-94c4-f49351b811f1", - "metadata": Object {}, + "metadata": {}, "role": "created", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.4", "updatedAt": "2022-01-21T22:50:20.522Z", }, @@ -765,43 +765,43 @@ Object { `; exports[`UpdateAssistant | v0.2 - v0.3.1 should correctly update the proofs records and create didcomm records with auto update 1`] = ` -Object { - "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": Object { +{ + "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e": { "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.261Z", "id": "3d5d7ad4-f0aa-4b1b-8c2c-780ee383564e", - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "requestMessage": Object { + "requestMessage": { "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", }, "mime-type": "application/json", @@ -812,64 +812,64 @@ Object { "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "72c96cd1-1f26-4bf3-8a00-5c00926859a8": Object { + "72c96cd1-1f26-4bf3-8a00-5c00926859a8": { "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.208Z", "id": "72c96cd1-1f26-4bf3-8a00-5c00926859a8", "isVerified": true, - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "4185f336-f307-4022-a27d-78d1271586f6", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "proposalMessage": Object { + "proposalMessage": { "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", - "presentation_proposal": Object { + "presentation_proposal": { "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", - "attributes": Array [ - Object { + "attributes": [ + { "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", "name": "name", "value": "Alice", }, ], - "predicates": Array [], + "predicates": [], }, }, - "requestMessage": Object { + "requestMessage": { "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, @@ -877,55 +877,55 @@ Object { "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2022-09-08T19:35:53.872Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.4", "updatedAt": "2022-01-21T22:50:20.522Z", }, }, - "ea840186-3c77-45f4-a2e6-349811ad8994": Object { + "ea840186-3c77-45f4-a2e6-349811ad8994": { "id": "ea840186-3c77-45f4-a2e6-349811ad8994", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.261Z", "id": "ea840186-3c77-45f4-a2e6-349811ad8994", "isVerified": true, - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "2481ce81-560b-4ce6-a22b-ee4b6ed369e8", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI5MDg5MzE2Mjc0ODc5NTI4MjMzNDA1MTY0NTgwOTM1OTIxMjIwMjMyMzg5ODE0NjE4OTc3ODA3MjA0MDg4OTQ0ODkzNDc1OTE5NTE4Mjc0NDEwNTU3OTU3NjEwNjYzOTAxMTcwODM1NDM2Nzk1NDU4NDU1Mjg3NzEwOTk3NTk3OTA1OTM3NTYyODIyNjg5NTE3MjAyNzQ4NTUxODgzODQ5NjY3MjYwNTA3NjU0NDM5OTk0MjczNDQ0MTU5NTQyMzg3MzI0OTM5OTAzMDcyMDc2MjQ4Njg1MTgyMjA4NTA0OTkyOTg5MTk0NzUwMDgyODU1MTc1NDE1OTIzMjU3MzA0MTQ0NjYxMDc5MDU2NzExMTg3NzMzMDE3NDQ1MTEyOTQyNDAyMTEzNDg0NjM5MTMxMDY2MDc3ODE2NzQzMzY3OTMzMDI3MjY3MTQ3MDIxMTkxODQ0NTQzMzI5NzUzMTA1NTA3MDk0Mzc5OTA5OTYzNjcxMTQ4NzM3Mjk3NDA2MzUxMzk0NTcwNTM3Nzk0NDg1Njc1ODc3MDU5OTI2NTc3MDU4MzY1NTA0MDQwNjAzNDIxMDI3NDYyOTY0OTExNTc5MDAyNjg2NDAzMjMyOTc3OTU0ODM1Nzc2NzQwMDI3NDIxNjI0MTUzNDQ4NzYyODAxODM3OTU3MTQ3NzM0MDkxNDk3NjMwMjA3MTY3MzUzMjAwMTM5ODE4MDg1NjgwMDIzMTc1MDEyNzM4Mjk1NzIwODU2OTMwNDYxMzIxMDQ4NTIxMjQ0ODQ5MjQ5Njc5MDMwMzI0NDcyNjYyOTQxNjc5NDU3OTk3NzQ4NiIsImUiOiI0NTE0MTczNzExODM2MzMzOTk0NjA3MTMwNjQ5MjA0NjEyNzU2Njk1MDI4ODA2NTY0NzI4MzE3OTExNzYxNDA0NTE5Nzk0NjA3NDk4Njg5NjgyOTYxODk3MDc4MDcwMzQ5Nzk0MzUzMDQ1MTY3MTUyMTk2OTU4NTU0NTI5MzgxNjY3MDE5MDA2OSIsInYiOiI1MzM4NDg2NDY2MjE4MTg2ODg3MTUwNzY4NTQ0OTQyMTEyMDcyOTQ1MzczMDQ1MDAzNTk0MTk0MzAxMDA5NDUzMzk5MjMxMDM5NjExMjU4NTE3MTgzODUyMDc4NjI0NjMyMDExNDE2MzI3Mjc1MTM3Nzc1MTAxNjgxODcwMzI4MjY3MTE4MjExNjEwNzAwNDc2MjA5NzMwMTIwODI2NzMyMTkwMDg0ODkyOTc2NTEwMTgxODE2MTkzMzM5MTk0MjE5MDIxOTQ1OTI1NTg4NjEzODEwMjE1Nzg1NDk1NDk0NjQ0NzIwMDM4MjMwMTg1MDUyMDAxMTMxNjE3MjQwMDIyNjQzOTYxNTkwOTU5ODE3ODMxMzg2Mzc5NDQ1MzI2Mzg4NzYzNjQ5MDYxODk4Nzk1ODcwMjE2NTkxMDI3NDkwMzAwMjA0OTc1NzM0NDgyNDM1ODE4MjgwMTQxNzA0MzA0MjMzNDE5NTMyNjc1Mzk3MDE3MTc1MTE3ODI5NDUzNjAxNDM2OTM2MDM3NDMyMzg4OTYyMjMwOTAyNTk1MjE3MTA3MzkxOTMwOTA3NDI4NDQyNDg4ODE2NjQ4NTI4OTkyNjUwMzY0NjIyNDA2MTA5MDUxOTczMjYyOTM3MzYyMTg5NDcwNTUyNDQ2MjAzNTMzNTQzNTY4NjY5MzAwODY0MzQyMzQwMDgwNjg5Mjg5MjI0OTg1MjU4MjU5MTk1Nzc5NTA3MzgwMDI1ODcwNDk0MDIwNDkyMTE2MDExOTA3NjI0NjI0ODg1ODk5NjMxOTk4ODMwNjg4NTY2OTQ5OTgyNjI5Mjg2Mzk2MjIzNDc2NDE1ODM2Nzg3MDA2Mzg5Nzc0MjYxMzk5NjUxMTY3NTYwNzcyMDc5NjkzMDA1NzMwOTQzNTIzNjAwNzM4Mjc4MzA4NDE2MjA5NzQzNzA1ODQ1MzUxNjQzMDUyMTY1MTcyNTg5NTMwMTU0MTU3NjE2OTYzMDg5NjM4ODg4NDc0NDg3MDA3NjY0NTA2ODk5NTE1OTE5MDAxNzIyMDEyNzczMzU3MDc4MjI4OTIzMDMzNTA1NDQ2MzAxOTQxNzA2OTc2NTY3Mzg5NDk3MzgxMDI2NjIyNDEzNTYyODc5MjM0MTM0NTI5Nzk4NzY2ODY0Nzk1OTQ3NzY1ODcwNDgwMTIyNDk0ODE0MzU0MDQ3MzE2ODY0ODczODMzNDgyNDU5NTc1NTQxNDI4NTE0MTciLCJtIjp7Im1hc3Rlcl9zZWNyZXQiOiIxNjE3NTE3NzgwNjcyMjkxNDYzNTc4ODc1NDk1NTkxODgyOTA3ODYzNTk0NzgyMzk4NjczMTIwMDg2OTEwMjA3NzczODk0ODYyNzQxOTIxMzk2OTE2MDUxNDk2NjYzMjIxNDA5MzA3NjA4NTczMDg1ODExMzAyNTYxNDcyMzgxMjY1NjE4MzQyNzc1NTY5MjQ4OTQ3NzY4Mjc3OTQzMzIxMjcyMTY1MjEyMDAxNDI0NDAwMyJ9LCJtMiI6IjE1MDQ5MTk3MTU3NDcyNjQ0MDMzMzE4OTAxNTc5MDYyNTk5NzA2NzU5MzcwMDk1MTk3NzI1NTE3MTM4OTAyMzcwNDUwMTQ5NDk2NjU0MTEzMzA5NTQ4MTc4MDM3NDU1NjY3Njk2NDA0MDY1ODI5MTUzNDYzNDczNzgzMTk5ODA3MjEzNjg5NDE3MTM2NDI4NDg5NzUwNjUzNTc5MjU0NDY0ODk0OTM4MDkyODY2NTUzNjU5In0sImdlX3Byb29mcyI6W119LCJub25fcmV2b2NfcHJvb2YiOm51bGx9XSwiYWdncmVnYXRlZF9wcm9vZiI6eyJjX2hhc2giOiIzNjk4Mjk3ODU5OTY5Nzg3MjI5MTA5NDY2OTIwMDA3ODEwNDA2ODQ3NTI2MDE2NjgxMTIwNDE4OTQ1NDk0NzcwODQyNjI3MjA2MjEyNCIsImNfbGlzdCI6W1syLDIwOCwzLDUzLDIyOSwxMzksMTAyLDUxLDI0MCwxOTUsMTM1LDExNSwxNzYsMTcyLDE4NCw5OSwxMDksMTU2LDgzLDUyLDIxNSwyMjMsODQsMjU1LDY2LDIyNiwyMjMsMTA1LDExMSwyMjEsMTgwLDk1LDEyMiwxMzMsMjIyLDI3LDM5LDk5LDcwLDEzLDM3LDI0LDI1NSwxMTQsMjM1LDEwOSwxODMsNTEsMjEzLDE5MCwyMjYsMTI2LDExOCwyLDIyMCw3OCw0OSw5LDI0MCw1NSwxNzksMTQ3LDUxLDIwMSwyMTMsMjEzLDEzMCw0LDE4MCwxMDMsMTk1LDgsMjYsMTE4LDE0LDEzMCwxOCwxMzMsMTg3LDYyLDMsOTcsMjEwLDEwMiwxMiwxNjIsNzksMTg0LDU1LDIzMiwyMTksMjIwLDE3NSwyNTUsMTY5LDE5NywxMjMsMTI3LDE2MCwyNSwxNTEsMTg3LDg3LDE5MSwxMDksMTk4LDQ0LDcxLDM4LDUwLDEwNCwyNiwyMTYsMTgwLDIxOCwxNDUsMTAsNzYsMTgwLDE1Nyw5OCwyMzQsNzcsMTY5LDE1MSw2OCwxNzAsMTg5LDE1LDIxNyw5OCwyMzUsMTI0LDE2NywyMzYsMjUzLDExMiwyNDQsMTg5LDk1LDE3OCw2MCw3MiwyMjgsMjIzLDcwLDI3LDYwLDIzNiwyMTIsOTcsMjA1LDIyLDI1MCwzNCwyNDYsMTIyLDM0LDgsMjU1LDIyLDEyNywxNTEsMjQyLDE4MCwxNzEsMTIxLDIyNywzMiwxMDMsNTEsMTcwLDIzNCwyMDYsMjAsMTAyLDIwNCwxMTYsMTk5LDAsMTE5LDExNSwxODAsMjA3LDE2LDQzLDU5LDI0MiwxNzksMTksMTk5LDQ4LDEyNyw5LDYzLDg4LDIxLDAsMjE1LDE3NCw0NywxNzcsMjMyLDE4MiwyNTMsMjQ5LDI0OCwxMTgsMTk2LDI1NCwxMzksMTIsMjksMSw0OCwxMDUsMzMsNCwyMDgsMTA2LDIzNSwyNDcsMjEwLDExMiwyMTAsMTA2LDE5OSwxOTgsNDcsOCwyMzYsNTIsOSw2NywxMjgsMjQwLDI1NCwyMzIsMjEwLDQsMjM5LDE4MywzOSwxOTMsMjQyLDMyLDEzMywxOTQsMTQ4LDk4LDExMSw3NywxNTUsMjA1LDE3OCwxOTcsMTRdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6eyJzdWJfcHJvb2ZfaW5kZXgiOjAsInJhdyI6IkFsaWNlIiwiZW5jb2RlZCI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In19LCJzZWxmX2F0dGVzdGVkX2F0dHJzIjp7fSwidW5yZXZlYWxlZF9hdHRycyI6e30sInByZWRpY2F0ZXMiOnt9fSwiaWRlbnRpZmllcnMiOlt7InNjaGVtYV9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6Mjp0ZXN0LXNjaGVtYS00ZTk0YzJlNC00ZjQ3LTRmZjMtYTg4OC02ZjY0ZGE2YTkyZGM6MS4wIiwiY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyIsInJldl9yZWdfaWQiOm51bGwsInRpbWVzdGFtcCI6bnVsbH1dfQ==", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "requestMessage": Object { + "requestMessage": { "@id": "7fcfc074-43ac-43cc-92b9-76afceeebe82", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjUyODExNDc1NTIxNzg3NzExMjI1Mzc0NSIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7Im5hbWUiOnsibmFtZSI6Im5hbWUiLCJyZXN0cmljdGlvbnMiOlt7ImNyZWRfZGVmX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODozOkNMOjQ3MjMxOTpUQUcifV19fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9fQ==", }, "mime-type": "application/json", @@ -936,63 +936,63 @@ Object { "threadId": "7fcfc074-43ac-43cc-92b9-76afceeebe82", }, }, - "ec02ba64-63e3-46bc-b2a4-9d549d642d30": Object { + "ec02ba64-63e3-46bc-b2a4-9d549d642d30": { "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "tags": Object { + "tags": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "state": "done", "threadId": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, "type": "ProofExchangeRecord", - "value": Object { + "value": { "connectionId": "946f660f-bfa6-4a98-a801-ebde5da95e2c", "createdAt": "2022-09-08T19:36:06.208Z", "id": "ec02ba64-63e3-46bc-b2a4-9d549d642d30", - "metadata": Object {}, - "presentationMessage": Object { + "metadata": {}, + "presentationMessage": { "@id": "4185f336-f307-4022-a27d-78d1271586f6", "@type": "https://didcomm.org/present-proof/1.0/presentation", - "presentations~attach": Array [ - Object { + "presentations~attach": [ + { "@id": "libindy-presentation-0", - "data": Object { + "data": { "base64": "eyJwcm9vZiI6eyJwcm9vZnMiOlt7InByaW1hcnlfcHJvb2YiOnsiZXFfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsibmFtZSI6IjI3MDM0NjQwMDI0MTE3MzMxMDMzMDYzMTI4MDQ0MDA0MzE4MjE4NDg2ODE2OTMxNTIwODg2NDA1NTM1NjU5OTM0NDE3NDM4NzgxNTA3In0sImFfcHJpbWUiOiI1MjE1MTkyMzYwMDA2NTg5OTUyMjM1MTYwNjkxMzU1OTc4MjA2NDUzODA3NDUzNTk5OTE5OTkzNzM1NTczNzA1OTA2OTY5MTAwMjIzOTM4MjMyMzE5NDE1Njk1NTMzNjQ4MDMzNzc3NzI4NTE3NzI4Mjk4NDU1OTI0NjQ1NTU1NjQyNjE1NTQ3NTI0MDc0MTAyOTQyNjg4NzQwNDcwMjg4MDAyNTgwMjI4OTg4MDk4MDk0NjQxODc5NjkxNzQwMTY2OTc0MzA2OTczNjQ2MDA3ODg3NTU1NDU2NjM3NTUyODk2NjkxNDc3NDg2MDM1MTIxMjU0NDY0NzA2MDY0Mzg2NzA1MzU0MDk3MDcxNDc5OTkwMzIwNzIxMzQ4MjM1NTQ2NjI5OTcxNzAyNzgwNjUwNTgyMDQ3MzMxOTIwNjE5OTIzODg4NjU1NjI0NjYzMDE4NTAzMDUzOTQ4OTEyNzk4MDE1ODA2Mzk5MTAxNTg3MzAzNTgwNzkyMzQ3OTU2NjE1MDgzODczNDQ5MTM0MjEyOTE2MDY3MTExMjM4MjQzMjM1MTk5OTk1MTUzNjIyMTM1NjA4Nzg5OTc2MTk2NTkzMTczNTU4NTA0NDYxODA0MTk5MzY3MTkwODY2MDY5MTk1MDE5MjA4NjQyMzAwNzE5NjAzMDk5MzI1MjA0Njg4MzkwMjc2NTM5NjQxMDk0Mjc4NTY2NTUzOTU1Mzg4NDAwMDUyNzEyNzk0MjUwODgxODg3NDIzMDM5NTgyMjM5MjQ4NTk0NjYyMDYzMDA0ODI2MzE5NDQxNTUwNzEyNDA3MTQ4NDc4Njc1MCIsImUiOiIxNTk2OTcwODg4Njk3NTIwMjIzMzA3ODgyOTM5MzI5OTY0NTM3NjA4Mzc3NjQ0MTc2MjIyODczMjc2MTc0Mjg5ODkzNjgyNDAzODg0NzkzOTk1NDIwMjQ2MTEzNDk5NjMxNDAyMTQxMzQ0OTYxOTg3NDAyNjAxNDc4NTQ3NjEwNjI5MTgzNzc2NDQiLCJ2IjoiOTE0NzEwNTI1NDAwMDA2MDgzNTMwMTMzOTIxMjQyNTQwMzI5ODAzNjI4MjgyMDA0MDAzMTY1ODIyMjMyOTM0NDYxODAyMjQ4MDk4OTU5MzY1MTY2ODA3MjEwODgzNzIyNjIyMzA1ODQyNjM4NzM4NjIzNzM5NzYwNjEzODc5NDcwOTU5NDkyNjg1ODY5MTY4NTQyMDU1MjAyNzcxMDYxODI4MDEwMTAzNDY3Nzk2MDA5NDIzMTc4NzU2NDQ5OTk3NDQzODk5NDkyMDEyMTE0OTYyMDk5MDgwMDg1MzAxMjUyMDU1NDk0MDUzMTM4MzU4NDE0MjM0MDg5MzQyNDY2Nzg0MTA4ODkwMjA2NjY0NDI1NDE1ODYzNDA5NTI1MjI3NDY0NTg1OTU0MzQ3OTk2OTIwNTU0NTg5Nzc3OTA1MzMzMDMxMzAzMzEzNTI1OTY3NDg3NzAyMDQxODEwNzA3ODQ1OTAyMTAyODAxMTM0NDk3ODAxNjQyNTgwNzQyNjg0Njk5OTk3ODM2NTY0NzM2NDE5NDc0NzM1Mzg2ODkzMTQwMDA4ODIzNjIzMjA1MDA5MzE3NjgzNTIyNjI5NzkwMDY1NDExNDE5MzY3MTc3MTgwMTQ3MDk3ODkwNTkxNDM4NTkyMjQyNzA1MTg5NDM4NDE2MjA4MTA0MjczNjI3MjUyNzc2NzY1NjI0NjExMjk3NjQzODA5NzIwMjcxMDUzOTY1MzM4MTA5Njk1MTM4NjA3NTA0NzExMDc0NDY0ODU5Mzc5MzUxNTI1ODY4NzgyMjk2MzIzNDIwMzIwMTAwMzQ4MDA5MDMyMzIxOTM2ODMzNDk0Nzc2NDUxMjQ0NzMzNTQxMzI3Mzc4MTc3OTYwMTMzOTg2NzE4NTA2NTk0MzE2MjMyMzEwMTk5NjE2NjQ4NzI3MjU0MTUwNjg2MjA4OTQ1MTM3Mzg5MTI5Mjg4NzUyNzE3MjA5NjI3ODcyNjE3NTM3MjEwMDk2NTU2MTkzMzgxMzUwNTgzMDM1NjI5OTg3MjE5MTk3OTQ4OTEyNDI3NzI0MTA1ODE2NzA0NDkyOTMzNzQyMjcxOTQ5NTk1ODAwOTUyMDA5NDczNzQ2OTM5ODU0Mzg0NDI4MzY0MjE0NjUyNjI4MDgyOTQwOTMzODcwNTE1MjE4NTc1MzY4NDgwMjM4OTM2MTc0NDM5NzExNTUzNDI3NDEwNzA2NTgxMTkzMzg2NDM2ODEyIiwibSI6eyJtYXN0ZXJfc2VjcmV0IjoiNDU1NjIzMDY3NjQ4NDQ0NTEzODAyNzM4ODkxODAyMTkxNDg1MzY0NzMzMDkxMTIwMjM5MDg1ODY0NTQxMzU5Njg0MDgyMDczNTgzNDMzMjM4MzU2Njg2MTgwNzI1MzIyMjU2MzgwMjYxNzYyMTc5MTg3MDU3Mjk4Nzg3NzEzNDcyODY5MjE5NDI1ODgyNTEyNzI0ODk4NjkzMDk3OTkwODI4MDIzMjUwNTkyMTAwNDI5OSJ9LCJtMiI6IjEyMjUwNjAyNjEwOTE0Njk5NDEzNDk3MjEyOTYwNTA3OTQ1MDkwOTk1OTMwNjYyMDMyOTA2NTIxNDUxNjI0MDUxMTk2MTg3MjIxNjgxNzEzMDc1OTg0MDY5MjY5MTY3MDQ5ODExMDc3NDk1NTM2MDg4OTE0NTc3MDk2NTgwNjMwODIzNDQyMTk4Nzk5Nzg4NzIyNDIzMzg4MDIyNjE5MzM4MzA2MjM3NTQ2MzI5OTI4MDEifSwiZ2VfcHJvb2ZzIjpbXX0sIm5vbl9yZXZvY19wcm9vZiI6bnVsbH1dLCJhZ2dyZWdhdGVkX3Byb29mIjp7ImNfaGFzaCI6IjU4Njg3MDc5MDA2NTc0OTY1OTA5ODk2MDc5OTU0NTA2NDM2ODg0NzMxOTIxMjk0NDA5NDYxNzI5NjkzMTI4MDM1OTcxMjAwMjY5NzgzIiwiY19saXN0IjpbWzEsMTU3LDMxLDExMiwxNjQsMjA1LDIyNCw3NiwzMiwyMTIsMTczLDc3LDIxNyw2MiwxNzMsMTc3LDQsMTU5LDE0OSwyMzMsMTE3LDEwNCw5MiwxMzgsMzUsMjYsNDgsODUsMjQwLDIzNCw0MywxOTYsNTksMTE0LDI0MiwxNTMsNDAsMTg3LDIzOSwxMDAsMTcwLDIsMzYsMTA3LDIyOSwzNywxMTEsNjAsOTIsOSwxMTcsMjM4LDE0MCwyNDMsMywyNDIsMjM0LDE1MiwxNjYsNiwxNjEsMTM0LDIyMiwxNjMsNDMsNjIsMTIsODEsMjUzLDE3NiwxMjUsMjQsOCw5MiwxMywyMjIsMTcxLDIzMywxODIsMjE1LDIyLDIzNCw5NSwxMTIsMTIyLDEwMiwxNTIsMTA2LDE4Miw0LDIxOSw1NiwxMDEsMTQ3LDIyNywyNDYsMTE1LDY0LDE4Myw0LDY3LDEyNCwxNDAsMTY2LDEzOCwxNCwyNCw1MSwxODUsMTk5LDEwOSwyLDUxLDE5MCwxODEsMjQ3LDYxLDE5MCw4NSwyNDcsMjE3LDIyLDE2NCwxOTIsMjM1LDE4NywxNDgsMjYsMTA5LDI1MywxMTYsMjQxLDY1LDEzNiw0MSwyMjUsMjI5LDE3OCwxMjUsNzksMjQ2LDUzLDQ0LDE5MiwxNDEsMjQzLDE4NCwxNTEsMTIsOSwxMDQsMjE5LDE2Miw3MSw3Miw3LDg5LDgzLDgsMjI1LDEzLDQ4LDEzMywyNDcsMTg3LDExNCw4NSw3Nyw1OSw5OSw3MCw2OSw3MSwxMzMsMTY5LDExMSwxMTAsNzksNDQsMTc0LDIzLDEwMywxNjYsNjksNSwxNDcsNjYsNzcsMTYzLDExMiw0MiwxOTksMTQyLDE0LDIyLDE1MSwyNDgsMzMsMzQsMTk4LDE1NiwyMCwyMzEsMjA0LDcwLDgyLDI1MywxMzgsMTMxLDQyLDIwNSwzMywxMDksMjMyLDE3LDEwNiw2OSw3Miw2MCwyMTAsMjMyLDExMywxMjQsNzYsMTgxLDIwOCwwLDE4NiwyNTEsMTQzLDExMyw0NSwxNzEsMTAsNDMsMTUsMzAsMjA4LDkzLDYsMTY4LDEwNiwyMDMsNDgsMjUwLDIxOSw5LDE3LDEwNCwzNSw1OCwxNTksMTgwLDExMyw2NiwxMzYsNjJdXX19LCJyZXF1ZXN0ZWRfcHJvb2YiOnsicmV2ZWFsZWRfYXR0cnMiOnsiMGZlYTE4OTctNDMxMS00OGYyLWEyYzItMzg3NGU5OTNhZWYwIjp7InN1Yl9wcm9vZl9pbmRleCI6MCwicmF3IjoiQWxpY2UiLCJlbmNvZGVkIjoiMjcwMzQ2NDAwMjQxMTczMzEwMzMwNjMxMjgwNDQwMDQzMTgyMTg0ODY4MTY5MzE1MjA4ODY0MDU1MzU2NTk5MzQ0MTc0Mzg3ODE1MDcifX0sInNlbGZfYXR0ZXN0ZWRfYXR0cnMiOnt9LCJ1bnJldmVhbGVkX2F0dHJzIjp7fSwicHJlZGljYXRlcyI6e319LCJpZGVudGlmaWVycyI6W3sic2NoZW1hX2lkIjoiN3lXNlNvVGpITmhEM3pZZ200UGJLODoyOnRlc3Qtc2NoZW1hLTRlOTRjMmU0LTRmNDctNGZmMy1hODg4LTZmNjRkYTZhOTJkYzoxLjAiLCJjcmVkX2RlZl9pZCI6Ijd5VzZTb1RqSE5oRDN6WWdtNFBiSzg6MzpDTDo0NzIzMTk6VEFHIiwicmV2X3JlZ19pZCI6bnVsbCwidGltZXN0YW1wIjpudWxsfV19", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, - "proposalMessage": Object { + "proposalMessage": { "@id": "00cdd404-82fc-431d-9c18-fb643636d2a5", "@type": "https://didcomm.org/present-proof/1.0/propose-presentation", - "presentation_proposal": Object { + "presentation_proposal": { "@type": "https://didcomm.org/present-proof/1.0/presentation-preview", - "attributes": Array [ - Object { + "attributes": [ + { "cred_def_id": "7yW6SoTjHNhD3zYgm4PbK8:3:CL:472319:TAG", "name": "name", "value": "Alice", }, ], - "predicates": Array [], + "predicates": [], }, }, - "requestMessage": Object { + "requestMessage": { "@id": "2862b25d-396c-46d7-ad09-f48ff4ea6db6", "@type": "https://didcomm.org/present-proof/1.0/request-presentation", - "request_presentations~attach": Array [ - Object { + "request_presentations~attach": [ + { "@id": "libindy-request-presentation-0", - "data": Object { + "data": { "base64": "eyJuYW1lIjoicHJvb2YtcmVxdWVzdCIsInZlcnNpb24iOiIxLjAiLCJub25jZSI6IjQwMzU1MDc0MDYxMTU0MzEwMzA5NzMyMiIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7IjBmZWExODk3LTQzMTEtNDhmMi1hMmMyLTM4NzRlOTkzYWVmMCI6eyJuYW1lIjoibmFtZSIsInJlc3RyaWN0aW9ucyI6W3siY3JlZF9kZWZfaWQiOiI3eVc2U29UakhOaEQzellnbTRQYks4OjM6Q0w6NDcyMzE5OlRBRyJ9XX19LCJyZXF1ZXN0ZWRfcHJlZGljYXRlcyI6e319", }, "mime-type": "application/json", }, ], - "~thread": Object { + "~thread": { "thid": "00cdd404-82fc-431d-9c18-fb643636d2a5", }, }, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap index c96903e554..92ecc32e40 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.3.test.ts.snap @@ -1,57 +1,57 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`UpdateAssistant | v0.3.1 - v0.4 should correctly update the did records and remove cache records 1`] = ` -Object { - "4993c740-5cd9-4c79-a7d8-23d1266d31be": Object { +{ + "4993c740-5cd9-4c79-a7d8-23d1266d31be": { "id": "4993c740-5cd9-4c79-a7d8-23d1266d31be", - "tags": Object { + "tags": { "did": "did:indy:bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", "legacyUnqualifiedDid": undefined, "method": "indy", "methodSpecificIdentifier": "bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", "qualifiedIndyDid": undefined, - "recipientKeyFingerprints": Array [], + "recipientKeyFingerprints": [], "role": "created", }, "type": "DidRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:35:07.208Z", "did": "did:indy:bcovrin:test:Pow4pdnPgTS7JAXvWkoF2c", "id": "4993c740-5cd9-4c79-a7d8-23d1266d31be", - "metadata": Object {}, + "metadata": {}, "role": "created", "updatedAt": "2023-03-18T22:50:20.522Z", }, }, - "8168612b-73d1-4917-9a61-84e8102988f0": Object { + "8168612b-73d1-4917-9a61-84e8102988f0": { "id": "8168612b-73d1-4917-9a61-84e8102988f0", - "tags": Object { + "tags": { "did": "did:indy:bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", "legacyUnqualifiedDid": undefined, "method": "indy", "methodSpecificIdentifier": "bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", "qualifiedIndyDid": undefined, - "recipientKeyFingerprints": Array [], + "recipientKeyFingerprints": [], "role": "created", }, "type": "DidRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:35:04.191Z", "did": "did:indy:bcovrin:test:8DFqUo6UtQLLZETE7Gm29k", "id": "8168612b-73d1-4917-9a61-84e8102988f0", - "metadata": Object {}, + "metadata": {}, "role": "created", "updatedAt": "2023-03-18T22:50:20.522Z", }, }, - "STORAGE_VERSION_RECORD_ID": Object { + "STORAGE_VERSION_RECORD_ID": { "id": "STORAGE_VERSION_RECORD_ID", - "tags": Object {}, + "tags": {}, "type": "StorageVersionRecord", - "value": Object { + "value": { "createdAt": "2023-03-18T18:35:02.888Z", "id": "STORAGE_VERSION_RECORD_ID", - "metadata": Object {}, + "metadata": {}, "storageVersion": "0.4", "updatedAt": "2023-03-18T22:50:20.522Z", }, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap index 29d6fd05da..0698482397 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup-askar.test.ts.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`UpdateAssistant | Backup | Aries Askar should create a backup 1`] = ` -Array [ - Object { - "_tags": Object { +[ + { + "_tags": { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", @@ -11,27 +11,27 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "createdAt": "2022-03-21T22:50:20.522Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -41,11 +41,11 @@ Array [ "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "updatedAt": "2022-03-22T22:50:20.522Z", }, - Object { - "_tags": Object { + { + "_tags": { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialIds": Array [ + "credentialIds": [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], "state": "done", @@ -54,37 +54,37 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "createdAt": "2022-03-21T22:50:20.535Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", "credentialRecordType": "indy", }, ], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", "vr_prime": null, }, @@ -97,8 +97,8 @@ Array [ "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "updatedAt": "2022-03-22T22:50:20.522Z", }, - Object { - "_tags": Object { + { + "_tags": { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", @@ -106,27 +106,27 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "createdAt": "2022-03-21T22:50:20.740Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -136,11 +136,11 @@ Array [ "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "updatedAt": "2022-03-22T22:50:20.522Z", }, - Object { - "_tags": Object { + { + "_tags": { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialIds": Array [ + "credentialIds": [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], "state": "done", @@ -149,37 +149,37 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "createdAt": "2022-03-21T22:50:20.746Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", "credentialRecordType": "indy", }, ], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", "vr_prime": null, }, diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap index 676480ae59..6c949c78f3 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/backup.test.ts.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`UpdateAssistant | Backup should create a backup 1`] = ` -Array [ - Object { - "_tags": Object { +[ + { + "_tags": { "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "state": "done", "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", @@ -11,27 +11,27 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "0b6de73d-b376-430f-b2b4-f6e51407bb66", "createdAt": "2022-03-21T22:50:20.522Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "574b2a37-1db1-4af1-a3bf-35c6cb9e1d7a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -41,11 +41,11 @@ Array [ "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "updatedAt": "2022-03-21T22:50:20.522Z", }, - Object { - "_tags": Object { + { + "_tags": { "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "credentialId": "a77114e1-c812-4bff-a53c-3d5003fcc278", - "credentialIds": Array [ + "credentialIds": [ "a77114e1-c812-4bff-a53c-3d5003fcc278", ], "state": "done", @@ -54,37 +54,37 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "54b61a2c-59ae-4e63-a441-7f1286350132", "createdAt": "2022-03-21T22:50:20.535Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "a77114e1-c812-4bff-a53c-3d5003fcc278", "credentialRecordType": "indy", }, ], "id": "5f2b7bc7-edfd-47e7-a1d4-aae050df2c4a", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "36456944381549782028917743247126995038265466209293312755125557271456380841610111892515020379470931691048072348420844231863825225515560265358581756565441268878364665494094789024845049226122885121039335781567964878826549149370097276812152226343824116049855825405977949749345353074025294938300401262824951638782220004732873597724698990420932910079362747837952520524827009393981876443737452031919055976088763615615890946142630576421462920865811255312740184209214306243871230276622595183415487741608569800898909023830922654063814555128779494528740438076748829436757078504882332589744263200806138145494157659396691564807976032319024007464003538934", "vr_prime": null, }, @@ -97,8 +97,8 @@ Array [ "threadId": "578e73da-c3be-43d4-949b-7aadfd5a6eae", "updatedAt": "2022-03-21T22:50:20.522Z", }, - Object { - "_tags": Object { + { + "_tags": { "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "state": "done", "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", @@ -106,27 +106,27 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "cd66cbf1-5721-449e-8724-f4d8dcef1bc4", "createdAt": "2022-03-21T22:50:20.740Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [], + "credentials": [], "id": "ad644d8a-48a2-4c55-b46d-7a7f1a9278c7", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, @@ -136,11 +136,11 @@ Array [ "threadId": "e2c2194c-6ac6-4b27-9030-18887c79b5eb", "updatedAt": "2022-03-21T22:50:20.522Z", }, - Object { - "_tags": Object { + { + "_tags": { "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "credentialId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", - "credentialIds": Array [ + "credentialIds": [ "19c1f29f-d2df-486c-b8c6-950c403fa7d9", ], "state": "done", @@ -149,37 +149,37 @@ Array [ "autoAcceptCredential": "contentApproved", "connectionId": "d8f23338-9e99-469a-bd57-1c9a26c0080f", "createdAt": "2022-03-21T22:50:20.746Z", - "credentialAttributes": Array [ - Object { + "credentialAttributes": [ + { "mime-type": "text/plain", "name": "name", "value": "Alice", }, - Object { + { "mime-type": "text/plain", "name": "age", "value": "25", }, - Object { + { "mime-type": "text/plain", "name": "dateOfBirth", "value": "2020-01-01", }, ], - "credentials": Array [ - Object { + "credentials": [ + { "credentialRecordId": "19c1f29f-d2df-486c-b8c6-950c403fa7d9", "credentialRecordType": "indy", }, ], "id": "c7e0a752-7f1c-41c0-b0ae-a68c2d97ca8c", - "metadata": Object { - "_internal/indyCredential": Object { + "metadata": { + "_internal/indyCredential": { "credentialDefinitionId": "TL1EaPFCZ8Si5aUrqScBDt:3:CL:681:default", "schemaId": "TL1EaPFCZ8Si5aUrqScBDt:2:schema-80f7eec5-8e5a-43ca-ad4d-3274fb9361b8:1.0", }, - "_internal/indyRequest": Object { - "master_secret_blinding_data": Object { + "_internal/indyRequest": { + "master_secret_blinding_data": { "v_prime": "24405223168730122709164916892481085040205443709643249329100687534344659826655374235392514476392517756663433844139774514430993889493707631169979521764390851593418941181409704266182779162417466204970949168472702858363964258641437554267668466400711344128132909691514606077477555576087059339291048485225394874964325220472232903203038212033940680060605090839733163438385288769519855418153181511119637865605476043416048121313638627002888436809192752657860306784733123742838413845299796745569824223645588826964796075250758249133953560017373025169692866449286962430731916293683231375510684692358406054381559324718715654332979447698704161714028193478", "vr_prime": null, }, diff --git a/packages/core/tests/logger.ts b/packages/core/tests/logger.ts index cff2e39586..769143e938 100644 --- a/packages/core/tests/logger.ts +++ b/packages/core/tests/logger.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { ILogObject } from 'tslog' +import type { ILogObj } from 'tslog' import { appendFileSync } from 'fs' import { Logger } from 'tslog' @@ -9,15 +9,15 @@ import { LogLevel } from '../src/logger' import { BaseLogger } from '../src/logger/BaseLogger' import { replaceError } from '../src/logger/replaceError' -function logToTransport(logObject: ILogObject) { +function logToTransport(logObject: ILogObj) { appendFileSync('logs.txt', JSON.stringify(logObject) + '\n') } export class TestLogger extends BaseLogger { - public readonly logger: Logger + public readonly logger: Logger // Map our log levels to tslog levels - private tsLogLevelMap = { + private tsLogLevelStringMap = { [LogLevel.test]: 'silly', [LogLevel.trace]: 'trace', [LogLevel.debug]: 'debug', @@ -27,44 +27,40 @@ export class TestLogger extends BaseLogger { [LogLevel.fatal]: 'fatal', } as const + // Map our log levels to tslog levels + private tsLogLevelNumgerMap = { + [LogLevel.test]: 0, + [LogLevel.trace]: 1, + [LogLevel.debug]: 2, + [LogLevel.info]: 3, + [LogLevel.warn]: 4, + [LogLevel.error]: 5, + [LogLevel.fatal]: 6, + } as const + public static fromLogger(logger: TestLogger, name?: string) { return new TestLogger(logger.logLevel, name, logger.logger) } - public constructor(logLevel: LogLevel, name?: string, logger?: Logger) { + public constructor(logLevel: LogLevel, name?: string, logger?: Logger) { super(logLevel) if (logger) { - this.logger = logger.getChildLogger({ + this.logger = logger.getSubLogger({ name, - minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelMap[this.logLevel], + minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelNumgerMap[this.logLevel], }) } else { this.logger = new Logger({ name, - minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelMap[this.logLevel], - ignoreStackLevels: 5, - attachedTransports: [ - { - transportLogger: { - silly: logToTransport, - debug: logToTransport, - trace: logToTransport, - info: logToTransport, - warn: logToTransport, - error: logToTransport, - fatal: logToTransport, - }, - // always log to file - minLevel: 'silly', - }, - ], + minLevel: this.logLevel == LogLevel.off ? undefined : this.tsLogLevelNumgerMap[this.logLevel], + attachedTransports: [logToTransport], }) } } private log(level: Exclude, message: string, data?: Record): void { - const tsLogLevel = this.tsLogLevelMap[level] + const tsLogLevel = this.tsLogLevelStringMap[level] if (this.logLevel === LogLevel.off) return diff --git a/packages/indy-sdk-to-askar-migration/jest.config.ts b/packages/indy-sdk-to-askar-migration/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/indy-sdk-to-askar-migration/jest.config.ts +++ b/packages/indy-sdk-to-askar-migration/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 5f8171a183..a84e6ed9cd 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -34,7 +34,7 @@ "devDependencies": { "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", "indy-sdk": "^1.16.0-dev-1655", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/indy-sdk/jest.config.ts b/packages/indy-sdk/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/indy-sdk/jest.config.ts +++ b/packages/indy-sdk/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/indy-sdk/package.json b/packages/indy-sdk/package.json index 12d019bdae..27f6402457 100644 --- a/packages/indy-sdk/package.json +++ b/packages/indy-sdk/package.json @@ -28,13 +28,13 @@ "@aries-framework/core": "0.3.3", "@types/indy-sdk": "1.16.26", "@stablelib/ed25519": "^1.0.3", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", + "class-transformer": "0.5.1", + "class-validator": "0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts index b087c499f5..f475158263 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import type { IndySdkPool } from '../../ledger/IndySdkPool' import type { DidRecord, RecordSavedEvent } from '@aries-framework/core' @@ -200,9 +201,9 @@ describe('IndySdkIndyDidRegistrar', () => { test('creates a did:indy document without services', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore method is private const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) const result = await indySdkIndyDidRegistrar.create(agentContext, { @@ -261,9 +262,9 @@ describe('IndySdkIndyDidRegistrar', () => { }) test('creates a did:indy document by passing did', async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore method is private const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) const result = await indySdkIndyDidRegistrar.create(agentContext, { @@ -321,14 +322,14 @@ describe('IndySdkIndyDidRegistrar', () => { test('creates a did:indy document with services', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore method is private const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore method is private const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') + // @ts-ignore type check fails because method is private setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) const result = await indySdkIndyDidRegistrar.create(agentContext, { @@ -429,14 +430,14 @@ describe('IndySdkIndyDidRegistrar', () => { test('stores the did document', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore method is private const registerPublicDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore method is private const setEndpointsForDidSpy = jest.spyOn(indySdkIndyDidRegistrar, 'setEndpointsForDid') + // @ts-ignore type check fails because method is private setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) const saveCalled = jest.fn() diff --git a/packages/indy-vdr/jest.config.ts b/packages/indy-vdr/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/indy-vdr/jest.config.ts +++ b/packages/indy-vdr/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index f7eee340a7..ec0839247e 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -31,8 +31,8 @@ "devDependencies": { "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", "@stablelib/ed25519": "^1.0.2", - "rimraf": "^4.0.7", + "rimraf": "^4.4.0", "rxjs": "^7.2.0", - "typescript": "~4.9.4" + "typescript": "~4.9.5" } } diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts index e75cc4d97e..308947062b 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import type { DidRecord, RecordSavedEvent } from '@aries-framework/core' import { @@ -199,9 +200,9 @@ describe('IndyVdrIndyDidRegistrar', () => { test('creates a did:indy document without services', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) const result = await indyVdrIndyDidRegistrar.create(agentContext, { @@ -261,9 +262,9 @@ describe('IndyVdrIndyDidRegistrar', () => { }) test('creates a did:indy document by passing did', async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private - const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) const result = await indyVdrIndyDidRegistrar.create(agentContext, { @@ -322,9 +323,9 @@ describe('IndyVdrIndyDidRegistrar', () => { test('creates a did:indy document with services using diddocContent', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private - const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -483,14 +484,14 @@ describe('IndyVdrIndyDidRegistrar', () => { test('creates a did:indy document with services using attrib', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') + // @ts-ignore type check fails because method is private setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) const result = await indyVdrIndyDidRegistrar.create(agentContext, { @@ -619,14 +620,14 @@ describe('IndyVdrIndyDidRegistrar', () => { test('stores the did document', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') + // @ts-ignore type check fails because method is private setEndpointsForDidSpy.mockImplementationOnce(() => Promise.resolve(undefined)) const saveCalled = jest.fn() diff --git a/packages/node/jest.config.ts b/packages/node/jest.config.ts index ce53584ebf..2556d19c61 100644 --- a/packages/node/jest.config.ts +++ b/packages/node/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, } diff --git a/packages/node/package.json b/packages/node/package.json index e82409ea98..f365568e32 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -30,16 +30,16 @@ "ffi-napi": "^4.0.3", "node-fetch": "^2.6.1", "ref-napi": "^3.0.3", - "ws": "^7.5.3" + "ws": "^8.13.0" }, "devDependencies": { "@types/ffi-napi": "^4.0.5", "@types/node": "^16.11.7", "@types/node-fetch": "^2.5.10", "@types/ref-napi": "^3.0.4", - "@types/ws": "^7.4.6", + "@types/ws": "^8.5.4", "nock": "^13.3.0", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/openid4vc-client/jest.config.ts b/packages/openid4vc-client/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/openid4vc-client/jest.config.ts +++ b/packages/openid4vc-client/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-client/package.json index 31acdd3ec9..d9d642171d 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-client/package.json @@ -31,7 +31,7 @@ "devDependencies": { "@aries-framework/node": "0.3.3", "nock": "^13.3.0", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/question-answer/jest.config.ts b/packages/question-answer/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/question-answer/jest.config.ts +++ b/packages/question-answer/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/question-answer/package.json b/packages/question-answer/package.json index 1640e283cd..9c00199122 100644 --- a/packages/question-answer/package.json +++ b/packages/question-answer/package.json @@ -26,13 +26,13 @@ "dependencies": { "@aries-framework/core": "0.3.3", "class-transformer": "0.5.1", - "class-validator": "0.13.1", + "class-validator": "0.14.0", "rxjs": "^7.2.0" }, "devDependencies": { "@aries-framework/node": "0.3.3", "reflect-metadata": "^0.1.13", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/react-native/jest.config.ts b/packages/react-native/jest.config.ts index 0e512f33f8..6426c5d8b8 100644 --- a/packages/react-native/jest.config.ts +++ b/packages/react-native/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, moduleNameMapper: { ...base.moduleNameMapper, diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 653c19627e..edc4b21683 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -25,21 +25,19 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@azure/core-asynciterator-polyfill": "^1.0.0", - "events": "^3.3.0", - "@types/react-native": "^0.64.10" + "@azure/core-asynciterator-polyfill": "^1.0.2", + "events": "^3.3.0" }, "devDependencies": { - "@types/react-native": "^0.64.10", - "react": "17.0.1", - "react-native": "0.64.2", - "react-native-fs": "^2.18.0", - "react-native-get-random-values": "^1.7.0", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "react-native": "^0.71.4", + "react-native-fs": "^2.20.0", + "react-native-get-random-values": "^1.8.0", + "rimraf": "^4.4.0", + "typescript": "~4.9.5" }, "peerDependencies": { - "react-native-fs": "^2.18.0", - "react-native-get-random-values": "^1.7.0" + "react-native": "^0.71.4", + "react-native-fs": "^2.20.0", + "react-native-get-random-values": "^1.8.0" } } diff --git a/packages/tenants/jest.config.ts b/packages/tenants/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/tenants/jest.config.ts +++ b/packages/tenants/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/tenants/package.json b/packages/tenants/package.json index 13f1f22438..24f8a859ab 100644 --- a/packages/tenants/package.json +++ b/packages/tenants/package.json @@ -25,12 +25,12 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "async-mutex": "^0.3.2" + "async-mutex": "^0.4.0" }, "devDependencies": { "@aries-framework/node": "0.3.3", "reflect-metadata": "^0.1.13", - "rimraf": "^4.0.7", - "typescript": "~4.9.4" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index b668ec5151..97ac103498 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -19,9 +19,9 @@ }, "dependencies": { "@types/express": "^4.17.13", - "@types/uuid": "^8.3.1", - "@types/ws": "^7.4.6", - "class-validator": "0.13.1", + "@types/uuid": "^9.0.1", + "@types/ws": "^8.5.4", + "class-validator": "0.14.0", "rxjs": "^7.2.0" } } diff --git a/tests/jest.config.ts b/tests/jest.config.ts index c9431e4a48..3218944397 100644 --- a/tests/jest.config.ts +++ b/tests/jest.config.ts @@ -4,7 +4,6 @@ import base from '../jest.config.base' const config: Config.InitialOptions = { ...base, - name: '@aries-framework/e2e-test', displayName: '@aries-framework/e2e-test', setupFilesAfterEnv: ['../packages/core/tests/setup.ts'], } diff --git a/yarn.lock b/yarn.lock index 03b8263561..10600e83b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,18 +10,11 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@azure/core-asynciterator-polyfill@^1.0.0": +"@azure/core-asynciterator-polyfill@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" integrity sha512-3rkP4LnnlWawl0LZptJOdXNrT/fHp2eQMadoasa6afspXdpGrtPZuAQc2PD0cpgyuoXtUWyC3tv7xfntjGS5Dw== -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== - dependencies: - "@babel/highlight" "^7.10.4" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" @@ -34,33 +27,33 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298" integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== -"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.0.tgz#1341aefdcc14ccc7553fcc688dd8986a2daffc13" - integrity sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA== +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.0": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e" + integrity sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.21.0" + "@babel/generator" "^7.21.3" "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.21.0" + "@babel/helper-module-transforms" "^7.21.2" "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.0" + "@babel/parser" "^7.21.3" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.0" - "@babel/types" "^7.21.0" + "@babel/traverse" "^7.21.3" + "@babel/types" "^7.21.3" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.21.0", "@babel/generator@^7.21.1", "@babel/generator@^7.5.0", "@babel/generator@^7.7.2": - version "7.21.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.1.tgz#951cc626057bc0af2c35cd23e9c64d384dea83dd" - integrity sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA== +"@babel/generator@^7.20.0", "@babel/generator@^7.21.3", "@babel/generator@^7.7.2": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce" + integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA== dependencies: - "@babel/types" "^7.21.0" + "@babel/types" "^7.21.3" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -72,14 +65,6 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" - integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.18.6" - "@babel/types" "^7.18.9" - "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" @@ -105,7 +90,7 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" -"@babel/helper-create-regexp-features-plugin@^7.18.6": +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz#53ff78472e5ce10a52664272a239787107603ebb" integrity sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg== @@ -130,14 +115,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-explode-assignable-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" - integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.21.0": +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== @@ -166,7 +144,7 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.21.0", "@babel/helper-module-transforms@^7.21.2": +"@babel/helper-module-transforms@^7.21.2": version "7.21.2" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== @@ -192,6 +170,16 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== +"@babel/helper-remap-async-to-generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" + "@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" @@ -240,6 +228,16 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== +"@babel/helper-wrap-function@^7.18.9": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" + integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== + dependencies: + "@babel/helper-function-name" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + "@babel/helpers@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" @@ -249,7 +247,7 @@ "@babel/traverse" "^7.21.0" "@babel/types" "^7.21.0" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": +"@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== @@ -258,12 +256,22 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.0", "@babel/parser@^7.21.2": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.2.tgz#dacafadfc6d7654c3051a66d6fe55b6cb2f2a0b3" - integrity sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3" + integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ== + +"@babel/plugin-proposal-async-generator-functions@^7.0.0": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" + integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0": +"@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.13.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== @@ -279,7 +287,7 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-export-default-from" "^7.18.6" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.1.0": +"@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== @@ -306,7 +314,7 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.1.0": +"@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.13.12": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== @@ -350,7 +358,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.6", "@babel/plugin-syntax-flow@^7.2.0": +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.0", "@babel/plugin-syntax-flow@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz#774d825256f2379d06139be0c723c4dd444f3ca1" integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== @@ -371,7 +379,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6": +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.7.2": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== @@ -441,6 +449,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-transform-async-to-generator@^7.0.0": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" + integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-transform-block-scoped-functions@^7.0.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" @@ -479,20 +496,12 @@ "@babel/template" "^7.20.7" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454" - integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" + integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-exponentiation-operator@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" - integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.18.6": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz#6aeca0adcb81dc627c8986e770bfaa4d9812aff5" @@ -531,7 +540,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.1.0": +"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8": version "7.21.2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== @@ -540,12 +549,13 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-simple-access" "^7.20.2" -"@babel/plugin-transform-object-assign@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.18.6.tgz#7830b4b6f83e1374a5afb9f6111bcfaea872cdd2" - integrity sha512-mQisZ3JfqWh2gVXvfqYCAAyRs6+7oev+myBsTwW5RnPhYXOTuCEw2oe3YgxlXMViXUS53lG8koulI7mJ+8JE+A== +"@babel/plugin-transform-named-capturing-groups-regex@^7.0.0": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" + integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-object-super@^7.0.0": version "7.18.6" @@ -556,9 +566,9 @@ "@babel/helper-replace-supers" "^7.18.6" "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f" - integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA== + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" + integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -601,14 +611,6 @@ "@babel/plugin-syntax-jsx" "^7.18.6" "@babel/types" "^7.21.0" -"@babel/plugin-transform-regenerator@^7.0.0": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" - integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - regenerator-transform "^0.15.1" - "@babel/plugin-transform-runtime@^7.0.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz#2a884f29556d0a68cd3d152dcc9e6c71dfb6eee8" @@ -651,10 +653,11 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typescript@^7.21.0", "@babel/plugin-transform-typescript@^7.5.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz#f0956a153679e3b377ae5b7f0143427151e4c848" - integrity sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg== + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz#316c5be579856ea890a57ebc5116c5d064658f2b" + integrity sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw== dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-create-class-features-plugin" "^7.21.0" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-typescript" "^7.20.0" @@ -667,7 +670,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/preset-flow@^7.0.0": +"@babel/preset-flow@^7.13.13": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.18.6.tgz#83f7602ba566e72a9918beefafef8ef16d2810cb" integrity sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ== @@ -676,7 +679,7 @@ "@babel/helper-validator-option" "^7.18.6" "@babel/plugin-transform-flow-strip-types" "^7.18.6" -"@babel/preset-typescript@^7.1.0": +"@babel/preset-typescript@^7.13.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz#bcbbca513e8213691fe5d4b23d9251e01f00ebff" integrity sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg== @@ -685,7 +688,7 @@ "@babel/helper-validator-option" "^7.21.0" "@babel/plugin-transform-typescript" "^7.21.0" -"@babel/register@^7.0.0": +"@babel/register@^7.13.16": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.21.0.tgz#c97bf56c2472e063774f31d344c592ebdcefa132" integrity sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw== @@ -701,14 +704,14 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.8.4": +"@babel/runtime@^7.0.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.0.0", "@babel/template@^7.20.7", "@babel/template@^7.3.3": +"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== @@ -717,26 +720,26 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.7.2": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.2.tgz#ac7e1f27658750892e815e60ae90f382a46d8e75" - integrity sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw== +"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3", "@babel/traverse@^7.7.2": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67" + integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.21.1" + "@babel/generator" "^7.21.3" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.2" - "@babel/types" "^7.21.2" + "@babel/parser" "^7.21.3" + "@babel/types" "^7.21.3" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.2.tgz#92246f6e00f91755893c2876ad653db70c8310d1" - integrity sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05" + integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -747,14 +750,6 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cnakazawa/watch@^1.0.3": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" - integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== - dependencies: - exec-sh "^0.3.2" - minimist "^1.2.0" - "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -813,22 +808,39 @@ "@digitalcredentials/jsonld-signatures" "^9.3.1" credentials-context "^2.0.0" -"@eslint/eslintrc@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" - integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== +"@eslint-community/eslint-utils@^4.2.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz#a556790523a351b4e47e9d385f47265eaaf9780a" + integrity sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.4.0.tgz#3e61c564fcd6b921cb789838631c5ee44df09403" + integrity sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ== + +"@eslint/eslintrc@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.1.tgz#7888fe7ec8f21bc26d646dbd2c11cd776e21192d" + integrity sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw== dependencies: ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^13.9.0" - ignore "^4.0.6" + debug "^4.3.2" + espree "^9.5.0" + globals "^13.19.0" + ignore "^5.2.0" import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" + js-yaml "^4.1.0" + minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@gar/promisify@^1.0.1": +"@eslint/js@8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe" + integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg== + +"@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== @@ -845,16 +857,21 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@humanwhocodes/config-array@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" - integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== +"@humanwhocodes/config-array@^0.11.8": + version "0.11.8" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" + integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== dependencies: - "@humanwhocodes/object-schema" "^1.2.0" + "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" - minimatch "^3.0.4" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.0": +"@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== @@ -920,6 +937,11 @@ resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.12.tgz#a5051cb91b7698f80265b7506789fb50490477e2" integrity sha512-CPVGTHVLFAVVU6uIhcbhAUWqDrn3u2R3D+ALdqgKwJY1Ca8kFiUvhFN1/DkHtZuEo549wPQmFqH2hCkXaiuF7Q== +"@isaacs/string-locale-compare@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b" + integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -936,170 +958,192 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" - integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== +"@jest/console@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.5.0.tgz#593a6c5c0d3f75689835f1b3b4688c4f8544cb57" + integrity sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^27.5.1" - jest-util "^27.5.1" + jest-message-util "^29.5.0" + jest-util "^29.5.0" slash "^3.0.0" -"@jest/core@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" - integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== +"@jest/core@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.5.0.tgz#76674b96904484e8214614d17261cc491e5f1f03" + integrity sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ== dependencies: - "@jest/console" "^27.5.1" - "@jest/reporters" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.5.0" + "@jest/reporters" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - emittery "^0.8.1" + ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^27.5.1" - jest-config "^27.5.1" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-resolve-dependencies "^27.5.1" - jest-runner "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - jest-watcher "^27.5.1" + jest-changed-files "^29.5.0" + jest-config "^29.5.0" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-resolve-dependencies "^29.5.0" + jest-runner "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" + jest-watcher "^29.5.0" micromatch "^4.0.4" - rimraf "^3.0.0" + pretty-format "^29.5.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/create-cache-key-function@^26.5.0": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-26.6.2.tgz#04cf439207a4fd12418d8aee551cddc86f9ac5f5" - integrity sha512-LgEuqU1f/7WEIPYqwLPIvvHuc1sB6gMVbT6zWhin3txYUNYK/kGQrC1F2WR4gR34YlI9bBtViTm5z98RqVZAaw== +"@jest/create-cache-key-function@^29.2.1": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.5.0.tgz#24e019d03e634be4affe8bcee787d75a36ae57a2" + integrity sha512-LIDZyZgnZss7uikvBKBB/USWwG+GO8+GnwRWT+YkCGDGsqLQlhm9BC3z6+7+eMs1kUlvXQIWEzBR8Q2Pnvx6lg== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^29.5.0" -"@jest/environment@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" - integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== +"@jest/environment@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65" + integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ== dependencies: - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" - jest-mock "^27.5.1" + jest-mock "^29.5.0" -"@jest/fake-timers@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" - integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== +"@jest/expect-utils@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" + integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== dependencies: - "@jest/types" "^27.5.1" - "@sinonjs/fake-timers" "^8.0.1" + jest-get-type "^29.4.3" + +"@jest/expect@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.5.0.tgz#80952f5316b23c483fbca4363ce822af79c38fba" + integrity sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g== + dependencies: + expect "^29.5.0" + jest-snapshot "^29.5.0" + +"@jest/fake-timers@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c" + integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg== + dependencies: + "@jest/types" "^29.5.0" + "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-util "^27.5.1" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-util "^29.5.0" -"@jest/globals@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" - integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== +"@jest/globals@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.5.0.tgz#6166c0bfc374c58268677539d0c181f9c1833298" + integrity sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ== dependencies: - "@jest/environment" "^27.5.1" - "@jest/types" "^27.5.1" - expect "^27.5.1" + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/types" "^29.5.0" + jest-mock "^29.5.0" -"@jest/reporters@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" - integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== +"@jest/reporters@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.5.0.tgz#985dfd91290cd78ddae4914ba7921bcbabe8ac9b" + integrity sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" - glob "^7.1.2" + glob "^7.1.3" graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" istanbul-lib-instrument "^5.1.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-haste-map "^27.5.1" - jest-resolve "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + jest-worker "^29.5.0" slash "^3.0.0" - source-map "^0.6.0" string-length "^4.0.1" - terminal-link "^2.0.0" - v8-to-istanbul "^8.1.0" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" -"@jest/source-map@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" - integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== + dependencies: + "@sinclair/typebox" "^0.25.16" + +"@jest/source-map@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20" + integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== dependencies: + "@jridgewell/trace-mapping" "^0.3.15" callsites "^3.0.0" graceful-fs "^4.2.9" - source-map "^0.6.0" -"@jest/test-result@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" - integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== +"@jest/test-result@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.5.0.tgz#7c856a6ca84f45cc36926a4e9c6b57f1973f1408" + integrity sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ== dependencies: - "@jest/console" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.5.0" + "@jest/types" "^29.5.0" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" - integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== +"@jest/test-sequencer@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz#34d7d82d3081abd523dbddc038a3ddcb9f6d3cc4" + integrity sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ== dependencies: - "@jest/test-result" "^27.5.1" + "@jest/test-result" "^29.5.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-runtime "^27.5.1" + jest-haste-map "^29.5.0" + slash "^3.0.0" -"@jest/transform@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" - integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== +"@jest/transform@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9" + integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw== dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^27.5.1" + "@babel/core" "^7.11.6" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-regex-util "^27.5.1" - jest-util "^27.5.1" + jest-haste-map "^29.5.0" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" + write-file-atomic "^4.0.2" "@jest/types@^26.6.2": version "26.6.2" @@ -1123,13 +1167,17 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@joshuajaco/get-monorepo-packages@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@joshuajaco/get-monorepo-packages/-/get-monorepo-packages-1.2.1.tgz#070bdc4268f5e14d2dd593b02f32bc4c5601d06d" - integrity sha512-3I32bp/UB4UmLqEj/yrBEWTYuCzojn8nR34PZ9Jwb2OeHV95l4Kw0ax1xnnA4yYWU1E1krM2RIWghP3U725dGQ== +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== dependencies: - globby "^7.1.1" - load-json-file "^4.0.0" + "@jest/schemas" "^29.4.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" "@jridgewell/gen-mapping@^0.1.0": version "0.1.1" @@ -1139,7 +1187,7 @@ "@jridgewell/set-array" "^1.0.0" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/gen-mapping@^0.3.2": +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== @@ -1158,6 +1206,14 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" @@ -1171,7 +1227,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== @@ -1179,741 +1235,98 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@lerna/add@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" - integrity sha512-cpmAH1iS3k8JBxNvnMqrGTTjbY/ZAiKa1ChJzFevMYY3eeqbvhsBKnBcxjRXtdrJ6bd3dCQM+ZtK+0i682Fhng== - dependencies: - "@lerna/bootstrap" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/npm-conf" "4.0.0" - "@lerna/validation-error" "4.0.0" - dedent "^0.7.0" - npm-package-arg "^8.1.0" - p-map "^4.0.0" - pacote "^11.2.6" - semver "^7.3.4" - -"@lerna/bootstrap@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-4.0.0.tgz#5f5c5e2c6cfc8fcec50cb2fbe569a8c607101891" - integrity sha512-RkS7UbeM2vu+kJnHzxNRCLvoOP9yGNgkzRdy4UV2hNalD7EP41bLvRVOwRYQ7fhc2QcbhnKNdOBihYRL0LcKtw== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/has-npm-version" "4.0.0" - "@lerna/npm-install" "4.0.0" - "@lerna/package-graph" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/rimraf-dir" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/symlink-binary" "4.0.0" - "@lerna/symlink-dependencies" "4.0.0" - "@lerna/validation-error" "4.0.0" - dedent "^0.7.0" - get-port "^5.1.1" - multimatch "^5.0.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - p-map "^4.0.0" - p-map-series "^2.1.0" - p-waterfall "^2.1.1" - read-package-tree "^5.3.1" - semver "^7.3.4" - -"@lerna/changed@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-4.0.0.tgz#b9fc76cea39b9292a6cd263f03eb57af85c9270b" - integrity sha512-cD+KuPRp6qiPOD+BO6S6SN5cARspIaWSOqGBpGnYzLb4uWT8Vk4JzKyYtc8ym1DIwyoFXHosXt8+GDAgR8QrgQ== - dependencies: - "@lerna/collect-updates" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/listable" "4.0.0" - "@lerna/output" "4.0.0" - -"@lerna/check-working-tree@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/check-working-tree/-/check-working-tree-4.0.0.tgz#257e36a602c00142e76082a19358e3e1ae8dbd58" - integrity sha512-/++bxM43jYJCshBiKP5cRlCTwSJdRSxVmcDAXM+1oUewlZJVSVlnks5eO0uLxokVFvLhHlC5kHMc7gbVFPHv6Q== - dependencies: - "@lerna/collect-uncommitted" "4.0.0" - "@lerna/describe-ref" "4.0.0" - "@lerna/validation-error" "4.0.0" - -"@lerna/child-process@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-4.0.0.tgz#341b96a57dffbd9705646d316e231df6fa4df6e1" - integrity sha512-XtCnmCT9eyVsUUHx6y/CTBYdV9g2Cr/VxyseTWBgfIur92/YKClfEtJTbOh94jRT62hlKLqSvux/UhxXVh613Q== +"@lerna/child-process@6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-6.5.1.tgz#da9161ba00e8d67fa7241a709703e5cc5e4a5e5e" + integrity sha512-QfyleXSD9slh4qM54wDaqKVPvtUH1NJMgsFc9BabqSHO1Ttpandv1EAvTCN9Lu73RbCX3LJpn+BfJmnjHbjCyw== dependencies: chalk "^4.1.0" execa "^5.0.0" strong-log-transformer "^2.1.0" -"@lerna/clean@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-4.0.0.tgz#8f778b6f2617aa2a936a6b5e085ae62498e57dc5" - integrity sha512-uugG2iN9k45ITx2jtd8nEOoAtca8hNlDCUM0N3lFgU/b1mEQYAPRkqr1qs4FLRl/Y50ZJ41wUz1eazS+d/0osA== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/rimraf-dir" "4.0.0" - p-map "^4.0.0" - p-map-series "^2.1.0" - p-waterfall "^2.1.1" - -"@lerna/cli@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-4.0.0.tgz#8eabd334558836c1664df23f19acb95e98b5bbf3" - integrity sha512-Neaw3GzFrwZiRZv2g7g6NwFjs3er1vhraIniEs0jjVLPMNC4eata0na3GfE5yibkM/9d3gZdmihhZdZ3EBdvYA== - dependencies: - "@lerna/global-options" "4.0.0" - dedent "^0.7.0" - npmlog "^4.1.2" - yargs "^16.2.0" - -"@lerna/collect-uncommitted@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/collect-uncommitted/-/collect-uncommitted-4.0.0.tgz#855cd64612969371cfc2453b90593053ff1ba779" - integrity sha512-ufSTfHZzbx69YNj7KXQ3o66V4RC76ffOjwLX0q/ab//61bObJ41n03SiQEhSlmpP+gmFbTJ3/7pTe04AHX9m/g== - dependencies: - "@lerna/child-process" "4.0.0" - chalk "^4.1.0" - npmlog "^4.1.2" - -"@lerna/collect-updates@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-4.0.0.tgz#8e208b1bafd98a372ff1177f7a5e288f6bea8041" - integrity sha512-bnNGpaj4zuxsEkyaCZLka9s7nMs58uZoxrRIPJ+nrmrZYp1V5rrd+7/NYTuunOhY2ug1sTBvTAxj3NZQ+JKnOw== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/describe-ref" "4.0.0" - minimatch "^3.0.4" - npmlog "^4.1.2" - slash "^3.0.0" - -"@lerna/command@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/command/-/command-4.0.0.tgz#991c7971df8f5bf6ae6e42c808869a55361c1b98" - integrity sha512-LM9g3rt5FsPNFqIHUeRwWXLNHJ5NKzOwmVKZ8anSp4e1SPrv2HNc1V02/9QyDDZK/w+5POXH5lxZUI1CHaOK/A== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/package-graph" "4.0.0" - "@lerna/project" "4.0.0" - "@lerna/validation-error" "4.0.0" - "@lerna/write-log-file" "4.0.0" - clone-deep "^4.0.1" - dedent "^0.7.0" - execa "^5.0.0" - is-ci "^2.0.0" - npmlog "^4.1.2" - -"@lerna/conventional-commits@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-4.0.0.tgz#660fb2c7b718cb942ead70110df61f18c6f99750" - integrity sha512-CSUQRjJHFrH8eBn7+wegZLV3OrNc0Y1FehYfYGhjLE2SIfpCL4bmfu/ViYuHh9YjwHaA+4SX6d3hR+xkeseKmw== - dependencies: - "@lerna/validation-error" "4.0.0" - conventional-changelog-angular "^5.0.12" - conventional-changelog-core "^4.2.2" - conventional-recommended-bump "^6.1.0" - fs-extra "^9.1.0" - get-stream "^6.0.0" - lodash.template "^4.5.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - pify "^5.0.0" - semver "^7.3.4" - -"@lerna/create-symlink@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/create-symlink/-/create-symlink-4.0.0.tgz#8c5317ce5ae89f67825443bd7651bf4121786228" - integrity sha512-I0phtKJJdafUiDwm7BBlEUOtogmu8+taxq6PtIrxZbllV9hWg59qkpuIsiFp+no7nfRVuaasNYHwNUhDAVQBig== - dependencies: - cmd-shim "^4.1.0" - fs-extra "^9.1.0" - npmlog "^4.1.2" - -"@lerna/create@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-4.0.0.tgz#b6947e9b5dfb6530321952998948c3e63d64d730" - integrity sha512-mVOB1niKByEUfxlbKTM1UNECWAjwUdiioIbRQZEeEabtjCL69r9rscIsjlGyhGWCfsdAG5wfq4t47nlDXdLLag== +"@lerna/create@6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-6.5.1.tgz#326b5d26c247bfc9e2d8728aa1f69419840cec8c" + integrity sha512-ejERJnfA36jEuKrfM+94feLiyf2/hF2NoG923N0rE4rsmvRFPr1XLVPvAKleXW+Gdi/t1p410lJ7NKaLRMYCYw== dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/npm-conf" "4.0.0" - "@lerna/validation-error" "4.0.0" + "@lerna/child-process" "6.5.1" dedent "^0.7.0" fs-extra "^9.1.0" - globby "^11.0.2" - init-package-json "^2.0.2" - npm-package-arg "^8.1.0" + init-package-json "^3.0.2" + npm-package-arg "8.1.1" p-reduce "^2.1.0" - pacote "^11.2.6" + pacote "^13.6.1" pify "^5.0.0" semver "^7.3.4" slash "^3.0.0" validate-npm-package-license "^3.0.4" - validate-npm-package-name "^3.0.0" - whatwg-url "^8.4.0" + validate-npm-package-name "^4.0.0" yargs-parser "20.2.4" -"@lerna/describe-ref@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/describe-ref/-/describe-ref-4.0.0.tgz#53c53b4ea65fdceffa072a62bfebe6772c45d9ec" - integrity sha512-eTU5+xC4C5Gcgz+Ey4Qiw9nV2B4JJbMulsYJMW8QjGcGh8zudib7Sduj6urgZXUYNyhYpRs+teci9M2J8u+UvQ== +"@mapbox/node-pre-gyp@1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc" + integrity sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw== dependencies: - "@lerna/child-process" "4.0.0" - npmlog "^4.1.2" + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" -"@lerna/diff@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-4.0.0.tgz#6d3071817aaa4205a07bf77cfc6e932796d48b92" - integrity sha512-jYPKprQVg41+MUMxx6cwtqsNm0Yxx9GDEwdiPLwcUTFx+/qKCEwifKNJ1oGIPBxyEHX2PFCOjkK39lHoj2qiag== +"@mapbox/node-pre-gyp@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c" + integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/validation-error" "4.0.0" - npmlog "^4.1.2" - -"@lerna/exec@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-4.0.0.tgz#eb6cb95cb92d42590e9e2d628fcaf4719d4a8be6" - integrity sha512-VGXtL/b/JfY84NB98VWZpIExfhLOzy0ozm/0XaS4a2SmkAJc5CeUfrhvHxxkxiTBLkU+iVQUyYEoAT0ulQ8PCw== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/profiler" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/validation-error" "4.0.0" - p-map "^4.0.0" + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" -"@lerna/filter-options@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-4.0.0.tgz#ac94cc515d7fa3b47e2f7d74deddeabb1de5e9e6" - integrity sha512-vV2ANOeZhOqM0rzXnYcFFCJ/kBWy/3OA58irXih9AMTAlQLymWAK0akWybl++sUJ4HB9Hx12TOqaXbYS2NM5uw== +"@mattrglobal/bbs-signatures@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.0.0.tgz#8ff272c6d201aadab7e08bd84dbfd6e0d48ba12d" + integrity sha512-FFzybdKqSCrS/e7pl5s6Tl/m/x8ZD5EMBbcTBQaqSOms/lebm91lFukYOIe2qc0a5o+gLhtRKye8OfKwD1Ex/g== dependencies: - "@lerna/collect-updates" "4.0.0" - "@lerna/filter-packages" "4.0.0" - dedent "^0.7.0" - npmlog "^4.1.2" + "@stablelib/random" "1.0.0" + optionalDependencies: + "@mattrglobal/node-bbs-signatures" "0.13.0" -"@lerna/filter-packages@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/filter-packages/-/filter-packages-4.0.0.tgz#b1f70d70e1de9cdd36a4e50caa0ac501f8d012f2" - integrity sha512-+4AJIkK7iIiOaqCiVTYJxh/I9qikk4XjNQLhE3kixaqgMuHl1NQ99qXRR0OZqAWB9mh8Z1HA9bM5K1HZLBTOqA== +"@mattrglobal/bbs-signatures@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.1.0.tgz#031418c6a003d1782e3aff95995bda6a0605f9f1" + integrity sha512-uf74y3S7kAxL3FZrva6u+BF3VY3El5QI9IMkyBCoNoJvO+nWJewmTqLMLWEh6QJ1N5egZfDCI4PuS9ISrZJTZg== dependencies: - "@lerna/validation-error" "4.0.0" - multimatch "^5.0.0" - npmlog "^4.1.2" + "@stablelib/random" "1.0.0" + optionalDependencies: + "@mattrglobal/node-bbs-signatures" "0.15.0" -"@lerna/get-npm-exec-opts@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-4.0.0.tgz#dc955be94a4ae75c374ef9bce91320887d34608f" - integrity sha512-yvmkerU31CTWS2c7DvmAWmZVeclPBqI7gPVr5VATUKNWJ/zmVcU4PqbYoLu92I9Qc4gY1TuUplMNdNuZTSL7IQ== +"@mattrglobal/bls12381-key-pair@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@mattrglobal/bls12381-key-pair/-/bls12381-key-pair-1.0.0.tgz#2959f8663595de0209bebe88517235ae34f1e2b1" + integrity sha512-FbvSkoy1n3t5FHtAPj8cyQJL7Bz+hvvmquCBZW2+bOBBBT26JhGtr//s6EmXE9e4EZk7bAA1yMHI6i1Ky2us0Q== dependencies: - npmlog "^4.1.2" + "@mattrglobal/bbs-signatures" "1.0.0" + bs58 "4.0.1" + rfc4648 "1.4.0" -"@lerna/get-packed@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/get-packed/-/get-packed-4.0.0.tgz#0989d61624ac1f97e393bdad2137c49cd7a37823" - integrity sha512-rfWONRsEIGyPJTxFzC8ECb3ZbsDXJbfqWYyeeQQDrJRPnEJErlltRLPLgC2QWbxFgFPsoDLeQmFHJnf0iDfd8w== +"@mattrglobal/node-bbs-signatures@0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@mattrglobal/node-bbs-signatures/-/node-bbs-signatures-0.13.0.tgz#3e431b915325d4b139706f8b26fd84b27c192a29" + integrity sha512-S2wOwDCQYxdjSEjVfcbP3bTq4ZMKeRw/wvBhWRff8CEwuH5u3Qiul+azwDGSesvve1DDceaEhXWiGkXeZTojfQ== dependencies: - fs-extra "^9.1.0" - ssri "^8.0.1" - tar "^6.1.0" - -"@lerna/github-client@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/github-client/-/github-client-4.0.0.tgz#2ced67721363ef70f8e12ffafce4410918f4a8a4" - integrity sha512-2jhsldZtTKXYUBnOm23Lb0Fx8G4qfSXF9y7UpyUgWUj+YZYd+cFxSuorwQIgk5P4XXrtVhsUesIsli+BYSThiw== - dependencies: - "@lerna/child-process" "4.0.0" - "@octokit/plugin-enterprise-rest" "^6.0.1" - "@octokit/rest" "^18.1.0" - git-url-parse "^11.4.4" - npmlog "^4.1.2" - -"@lerna/gitlab-client@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/gitlab-client/-/gitlab-client-4.0.0.tgz#00dad73379c7b38951d4b4ded043504c14e2b67d" - integrity sha512-OMUpGSkeDWFf7BxGHlkbb35T7YHqVFCwBPSIR6wRsszY8PAzCYahtH3IaJzEJyUg6vmZsNl0FSr3pdA2skhxqA== - dependencies: - node-fetch "^2.6.1" - npmlog "^4.1.2" - whatwg-url "^8.4.0" - -"@lerna/global-options@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/global-options/-/global-options-4.0.0.tgz#c7d8b0de6a01d8a845e2621ea89e7f60f18c6a5f" - integrity sha512-TRMR8afAHxuYBHK7F++Ogop2a82xQjoGna1dvPOY6ltj/pEx59pdgcJfYcynYqMkFIk8bhLJJN9/ndIfX29FTQ== - -"@lerna/has-npm-version@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/has-npm-version/-/has-npm-version-4.0.0.tgz#d3fc3292c545eb28bd493b36e6237cf0279f631c" - integrity sha512-LQ3U6XFH8ZmLCsvsgq1zNDqka0Xzjq5ibVN+igAI5ccRWNaUsE/OcmsyMr50xAtNQMYMzmpw5GVLAivT2/YzCg== - dependencies: - "@lerna/child-process" "4.0.0" - semver "^7.3.4" - -"@lerna/import@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/import/-/import-4.0.0.tgz#bde656c4a451fa87ae41733ff8a8da60547c5465" - integrity sha512-FaIhd+4aiBousKNqC7TX1Uhe97eNKf5/SC7c5WZANVWtC7aBWdmswwDt3usrzCNpj6/Wwr9EtEbYROzxKH8ffg== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/validation-error" "4.0.0" - dedent "^0.7.0" - fs-extra "^9.1.0" - p-map-series "^2.1.0" - -"@lerna/info@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/info/-/info-4.0.0.tgz#b9fb0e479d60efe1623603958a831a88b1d7f1fc" - integrity sha512-8Uboa12kaCSZEn4XRfPz5KU9XXoexSPS4oeYGj76s2UQb1O1GdnEyfjyNWoUl1KlJ2i/8nxUskpXIftoFYH0/Q== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/output" "4.0.0" - envinfo "^7.7.4" - -"@lerna/init@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/init/-/init-4.0.0.tgz#dadff67e6dfb981e8ccbe0e6a310e837962f6c7a" - integrity sha512-wY6kygop0BCXupzWj5eLvTUqdR7vIAm0OgyV9WHpMYQGfs1V22jhztt8mtjCloD/O0nEe4tJhdG62XU5aYmPNQ== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - fs-extra "^9.1.0" - p-map "^4.0.0" - write-json-file "^4.3.0" - -"@lerna/link@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/link/-/link-4.0.0.tgz#c3a38aabd44279d714e90f2451e31b63f0fb65ba" - integrity sha512-KlvPi7XTAcVOByfaLlOeYOfkkDcd+bejpHMCd1KcArcFTwijOwXOVi24DYomIeHvy6HsX/IUquJ4PPUJIeB4+w== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/package-graph" "4.0.0" - "@lerna/symlink-dependencies" "4.0.0" - p-map "^4.0.0" - slash "^3.0.0" - -"@lerna/list@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/list/-/list-4.0.0.tgz#24b4e6995bd73f81c556793fe502b847efd9d1d7" - integrity sha512-L2B5m3P+U4Bif5PultR4TI+KtW+SArwq1i75QZ78mRYxPc0U/piau1DbLOmwrdqr99wzM49t0Dlvl6twd7GHFg== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/listable" "4.0.0" - "@lerna/output" "4.0.0" - -"@lerna/listable@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-4.0.0.tgz#d00d6cb4809b403f2b0374fc521a78e318b01214" - integrity sha512-/rPOSDKsOHs5/PBLINZOkRIX1joOXUXEtyUs5DHLM8q6/RP668x/1lFhw6Dx7/U+L0+tbkpGtZ1Yt0LewCLgeQ== - dependencies: - "@lerna/query-graph" "4.0.0" - chalk "^4.1.0" - columnify "^1.5.4" - -"@lerna/log-packed@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/log-packed/-/log-packed-4.0.0.tgz#95168fe2e26ac6a71e42f4be857519b77e57a09f" - integrity sha512-+dpCiWbdzgMAtpajLToy9PO713IHoE6GV/aizXycAyA07QlqnkpaBNZ8DW84gHdM1j79TWockGJo9PybVhrrZQ== - dependencies: - byte-size "^7.0.0" - columnify "^1.5.4" - has-unicode "^2.0.1" - npmlog "^4.1.2" - -"@lerna/npm-conf@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-conf/-/npm-conf-4.0.0.tgz#b259fd1e1cee2bf5402b236e770140ff9ade7fd2" - integrity sha512-uS7H02yQNq3oejgjxAxqq/jhwGEE0W0ntr8vM3EfpCW1F/wZruwQw+7bleJQ9vUBjmdXST//tk8mXzr5+JXCfw== - dependencies: - config-chain "^1.1.12" - pify "^5.0.0" - -"@lerna/npm-dist-tag@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-4.0.0.tgz#d1e99b4eccd3414142f0548ad331bf2d53f3257a" - integrity sha512-F20sg28FMYTgXqEQihgoqSfwmq+Id3zT23CnOwD+XQMPSy9IzyLf1fFVH319vXIw6NF6Pgs4JZN2Qty6/CQXGw== - dependencies: - "@lerna/otplease" "4.0.0" - npm-package-arg "^8.1.0" - npm-registry-fetch "^9.0.0" - npmlog "^4.1.2" - -"@lerna/npm-install@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-install/-/npm-install-4.0.0.tgz#31180be3ab3b7d1818a1a0c206aec156b7094c78" - integrity sha512-aKNxq2j3bCH3eXl3Fmu4D54s/YLL9WSwV8W7X2O25r98wzrO38AUN6AB9EtmAx+LV/SP15et7Yueg9vSaanRWg== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/get-npm-exec-opts" "4.0.0" - fs-extra "^9.1.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - signal-exit "^3.0.3" - write-pkg "^4.0.0" - -"@lerna/npm-publish@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-4.0.0.tgz#84eb62e876fe949ae1fd62c60804423dbc2c4472" - integrity sha512-vQb7yAPRo5G5r77DRjHITc9piR9gvEKWrmfCH7wkfBnGWEqu7n8/4bFQ7lhnkujvc8RXOsYpvbMQkNfkYibD/w== - dependencies: - "@lerna/otplease" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - fs-extra "^9.1.0" - libnpmpublish "^4.0.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - pify "^5.0.0" - read-package-json "^3.0.0" - -"@lerna/npm-run-script@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-run-script/-/npm-run-script-4.0.0.tgz#dfebf4f4601442e7c0b5214f9fb0d96c9350743b" - integrity sha512-Jmyh9/IwXJjOXqKfIgtxi0bxi1pUeKe5bD3S81tkcy+kyng/GNj9WSqD5ZggoNP2NP//s4CLDAtUYLdP7CU9rA== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/get-npm-exec-opts" "4.0.0" - npmlog "^4.1.2" - -"@lerna/otplease@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/otplease/-/otplease-4.0.0.tgz#84972eb43448f8a1077435ba1c5e59233b725850" - integrity sha512-Sgzbqdk1GH4psNiT6hk+BhjOfIr/5KhGBk86CEfHNJTk9BK4aZYyJD4lpDbDdMjIV4g03G7pYoqHzH765T4fxw== - dependencies: - "@lerna/prompt" "4.0.0" - -"@lerna/output@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/output/-/output-4.0.0.tgz#b1d72215c0e35483e4f3e9994debc82c621851f2" - integrity sha512-Un1sHtO1AD7buDQrpnaYTi2EG6sLF+KOPEAMxeUYG5qG3khTs2Zgzq5WE3dt2N/bKh7naESt20JjIW6tBELP0w== - dependencies: - npmlog "^4.1.2" - -"@lerna/pack-directory@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/pack-directory/-/pack-directory-4.0.0.tgz#8b617db95d20792f043aaaa13a9ccc0e04cb4c74" - integrity sha512-NJrmZNmBHS+5aM+T8N6FVbaKFScVqKlQFJNY2k7nsJ/uklNKsLLl6VhTQBPwMTbf6Tf7l6bcKzpy7aePuq9UiQ== - dependencies: - "@lerna/get-packed" "4.0.0" - "@lerna/package" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - npm-packlist "^2.1.4" - npmlog "^4.1.2" - tar "^6.1.0" - temp-write "^4.0.0" - -"@lerna/package-graph@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-4.0.0.tgz#16a00253a8ac810f72041481cb46bcee8d8123dd" - integrity sha512-QED2ZCTkfXMKFoTGoccwUzjHtZMSf3UKX14A4/kYyBms9xfFsesCZ6SLI5YeySEgcul8iuIWfQFZqRw+Qrjraw== - dependencies: - "@lerna/prerelease-id-from-version" "4.0.0" - "@lerna/validation-error" "4.0.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - semver "^7.3.4" - -"@lerna/package@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/package/-/package-4.0.0.tgz#1b4c259c4bcff45c876ee1d591a043aacbc0d6b7" - integrity sha512-l0M/izok6FlyyitxiQKr+gZLVFnvxRQdNhzmQ6nRnN9dvBJWn+IxxpM+cLqGACatTnyo9LDzNTOj2Db3+s0s8Q== - dependencies: - load-json-file "^6.2.0" - npm-package-arg "^8.1.0" - write-pkg "^4.0.0" - -"@lerna/prerelease-id-from-version@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-4.0.0.tgz#c7e0676fcee1950d85630e108eddecdd5b48c916" - integrity sha512-GQqguzETdsYRxOSmdFZ6zDBXDErIETWOqomLERRY54f4p+tk4aJjoVdd9xKwehC9TBfIFvlRbL1V9uQGHh1opg== - dependencies: - semver "^7.3.4" - -"@lerna/profiler@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/profiler/-/profiler-4.0.0.tgz#8a53ab874522eae15d178402bff90a14071908e9" - integrity sha512-/BaEbqnVh1LgW/+qz8wCuI+obzi5/vRE8nlhjPzdEzdmWmZXuCKyWSEzAyHOJWw1ntwMiww5dZHhFQABuoFz9Q== - dependencies: - fs-extra "^9.1.0" - npmlog "^4.1.2" - upath "^2.0.1" - -"@lerna/project@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/project/-/project-4.0.0.tgz#ff84893935833533a74deff30c0e64ddb7f0ba6b" - integrity sha512-o0MlVbDkD5qRPkFKlBZsXZjoNTWPyuL58564nSfZJ6JYNmgAptnWPB2dQlAc7HWRZkmnC2fCkEdoU+jioPavbg== - dependencies: - "@lerna/package" "4.0.0" - "@lerna/validation-error" "4.0.0" - cosmiconfig "^7.0.0" - dedent "^0.7.0" - dot-prop "^6.0.1" - glob-parent "^5.1.1" - globby "^11.0.2" - load-json-file "^6.2.0" - npmlog "^4.1.2" - p-map "^4.0.0" - resolve-from "^5.0.0" - write-json-file "^4.3.0" - -"@lerna/prompt@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-4.0.0.tgz#5ec69a803f3f0db0ad9f221dad64664d3daca41b" - integrity sha512-4Ig46oCH1TH5M7YyTt53fT6TuaKMgqUUaqdgxvp6HP6jtdak6+amcsqB8YGz2eQnw/sdxunx84DfI9XpoLj4bQ== - dependencies: - inquirer "^7.3.3" - npmlog "^4.1.2" - -"@lerna/publish@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-4.0.0.tgz#f67011305adeba120066a3b6d984a5bb5fceef65" - integrity sha512-K8jpqjHrChH22qtkytA5GRKIVFEtqBF6JWj1I8dWZtHs4Jywn8yB1jQ3BAMLhqmDJjWJtRck0KXhQQKzDK2UPg== - dependencies: - "@lerna/check-working-tree" "4.0.0" - "@lerna/child-process" "4.0.0" - "@lerna/collect-updates" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/describe-ref" "4.0.0" - "@lerna/log-packed" "4.0.0" - "@lerna/npm-conf" "4.0.0" - "@lerna/npm-dist-tag" "4.0.0" - "@lerna/npm-publish" "4.0.0" - "@lerna/otplease" "4.0.0" - "@lerna/output" "4.0.0" - "@lerna/pack-directory" "4.0.0" - "@lerna/prerelease-id-from-version" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/validation-error" "4.0.0" - "@lerna/version" "4.0.0" - fs-extra "^9.1.0" - libnpmaccess "^4.0.1" - npm-package-arg "^8.1.0" - npm-registry-fetch "^9.0.0" - npmlog "^4.1.2" - p-map "^4.0.0" - p-pipe "^3.1.0" - pacote "^11.2.6" - semver "^7.3.4" - -"@lerna/pulse-till-done@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/pulse-till-done/-/pulse-till-done-4.0.0.tgz#04bace7d483a8205c187b806bcd8be23d7bb80a3" - integrity sha512-Frb4F7QGckaybRhbF7aosLsJ5e9WuH7h0KUkjlzSByVycxY91UZgaEIVjS2oN9wQLrheLMHl6SiFY0/Pvo0Cxg== - dependencies: - npmlog "^4.1.2" - -"@lerna/query-graph@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/query-graph/-/query-graph-4.0.0.tgz#09dd1c819ac5ee3f38db23931143701f8a6eef63" - integrity sha512-YlP6yI3tM4WbBmL9GCmNDoeQyzcyg1e4W96y/PKMZa5GbyUvkS2+Jc2kwPD+5KcXou3wQZxSPzR3Te5OenaDdg== - dependencies: - "@lerna/package-graph" "4.0.0" - -"@lerna/resolve-symlink@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/resolve-symlink/-/resolve-symlink-4.0.0.tgz#6d006628a210c9b821964657a9e20a8c9a115e14" - integrity sha512-RtX8VEUzqT+uLSCohx8zgmjc6zjyRlh6i/helxtZTMmc4+6O4FS9q5LJas2uGO2wKvBlhcD6siibGt7dIC3xZA== - dependencies: - fs-extra "^9.1.0" - npmlog "^4.1.2" - read-cmd-shim "^2.0.0" - -"@lerna/rimraf-dir@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/rimraf-dir/-/rimraf-dir-4.0.0.tgz#2edf3b62d4eb0ef4e44e430f5844667d551ec25a" - integrity sha512-QNH9ABWk9mcMJh2/muD9iYWBk1oQd40y6oH+f3wwmVGKYU5YJD//+zMiBI13jxZRtwBx0vmBZzkBkK1dR11cBg== - dependencies: - "@lerna/child-process" "4.0.0" - npmlog "^4.1.2" - path-exists "^4.0.0" - rimraf "^3.0.2" - -"@lerna/run-lifecycle@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/run-lifecycle/-/run-lifecycle-4.0.0.tgz#e648a46f9210a9bcd7c391df6844498cb5079334" - integrity sha512-IwxxsajjCQQEJAeAaxF8QdEixfI7eLKNm4GHhXHrgBu185JcwScFZrj9Bs+PFKxwb+gNLR4iI5rpUdY8Y0UdGQ== - dependencies: - "@lerna/npm-conf" "4.0.0" - npm-lifecycle "^3.1.5" - npmlog "^4.1.2" - -"@lerna/run-topologically@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/run-topologically/-/run-topologically-4.0.0.tgz#af846eeee1a09b0c2be0d1bfb5ef0f7b04bb1827" - integrity sha512-EVZw9hGwo+5yp+VL94+NXRYisqgAlj0jWKWtAIynDCpghRxCE5GMO3xrQLmQgqkpUl9ZxQFpICgYv5DW4DksQA== - dependencies: - "@lerna/query-graph" "4.0.0" - p-queue "^6.6.2" - -"@lerna/run@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/run/-/run-4.0.0.tgz#4bc7fda055a729487897c23579694f6183c91262" - integrity sha512-9giulCOzlMPzcZS/6Eov6pxE9gNTyaXk0Man+iCIdGJNMrCnW7Dme0Z229WWP/UoxDKg71F2tMsVVGDiRd8fFQ== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/npm-run-script" "4.0.0" - "@lerna/output" "4.0.0" - "@lerna/profiler" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/timer" "4.0.0" - "@lerna/validation-error" "4.0.0" - p-map "^4.0.0" - -"@lerna/symlink-binary@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/symlink-binary/-/symlink-binary-4.0.0.tgz#21009f62d53a425f136cb4c1a32c6b2a0cc02d47" - integrity sha512-zualodWC4q1QQc1pkz969hcFeWXOsVYZC5AWVtAPTDfLl+TwM7eG/O6oP+Rr3fFowspxo6b1TQ6sYfDV6HXNWA== - dependencies: - "@lerna/create-symlink" "4.0.0" - "@lerna/package" "4.0.0" - fs-extra "^9.1.0" - p-map "^4.0.0" - -"@lerna/symlink-dependencies@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/symlink-dependencies/-/symlink-dependencies-4.0.0.tgz#8910eca084ae062642d0490d8972cf2d98e9ebbd" - integrity sha512-BABo0MjeUHNAe2FNGty1eantWp8u83BHSeIMPDxNq0MuW2K3CiQRaeWT3EGPAzXpGt0+hVzBrA6+OT0GPn7Yuw== - dependencies: - "@lerna/create-symlink" "4.0.0" - "@lerna/resolve-symlink" "4.0.0" - "@lerna/symlink-binary" "4.0.0" - fs-extra "^9.1.0" - p-map "^4.0.0" - p-map-series "^2.1.0" - -"@lerna/timer@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/timer/-/timer-4.0.0.tgz#a52e51bfcd39bfd768988049ace7b15c1fd7a6da" - integrity sha512-WFsnlaE7SdOvjuyd05oKt8Leg3ENHICnvX3uYKKdByA+S3g+TCz38JsNs7OUZVt+ba63nC2nbXDlUnuT2Xbsfg== - -"@lerna/validation-error@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/validation-error/-/validation-error-4.0.0.tgz#af9d62fe8304eaa2eb9a6ba1394f9aa807026d35" - integrity sha512-1rBOM5/koiVWlRi3V6dB863E1YzJS8v41UtsHgMr6gB2ncJ2LsQtMKlJpi3voqcgh41H8UsPXR58RrrpPpufyw== - dependencies: - npmlog "^4.1.2" - -"@lerna/version@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/version/-/version-4.0.0.tgz#532659ec6154d8a8789c5ab53878663e244e3228" - integrity sha512-otUgiqs5W9zGWJZSCCMRV/2Zm2A9q9JwSDS7s/tlKq4mWCYriWo7+wsHEA/nPTMDyYyBO5oyZDj+3X50KDUzeA== - dependencies: - "@lerna/check-working-tree" "4.0.0" - "@lerna/child-process" "4.0.0" - "@lerna/collect-updates" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/conventional-commits" "4.0.0" - "@lerna/github-client" "4.0.0" - "@lerna/gitlab-client" "4.0.0" - "@lerna/output" "4.0.0" - "@lerna/prerelease-id-from-version" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/validation-error" "4.0.0" - chalk "^4.1.0" - dedent "^0.7.0" - load-json-file "^6.2.0" - minimatch "^3.0.4" - npmlog "^4.1.2" - p-map "^4.0.0" - p-pipe "^3.1.0" - p-reduce "^2.1.0" - p-waterfall "^2.1.1" - semver "^7.3.4" - slash "^3.0.0" - temp-write "^4.0.0" - write-json-file "^4.3.0" - -"@lerna/write-log-file@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/write-log-file/-/write-log-file-4.0.0.tgz#18221a38a6a307d6b0a5844dd592ad53fa27091e" - integrity sha512-XRG5BloiArpXRakcnPHmEHJp+4AtnhRtpDIHSghmXD5EichI1uD73J7FgPp30mm2pDRq3FdqB0NbwSEsJ9xFQg== - dependencies: - npmlog "^4.1.2" - write-file-atomic "^3.0.3" - -"@mapbox/node-pre-gyp@1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc" - integrity sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - -"@mapbox/node-pre-gyp@^1.0.10": - version "1.0.10" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c" - integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - -"@mattrglobal/bbs-signatures@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.0.0.tgz#8ff272c6d201aadab7e08bd84dbfd6e0d48ba12d" - integrity sha512-FFzybdKqSCrS/e7pl5s6Tl/m/x8ZD5EMBbcTBQaqSOms/lebm91lFukYOIe2qc0a5o+gLhtRKye8OfKwD1Ex/g== - dependencies: - "@stablelib/random" "1.0.0" - optionalDependencies: - "@mattrglobal/node-bbs-signatures" "0.13.0" - -"@mattrglobal/bbs-signatures@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-1.1.0.tgz#031418c6a003d1782e3aff95995bda6a0605f9f1" - integrity sha512-uf74y3S7kAxL3FZrva6u+BF3VY3El5QI9IMkyBCoNoJvO+nWJewmTqLMLWEh6QJ1N5egZfDCI4PuS9ISrZJTZg== - dependencies: - "@stablelib/random" "1.0.0" - optionalDependencies: - "@mattrglobal/node-bbs-signatures" "0.15.0" - -"@mattrglobal/bls12381-key-pair@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/bls12381-key-pair/-/bls12381-key-pair-1.0.0.tgz#2959f8663595de0209bebe88517235ae34f1e2b1" - integrity sha512-FbvSkoy1n3t5FHtAPj8cyQJL7Bz+hvvmquCBZW2+bOBBBT26JhGtr//s6EmXE9e4EZk7bAA1yMHI6i1Ky2us0Q== - dependencies: - "@mattrglobal/bbs-signatures" "1.0.0" - bs58 "4.0.1" - rfc4648 "1.4.0" - -"@mattrglobal/node-bbs-signatures@0.13.0": - version "0.13.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/node-bbs-signatures/-/node-bbs-signatures-0.13.0.tgz#3e431b915325d4b139706f8b26fd84b27c192a29" - integrity sha512-S2wOwDCQYxdjSEjVfcbP3bTq4ZMKeRw/wvBhWRff8CEwuH5u3Qiul+azwDGSesvve1DDceaEhXWiGkXeZTojfQ== - dependencies: - neon-cli "0.8.2" - node-pre-gyp "0.17.0" + neon-cli "0.8.2" + node-pre-gyp "0.17.0" "@mattrglobal/node-bbs-signatures@0.15.0": version "0.15.0" @@ -1941,7 +1354,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -1949,10 +1362,45 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@npmcli/ci-detect@^1.0.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@npmcli/ci-detect/-/ci-detect-1.4.0.tgz#18478bbaa900c37bfbd8a2006a6262c62e8b0fe1" - integrity sha512-3BGrt6FLjqM6br5AhWRKTr3u5GIVkjRYeAFrMp3HjnfICrg4xOrVRwFavKT6tsp++bq5dluL5t8ME/Nha/6c1Q== +"@npmcli/arborist@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-5.3.0.tgz#321d9424677bfc08569e98a5ac445ee781f32053" + integrity sha512-+rZ9zgL1lnbl8Xbb1NQdMjveOMwj4lIYfcDtyJHHi5x4X8jtR6m8SXooJMZy5vmFVZ8w7A2Bnd/oX9eTuU8w5A== + dependencies: + "@isaacs/string-locale-compare" "^1.1.0" + "@npmcli/installed-package-contents" "^1.0.7" + "@npmcli/map-workspaces" "^2.0.3" + "@npmcli/metavuln-calculator" "^3.0.1" + "@npmcli/move-file" "^2.0.0" + "@npmcli/name-from-folder" "^1.0.1" + "@npmcli/node-gyp" "^2.0.0" + "@npmcli/package-json" "^2.0.0" + "@npmcli/run-script" "^4.1.3" + bin-links "^3.0.0" + cacache "^16.0.6" + common-ancestor-path "^1.0.1" + json-parse-even-better-errors "^2.3.1" + json-stringify-nice "^1.1.4" + mkdirp "^1.0.4" + mkdirp-infer-owner "^2.0.0" + nopt "^5.0.0" + npm-install-checks "^5.0.0" + npm-package-arg "^9.0.0" + npm-pick-manifest "^7.0.0" + npm-registry-fetch "^13.0.0" + npmlog "^6.0.2" + pacote "^13.6.1" + parse-conflict-json "^2.0.1" + proc-log "^2.0.0" + promise-all-reject-late "^1.0.0" + promise-call-limit "^1.0.1" + read-package-json-fast "^2.0.2" + readdir-scoped-modules "^1.1.0" + rimraf "^3.0.2" + semver "^7.3.7" + ssri "^9.0.0" + treeverse "^2.0.0" + walk-up-path "^1.0.0" "@npmcli/fs@^1.0.0": version "1.1.1" @@ -1962,21 +1410,30 @@ "@gar/promisify" "^1.0.1" semver "^7.3.5" -"@npmcli/git@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" - integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw== +"@npmcli/fs@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" + integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== dependencies: - "@npmcli/promise-spawn" "^1.3.2" - lru-cache "^6.0.0" + "@gar/promisify" "^1.1.3" + semver "^7.3.5" + +"@npmcli/git@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-3.0.2.tgz#5c5de6b4d70474cf2d09af149ce42e4e1dacb931" + integrity sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w== + dependencies: + "@npmcli/promise-spawn" "^3.0.0" + lru-cache "^7.4.4" mkdirp "^1.0.4" - npm-pick-manifest "^6.1.1" + npm-pick-manifest "^7.0.0" + proc-log "^2.0.0" promise-inflight "^1.0.1" promise-retry "^2.0.1" semver "^7.3.5" which "^2.0.2" -"@npmcli/installed-package-contents@^1.0.6": +"@npmcli/installed-package-contents@^1.0.7": version "1.0.7" resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== @@ -1984,6 +1441,26 @@ npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" +"@npmcli/map-workspaces@^2.0.3": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-2.0.4.tgz#9e5e8ab655215a262aefabf139782b894e0504fc" + integrity sha512-bMo0aAfwhVwqoVM5UzX1DJnlvVvzDCHae821jv48L1EsrYwfOZChlqWYXEtto/+BkBXetPbEWgau++/brh4oVg== + dependencies: + "@npmcli/name-from-folder" "^1.0.1" + glob "^8.0.1" + minimatch "^5.0.1" + read-package-json-fast "^2.0.3" + +"@npmcli/metavuln-calculator@^3.0.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/metavuln-calculator/-/metavuln-calculator-3.1.1.tgz#9359bd72b400f8353f6a28a25c8457b562602622" + integrity sha512-n69ygIaqAedecLeVH3KnO39M6ZHiJ2dEv5A7DGvcqCB8q17BGUgW8QaanIkbWUo2aYGZqJaOORTLAlIvKjNDKA== + dependencies: + cacache "^16.0.0" + json-parse-even-better-errors "^2.3.1" + pacote "^13.0.3" + semver "^7.3.5" + "@npmcli/move-file@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" @@ -1992,64 +1469,167 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@npmcli/node-gyp@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz#a912e637418ffc5f2db375e93b85837691a43a33" - integrity sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA== +"@npmcli/move-file@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" + integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" -"@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" - integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== +"@npmcli/name-from-folder@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz#77ecd0a4fcb772ba6fe927e2e2e155fbec2e6b1a" + integrity sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA== + +"@npmcli/node-gyp@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz#8c20e53e34e9078d18815c1d2dda6f2420d75e35" + integrity sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A== + +"@npmcli/package-json@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-2.0.0.tgz#3bbcf4677e21055adbe673d9f08c9f9cde942e4a" + integrity sha512-42jnZ6yl16GzjWSH7vtrmWyJDGVa/LXPdpN2rcUWolFjc9ON2N3uz0qdBbQACfmhuJZ2lbKYtmK5qx68ZPLHMA== + dependencies: + json-parse-even-better-errors "^2.3.1" + +"@npmcli/promise-spawn@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz#53283b5f18f855c6925f23c24e67c911501ef573" + integrity sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g== dependencies: infer-owner "^1.0.4" -"@npmcli/run-script@^1.8.2": - version "1.8.6" - resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.6.tgz#18314802a6660b0d4baa4c3afe7f1ad39d8c28b7" - integrity sha512-e42bVZnC6VluBZBAFEr3YrdqSspG3bgilyg4nSLBJ7TRGNCzxHa92XAHxQBLYg0BmgwO4b2mf3h/l5EkEWRn3g== +"@npmcli/run-script@4.1.7": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-4.1.7.tgz#b1a2f57568eb738e45e9ea3123fb054b400a86f7" + integrity sha512-WXr/MyM4tpKA4BotB81NccGAv8B48lNH0gRoILucbcAhTQXLCoi6HflMV3KdXubIqvP9SuLsFn68Z7r4jl+ppw== dependencies: - "@npmcli/node-gyp" "^1.0.2" - "@npmcli/promise-spawn" "^1.3.2" - node-gyp "^7.1.0" - read-package-json-fast "^2.0.1" + "@npmcli/node-gyp" "^2.0.0" + "@npmcli/promise-spawn" "^3.0.0" + node-gyp "^9.0.0" + read-package-json-fast "^2.0.3" + which "^2.0.2" -"@octokit/auth-token@^2.4.4": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" - integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== +"@npmcli/run-script@^4.1.0", "@npmcli/run-script@^4.1.3": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-4.2.1.tgz#c07c5c71bc1c70a5f2a06b0d4da976641609b946" + integrity sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg== dependencies: - "@octokit/types" "^6.0.3" + "@npmcli/node-gyp" "^2.0.0" + "@npmcli/promise-spawn" "^3.0.0" + node-gyp "^9.0.0" + read-package-json-fast "^2.0.3" + which "^2.0.2" -"@octokit/core@^3.5.1": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085" - integrity sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.6.3" - "@octokit/request-error" "^2.0.5" - "@octokit/types" "^6.0.3" +"@nrwl/cli@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.8.7.tgz#1de7ba802de24edac64e8cb4cac1459a3f403505" + integrity sha512-G1NEy4jGuZJ/7KjhLQNOe11XmoTgwJS82FW8Tbo4iceq2ItSEbe7bkA8xTSK/AzUixZIMimztb9Oyxw/n1ajGQ== + dependencies: + nx "15.8.7" + +"@nrwl/devkit@>=15.5.2 < 16": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-15.8.7.tgz#98e881e993c1314a20c050926df1466154782e58" + integrity sha512-A99nZrA5KN9wRn2uYX2vKByA+t2XEGoZBR5TU/bpXbPYrh92qAHkIJ8ke3ImGQOlzk4iIaZ5Me0k7k1p9Zx4wA== + dependencies: + "@phenomnomnominal/tsquery" "4.1.1" + ejs "^3.1.7" + ignore "^5.0.4" + semver "7.3.4" + tmp "~0.2.1" + tslib "^2.3.0" + +"@nrwl/nx-darwin-arm64@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.8.7.tgz#1fed566b5206afd710309079644782997ccb7895" + integrity sha512-+cu8J337gRxUHjz2TGwS/2Oh3yw8d3/T6SoBfvee1DY72VQaeYd8UTz0doOhDtmc/zowvRu7ZVsW0ytNB0jIXQ== + +"@nrwl/nx-darwin-x64@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.8.7.tgz#7aaee9f56fa526e7049fa5a9829fa72044b35055" + integrity sha512-VqHJEP0wgFu1MU0Bo1vKZ5/s7ThRfYkX8SyGUxjVTzR02CrsjC4rNxFoKD8Cc4YkUn44U/F78toGf+i2gRcjSQ== + +"@nrwl/nx-linux-arm-gnueabihf@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.8.7.tgz#379a77ea46e0f741c487eeedd3389eafab26dcae" + integrity sha512-4F/8awwqPTt7zKQolvjBNrcR1wYicPjGchLOdaqnfMxn/iRRUdh0hD11mEP5zHNv9gZs/nOIvhdBUErNjFkplQ== + +"@nrwl/nx-linux-arm64-gnu@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.8.7.tgz#201a41c0c8531de94169faa48bd9a49bed04ec4b" + integrity sha512-3ZTSZx02Vv5emQOpaDROIcLtQucoXAe73zGKYDTXB95mxbOPSjjQJ8Rtx+BeqWq9JQoZZyRcD0qnBkTTy1aLRg== + +"@nrwl/nx-linux-arm64-musl@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.8.7.tgz#f6bbf2e7a1941952c25387a36be6cfa88079975d" + integrity sha512-SZxTomiHxAh8El+swbmGSGcaA0vGbHb/rmhFAixo19INu1wBJfD6hjkVJt17h6PyEO7BIYPOpRia6Poxnyv8hA== + +"@nrwl/nx-linux-x64-gnu@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.8.7.tgz#76a88784858224a720c5a28e40ad513704f45722" + integrity sha512-BlNC6Zz1/x6CFbBFTVrgRGMOPqb7zWh5cOjBVNpoBXYTEth1UXb2r1U+gpuQ4xdUqG+uXoWhy6BHJjqBIjzLJA== + +"@nrwl/nx-linux-x64-musl@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.8.7.tgz#dd906423fa129d0c55633ebe80572bdd6be4d57f" + integrity sha512-FNYX/IKy8SUbw6bJpvwZrup2YQBYmSJwP6Rw76Vf7c32XHk7uA6AjiPWMIrZCSndXcry8fnwXvR+J2Dnyo82nQ== + +"@nrwl/nx-win32-arm64-msvc@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.8.7.tgz#145e415950d8ff507dcfbd7879f9c37477e7a620" + integrity sha512-sZALEzazjPAeLlw6IbFWsMidCZ4ZM3GKWZZ6rsAqG2y7I9t4nlUPH/y/Isl9MuLBvrBCBXbVnD20wh6EhtuwTw== + +"@nrwl/nx-win32-x64-msvc@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.8.7.tgz#66aa3cda4b9ae7b676d2282fbac129ce7a3c15d0" + integrity sha512-VMdDptI2rqkLQRCvertF29QeA/V/MnFtHbsmVzMCEv5EUfrkHbA5LLxV66LLfngmkDT1FHktffztlsMpbxvhRw== + +"@nrwl/tao@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.8.7.tgz#ea0bd4bc1784a2578dfb7cfb93f42d98504344cb" + integrity sha512-wA7QIEh0VwWcyo32Y/xSCTwnQTGcZupe933nResXv8mAb36W8MoR5SXRx+Wdd8fJ1eWlm2tuotIrslhN+lYx/Q== + dependencies: + nx "15.8.7" + +"@octokit/auth-token@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.3.tgz#ce7e48a3166731f26068d7a7a7996b5da58cbe0c" + integrity sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA== + dependencies: + "@octokit/types" "^9.0.0" + +"@octokit/core@^4.0.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.2.0.tgz#8c253ba9605aca605bc46187c34fcccae6a96648" + integrity sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg== + dependencies: + "@octokit/auth-token" "^3.0.0" + "@octokit/graphql" "^5.0.0" + "@octokit/request" "^6.0.0" + "@octokit/request-error" "^3.0.0" + "@octokit/types" "^9.0.0" before-after-hook "^2.2.0" universal-user-agent "^6.0.0" -"@octokit/endpoint@^6.0.1": - version "6.0.12" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" - integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== +"@octokit/endpoint@^7.0.0": + version "7.0.5" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.5.tgz#2bb2a911c12c50f10014183f5d596ce30ac67dd1" + integrity sha512-LG4o4HMY1Xoaec87IqQ41TQ+glvIeTKqfjkCEmt5AIwDZJwQeVZFIEYXrYY6yLwK+pAScb9Gj4q+Nz2qSw1roA== dependencies: - "@octokit/types" "^6.0.3" + "@octokit/types" "^9.0.0" is-plain-object "^5.0.0" universal-user-agent "^6.0.0" -"@octokit/graphql@^4.5.8": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" - integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== +"@octokit/graphql@^5.0.0": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.5.tgz#a4cb3ea73f83b861893a6370ee82abb36e81afd2" + integrity sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ== dependencies: - "@octokit/request" "^5.6.0" - "@octokit/types" "^6.0.3" + "@octokit/request" "^6.0.0" + "@octokit/types" "^9.0.0" universal-user-agent "^6.0.0" "@octokit/openapi-types@^12.11.0": @@ -2057,73 +1637,105 @@ resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== -"@octokit/plugin-enterprise-rest@^6.0.1": +"@octokit/openapi-types@^14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-14.0.0.tgz#949c5019028c93f189abbc2fb42f333290f7134a" + integrity sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw== + +"@octokit/openapi-types@^16.0.0": + version "16.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-16.0.0.tgz#d92838a6cd9fb4639ca875ddb3437f1045cc625e" + integrity sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA== + +"@octokit/plugin-enterprise-rest@6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== -"@octokit/plugin-paginate-rest@^2.16.8": - version "2.21.3" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e" - integrity sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw== +"@octokit/plugin-paginate-rest@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-3.1.0.tgz#86f8be759ce2d6d7c879a31490fd2f7410b731f0" + integrity sha512-+cfc40pMzWcLkoDcLb1KXqjX0jTGYXjKuQdFQDc6UAknISJHnZTiBqld6HDwRJvD4DsouDKrWXNbNV0lE/3AXA== dependencies: - "@octokit/types" "^6.40.0" + "@octokit/types" "^6.41.0" "@octokit/plugin-request-log@^1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== -"@octokit/plugin-rest-endpoint-methods@^5.12.0": - version "5.16.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" - integrity sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw== +"@octokit/plugin-rest-endpoint-methods@^6.0.0": + version "6.8.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.8.1.tgz#97391fda88949eb15f68dc291957ccbe1d3e8ad1" + integrity sha512-QrlaTm8Lyc/TbU7BL/8bO49vp+RZ6W3McxxmmQTgYxf2sWkO8ZKuj4dLhPNJD6VCUW1hetCmeIM0m6FTVpDiEg== dependencies: - "@octokit/types" "^6.39.0" + "@octokit/types" "^8.1.1" deprecation "^2.3.1" -"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" - integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== +"@octokit/request-error@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-3.0.3.tgz#ef3dd08b8e964e53e55d471acfe00baa892b9c69" + integrity sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ== dependencies: - "@octokit/types" "^6.0.3" + "@octokit/types" "^9.0.0" deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.6.0", "@octokit/request@^5.6.3": - version "5.6.3" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" - integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== +"@octokit/request@^6.0.0": + version "6.2.3" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-6.2.3.tgz#76d5d6d44da5c8d406620a4c285d280ae310bdb4" + integrity sha512-TNAodj5yNzrrZ/VxP+H5HiYaZep0H3GU0O7PaF+fhDrt8FPrnkei9Aal/txsN/1P7V3CPiThG0tIvpPDYUsyAA== dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.1.0" - "@octokit/types" "^6.16.1" + "@octokit/endpoint" "^7.0.0" + "@octokit/request-error" "^3.0.0" + "@octokit/types" "^9.0.0" is-plain-object "^5.0.0" node-fetch "^2.6.7" universal-user-agent "^6.0.0" -"@octokit/rest@^18.1.0": - version "18.12.0" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" - integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== +"@octokit/rest@19.0.3": + version "19.0.3" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-19.0.3.tgz#b9a4e8dc8d53e030d611c053153ee6045f080f02" + integrity sha512-5arkTsnnRT7/sbI4fqgSJ35KiFaN7zQm0uQiQtivNQLI8RQx8EHwJCajcTUwmaCMNDg7tdCvqAnc7uvHHPxrtQ== dependencies: - "@octokit/core" "^3.5.1" - "@octokit/plugin-paginate-rest" "^2.16.8" + "@octokit/core" "^4.0.0" + "@octokit/plugin-paginate-rest" "^3.0.0" "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^5.12.0" + "@octokit/plugin-rest-endpoint-methods" "^6.0.0" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": +"@octokit/types@^6.41.0": version "6.41.0" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== dependencies: "@octokit/openapi-types" "^12.11.0" +"@octokit/types@^8.1.1": + version "8.2.1" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-8.2.1.tgz#a6de091ae68b5541f8d4fcf9a12e32836d4648aa" + integrity sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw== + dependencies: + "@octokit/openapi-types" "^14.0.0" + +"@octokit/types@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.0.0.tgz#6050db04ddf4188ec92d60e4da1a2ce0633ff635" + integrity sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw== + dependencies: + "@octokit/openapi-types" "^16.0.0" + +"@parcel/watcher@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.4.tgz#f300fef4cc38008ff4b8c29d92588eced3ce014b" + integrity sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg== + dependencies: + node-addon-api "^3.2.1" + node-gyp-build "^4.3.0" + "@peculiar/asn1-schema@^2.1.6", "@peculiar/asn1-schema@^2.3.0": - version "2.3.3" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.3.tgz#21418e1f3819e0b353ceff0c2dad8ccb61acd777" - integrity sha512-6GptMYDMyWBHTUKndHaDsRZUO/XMSgIns2krxcm2L7SEExRHwawFvSwNBhqNPR9HJwv3MruAiF1bhN0we6j6GQ== + version "2.3.6" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz#3dd3c2ade7f702a9a94dfb395c192f5fa5d6b922" + integrity sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA== dependencies: asn1js "^3.0.5" pvtsutils "^1.3.2" @@ -2141,150 +1753,219 @@ resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.1.tgz#821493bd5ad0f05939bd5f53b28536f68158360a" integrity sha512-eK4C6WTNYxoI7JOabMoZICiyqRRtJB220bh0Mbj5RwRycleZf9BPyZoxsTvpP0FpmVS2aS13NKOuh5/tN3sIRw== dependencies: - "@peculiar/asn1-schema" "^2.3.0" - "@peculiar/json-schema" "^1.1.12" - pvtsutils "^1.3.2" - tslib "^2.4.1" - webcrypto-core "^1.7.4" + "@peculiar/asn1-schema" "^2.3.0" + "@peculiar/json-schema" "^1.1.12" + pvtsutils "^1.3.2" + tslib "^2.4.1" + webcrypto-core "^1.7.4" + +"@phenomnomnominal/tsquery@4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@phenomnomnominal/tsquery/-/tsquery-4.1.1.tgz#42971b83590e9d853d024ddb04a18085a36518df" + integrity sha512-jjMmK1tnZbm1Jq5a7fBliM4gQwjxMU7TFoRNwIyzwlO+eHPRCFv/Nv+H/Gi1jc3WR7QURG8D5d0Tn12YGrUqBQ== + dependencies: + esquery "^1.0.1" + +"@pkgr/utils@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.3.1.tgz#0a9b06ffddee364d6642b3cd562ca76f55b34a03" + integrity sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw== + dependencies: + cross-spawn "^7.0.3" + is-glob "^4.0.3" + open "^8.4.0" + picocolors "^1.0.0" + tiny-glob "^0.2.9" + tslib "^2.4.0" + +"@react-native-community/cli-clean@^10.1.1": + version "10.1.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-10.1.1.tgz#4c73ce93a63a24d70c0089d4025daac8184ff504" + integrity sha512-iNsrjzjIRv9yb5y309SWJ8NDHdwYtnCpmxZouQDyOljUdC9MwdZ4ChbtA4rwQyAwgOVfS9F/j56ML3Cslmvrxg== + dependencies: + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" + execa "^1.0.0" + prompts "^2.4.0" + +"@react-native-community/cli-config@^10.1.1": + version "10.1.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-config/-/cli-config-10.1.1.tgz#08dcc5d7ca1915647dc06507ed853fe0c1488395" + integrity sha512-p4mHrjC+s/ayiNVG6T35GdEGdP6TuyBUg5plVGRJfTl8WT6LBfLYLk+fz/iETrEZ/YkhQIsQcEUQC47MqLNHog== + dependencies: + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" + cosmiconfig "^5.1.0" + deepmerge "^3.2.0" + glob "^7.1.3" + joi "^17.2.1" -"@react-native-community/cli-debugger-ui@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1.tgz#6b1f3367b8e5211e899983065ea2e72c1901d75f" - integrity sha512-5gGKaaXYOVE423BUqxIfvfAVSj5Cg1cU/TpGbeg/iqpy2CfqyWqJB3tTuVUbOOiOvR5wbU8tti6pIi1pchJ+oA== +"@react-native-community/cli-debugger-ui@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-10.0.0.tgz#4bb6d41c7e46449714dc7ba5d9f5b41ef0ea7c57" + integrity sha512-8UKLcvpSNxnUTRy8CkCl27GGLqZunQ9ncGYhSrWyKrU9SWBJJGeZwi2k2KaoJi5FvF2+cD0t8z8cU6lsq2ZZmA== dependencies: serve-static "^1.13.1" -"@react-native-community/cli-hermes@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-5.0.1.tgz#039d064bf2dcd5043beb7dcd6cdf5f5cdd51e7fc" - integrity sha512-nD+ZOFvu5MfjLB18eDJ01MNiFrzj8SDtENjGpf0ZRFndOWASDAmU54/UlU/wj8OzTToK1+S1KY7j2P2M1gleww== +"@react-native-community/cli-doctor@^10.2.0": + version "10.2.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-10.2.1.tgz#b6b7a3f0f9cef1a05f1adc6393eb29c6f8f2972c" + integrity sha512-IwhdSD+mtgWdxg2eMr0fpkn08XN7r70DC1riGSmqK/DXNyWBzIZlCkDN+/TwlaUEsiFk6LQTjgCiqZSMpmDrsg== + dependencies: + "@react-native-community/cli-config" "^10.1.1" + "@react-native-community/cli-platform-ios" "^10.2.1" + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" + command-exists "^1.2.8" + envinfo "^7.7.2" + execa "^1.0.0" + hermes-profile-transformer "^0.0.6" + ip "^1.1.5" + node-stream-zip "^1.9.1" + ora "^5.4.1" + prompts "^2.4.0" + semver "^6.3.0" + strip-ansi "^5.2.0" + sudo-prompt "^9.0.0" + wcwidth "^1.0.1" + +"@react-native-community/cli-hermes@^10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-10.2.0.tgz#cc252f435b149f74260bc918ce22fdf58033a87e" + integrity sha512-urfmvNeR8IiO/Sd92UU3xPO+/qI2lwCWQnxOkWaU/i2EITFekE47MD6MZrfVulRVYRi5cuaFqKZO/ccOdOB/vQ== dependencies: - "@react-native-community/cli-platform-android" "^5.0.1" - "@react-native-community/cli-tools" "^5.0.1" - chalk "^3.0.0" + "@react-native-community/cli-platform-android" "^10.2.0" + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" hermes-profile-transformer "^0.0.6" ip "^1.1.5" -"@react-native-community/cli-platform-android@^5.0.1", "@react-native-community/cli-platform-android@^5.0.1-alpha.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-5.0.1.tgz#7f761e1818e5a099877ec59a1b739553fd6a6905" - integrity sha512-qv9GJX6BJ+Y4qvV34vgxKwwN1cnveXUdP6y2YmTW7XoAYs5YUzKqHajpY58EyucAL2y++6+573t5y4U/9IIoww== +"@react-native-community/cli-platform-android@10.2.0", "@react-native-community/cli-platform-android@^10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-10.2.0.tgz#0bc689270a5f1d9aaf9e723181d43ca4dbfffdef" + integrity sha512-CBenYwGxwFdObZTn1lgxWtMGA5ms2G/ALQhkS+XTAD7KHDrCxFF9yT/fnAjFZKM6vX/1TqGI1RflruXih3kAhw== dependencies: - "@react-native-community/cli-tools" "^5.0.1" - chalk "^3.0.0" + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" execa "^1.0.0" - fs-extra "^8.1.0" glob "^7.1.3" - jetifier "^1.6.2" - lodash "^4.17.15" logkitty "^0.7.1" - slash "^3.0.0" - xmldoc "^1.1.2" -"@react-native-community/cli-platform-ios@^5.0.1-alpha.1": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.2.tgz#62485534053c0dad28a67de188248de177f4b0fb" - integrity sha512-IAJ2B3j2BTsQUJZ4R6cVvnTbPq0Vza7+dOgP81ISz2BKRtQ0VqNFv+VOALH2jLaDzf4t7NFlskzIXFqWqy2BLg== +"@react-native-community/cli-platform-ios@10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.0.tgz#be21c0e3bbf17358d540cc23e5556bf679f6322e" + integrity sha512-hIPK3iL/mL+0ChXmQ9uqqzNOKA48H+TAzg+hrxQLll/6dNMxDeK9/wZpktcsh8w+CyhqzKqVernGcQs7tPeKGw== + dependencies: + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" + execa "^1.0.0" + fast-xml-parser "^4.0.12" + glob "^7.1.3" + ora "^5.4.1" + +"@react-native-community/cli-platform-ios@^10.2.1": + version "10.2.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.1.tgz#2e6bd2cb6d48cbb8720d7b7265bb1bab80745f72" + integrity sha512-hz4zu4Y6eyj7D0lnZx8Mf2c2si8y+zh/zUTgCTaPPLzQD8jSZNNBtUUiA1cARm2razpe8marCZ1QbTMAGbf3mg== dependencies: - "@react-native-community/cli-tools" "^5.0.1" - chalk "^3.0.0" + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" + execa "^1.0.0" + fast-xml-parser "^4.0.12" glob "^7.1.3" - js-yaml "^3.13.1" - lodash "^4.17.15" - plist "^3.0.1" - xcode "^2.0.0" + ora "^5.4.1" -"@react-native-community/cli-server-api@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-5.0.1.tgz#3cf92dac766fab766afedf77df3fe4d5f51e4d2b" - integrity sha512-OOxL+y9AOZayQzmSW+h5T54wQe+QBc/f67Y9QlWzzJhkKJdYx+S4VOooHoD5PFJzGbYaxhu2YF17p517pcEIIA== +"@react-native-community/cli-plugin-metro@^10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-10.2.0.tgz#83cabbc04c80f7e94f88ed998b72c7d572c6f094" + integrity sha512-9eiJrKYuauEDkQLCrjJUh7tS9T0oaMQqVUSSSuyDG6du7HQcfaR4mSf21wK75jvhKiwcQLpsFmMdctAb+0v+Cg== dependencies: - "@react-native-community/cli-debugger-ui" "^5.0.1" - "@react-native-community/cli-tools" "^5.0.1" + "@react-native-community/cli-server-api" "^10.1.1" + "@react-native-community/cli-tools" "^10.1.1" + chalk "^4.1.2" + execa "^1.0.0" + metro "0.73.8" + metro-config "0.73.8" + metro-core "0.73.8" + metro-react-native-babel-transformer "0.73.8" + metro-resolver "0.73.8" + metro-runtime "0.73.8" + readline "^1.3.0" + +"@react-native-community/cli-server-api@^10.1.1": + version "10.1.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-10.1.1.tgz#e382269de281bb380c2e685431364fbbb8c1cb3a" + integrity sha512-NZDo/wh4zlm8as31UEBno2bui8+ufzsZV+KN7QjEJWEM0levzBtxaD+4je0OpfhRIIkhaRm2gl/vVf7OYAzg4g== + dependencies: + "@react-native-community/cli-debugger-ui" "^10.0.0" + "@react-native-community/cli-tools" "^10.1.1" compression "^1.7.1" connect "^3.6.5" errorhandler "^1.5.0" - nocache "^2.1.0" + nocache "^3.0.1" pretty-format "^26.6.2" serve-static "^1.13.1" - ws "^1.1.0" + ws "^7.5.1" -"@react-native-community/cli-tools@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-5.0.1.tgz#9ee564dbe20448becd6bce9fbea1b59aa5797919" - integrity sha512-XOX5w98oSE8+KnkMZZPMRT7I5TaP8fLbDl0tCu40S7Epz+Zz924n80fmdu6nUDIfPT1nV6yH1hmHmWAWTDOR+Q== +"@react-native-community/cli-tools@^10.1.1": + version "10.1.1" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-10.1.1.tgz#fa66e509c0d3faa31f7bb87ed7d42ad63f368ddd" + integrity sha512-+FlwOnZBV+ailEzXjcD8afY2ogFEBeHOw/8+XXzMgPaquU2Zly9B+8W089tnnohO3yfiQiZqkQlElP423MY74g== dependencies: - chalk "^3.0.0" - lodash "^4.17.15" + appdirsjs "^1.2.4" + chalk "^4.1.2" + find-up "^5.0.0" mime "^2.4.1" node-fetch "^2.6.0" open "^6.2.0" - shell-quote "1.6.1" + ora "^5.4.1" + semver "^6.3.0" + shell-quote "^1.7.3" -"@react-native-community/cli-types@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-5.0.1.tgz#8c5db4011988b0836d27a5efe230cb34890915dc" - integrity sha512-BesXnuFFlU/d1F3+sHhvKt8fUxbQlAbZ3hhMEImp9A6sopl8TEtryUGJ1dbazGjRXcADutxvjwT/i3LJVTIQug== +"@react-native-community/cli-types@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-10.0.0.tgz#046470c75ec18f8b3bd906e54e43a6f678e01a45" + integrity sha512-31oUM6/rFBZQfSmDQsT1DX/5fjqfxg7sf2u8kTPJK7rXVya5SRpAMaCXsPAG0omsmJxXt+J9HxUi3Ic+5Ux5Iw== dependencies: - ora "^3.4.0" + joi "^17.2.1" -"@react-native-community/cli@^5.0.1-alpha.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-5.0.1.tgz#1f7a66d813d5daf102e593f3c550650fa0cc8314" - integrity sha512-9VzSYUYSEqxEH5Ib2UNSdn2eyPiYZ4T7Y79o9DKtRBuSaUIwbCUdZtIm+UUjBpLS1XYBkW26FqL8/UdZDmQvXw== - dependencies: - "@react-native-community/cli-debugger-ui" "^5.0.1" - "@react-native-community/cli-hermes" "^5.0.1" - "@react-native-community/cli-server-api" "^5.0.1" - "@react-native-community/cli-tools" "^5.0.1" - "@react-native-community/cli-types" "^5.0.1" - appdirsjs "^1.2.4" - chalk "^3.0.0" - command-exists "^1.2.8" - commander "^2.19.0" - cosmiconfig "^5.1.0" - deepmerge "^3.2.0" - envinfo "^7.7.2" +"@react-native-community/cli@10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-10.2.0.tgz#bcb65bb3dcb03b0fc4e49619d51e12d23396b301" + integrity sha512-QH7AFBz5FX2zTZRH/o3XehHrZ0aZZEL5Sh+23nSEFgSj3bLFfvjjZhuoiRSAo7iiBdvAoXrfxQ8TXgg4Xf/7fw== + dependencies: + "@react-native-community/cli-clean" "^10.1.1" + "@react-native-community/cli-config" "^10.1.1" + "@react-native-community/cli-debugger-ui" "^10.0.0" + "@react-native-community/cli-doctor" "^10.2.0" + "@react-native-community/cli-hermes" "^10.2.0" + "@react-native-community/cli-plugin-metro" "^10.2.0" + "@react-native-community/cli-server-api" "^10.1.1" + "@react-native-community/cli-tools" "^10.1.1" + "@react-native-community/cli-types" "^10.0.0" + chalk "^4.1.2" + commander "^9.4.1" execa "^1.0.0" find-up "^4.1.0" fs-extra "^8.1.0" - glob "^7.1.3" graceful-fs "^4.1.3" - joi "^17.2.1" - leven "^3.1.0" - lodash "^4.17.15" - metro "^0.64.0" - metro-config "^0.64.0" - metro-core "^0.64.0" - metro-react-native-babel-transformer "^0.64.0" - metro-resolver "^0.64.0" - metro-runtime "^0.64.0" - minimist "^1.2.0" - mkdirp "^0.5.1" - node-stream-zip "^1.9.1" - ora "^3.4.0" - pretty-format "^26.6.2" prompts "^2.4.0" semver "^6.3.0" - serve-static "^1.13.1" - strip-ansi "^5.2.0" - sudo-prompt "^9.0.0" - wcwidth "^1.0.1" "@react-native/assets@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" integrity sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ== -"@react-native/normalize-color@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-1.0.0.tgz#c52a99d4fe01049102d47dc45d40cbde4f720ab6" - integrity sha512-xUNRvNmCl3UGCPbbHvfyFMnpvLPoOjDCcp5bT9m2k+TF/ZBklEQwhPZlkrxRx2NhgFh1X3a5uL7mJ7ZR+8G7Qg== +"@react-native/normalize-color@*", "@react-native/normalize-color@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.1.0.tgz#939b87a9849e81687d3640c5efa2a486ac266f91" + integrity sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA== -"@react-native/polyfills@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-1.0.0.tgz#05bb0031533598f9458cf65a502b8df0eecae780" - integrity sha512-0jbp4RxjYopTsIdLl+/Fy2TiwVYHy4mgeu07DG4b/LyM0OS/+lPP5c9sbnt/AMlnF6qz2JRZpPpGw1eMNS6A4w== +"@react-native/polyfills@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa" + integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ== "@sideway/address@^4.1.3": version "4.1.4" @@ -2303,19 +1984,24 @@ resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== -"@sinonjs/commons@^1.7.0": - version "1.8.6" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" - integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== +"@sinclair/typebox@^0.25.16": + version "0.25.24" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" + integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== + +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^8.0.1": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" - integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== +"@sinonjs/fake-timers@^10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz#d10549ed1f423d80639c528b6c7f5a1017747d0c" + integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== dependencies: - "@sinonjs/commons" "^1.7.0" + "@sinonjs/commons" "^2.0.0" "@sovpro/delimited-stream@^1.1.0": version "1.1.0" @@ -2409,6 +2095,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -2429,7 +2120,7 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": +"@types/babel__core@^7.1.14": version "7.20.0" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== @@ -2455,7 +2146,7 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": version "7.18.3" resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.3.tgz#dfc508a85781e5698d5b33443416b6268c4b3e8d" integrity sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w== @@ -2491,10 +2182,10 @@ dependencies: "@types/node" "*" -"@types/eslint@^7.2.13": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78" - integrity sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng== +"@types/eslint@^8.21.2": + version "8.21.2" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.21.2.tgz#2b61b43a8b0e66006856a2a4c8e51f6f773ead27" + integrity sha512-EMpxUyystd3uZVByZap1DACsMXvb82ypQnGn89e1Y0a+LYu3JJscUd/gqhRsVFDkaD2MIiWo0MT8EfXr3DGRKw== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -2542,7 +2233,7 @@ resolved "https://registry.yarnpkg.com/@types/figlet/-/figlet-1.5.5.tgz#da93169178f0187da288c313ab98ab02fb1e8b8c" integrity sha512-0sMBeFoqdGgdXoR/hgKYSWMpFufSpToosNsI2VgmkPqZJgeEXsXNu2hGr0FN401dBro2tNO5y2D6uw3UxVaxbg== -"@types/graceful-fs@^4.1.2": +"@types/graceful-fs@^4.1.3": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== @@ -2556,7 +2247,7 @@ dependencies: buffer "^6.0.0" -"@types/inquirer@^8.1.3": +"@types/inquirer@^8.2.6": version "8.2.6" resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.6.tgz#abd41a5fb689c7f1acb12933d787d4262a02a0ab" integrity sha512-3uT88kxg8lNzY8ay2ZjP44DKcRaTGztqeIvN2zHvhzIBH/uAPaL75aBtdNRKbA7xXoMbBt5kX0M00VKAnfOYlA== @@ -2583,13 +2274,13 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^26.0.23": - version "26.0.24" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" - integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w== +"@types/jest@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.0.tgz#337b90bbcfe42158f39c2fb5619ad044bbb518ac" + integrity sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg== dependencies: - jest-diff "^26.0.0" - pretty-format "^26.0.0" + expect "^29.0.0" + pretty-format "^29.0.0" "@types/json-schema@*", "@types/json-schema@^7.0.9": version "7.0.11" @@ -2601,10 +2292,10 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/luxon@^1.27.0": - version "1.27.1" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.27.1.tgz#aceeb2d5be8fccf541237e184e37ecff5faa9096" - integrity sha512-cPiXpOvPFDr2edMnOXlz3UBDApwUfR+cpizvxCy0n3vp9bz/qe8BWzHPIEFcy+ogUOyjKuCISgyq77ELZPmkkg== +"@types/luxon@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.2.0.tgz#99901b4ab29a5fdffc88fff59b3b47fbfbe0557b" + integrity sha512-lGmaGFoaXHuOLXFvuju2bfvZRqxAqkHPx9Y9IQdQABrinJJshJwfNCKV+u7rR3kJbiqfTF/NhOkcxxAFrObyaA== "@types/mime@*": version "3.0.1" @@ -2630,9 +2321,9 @@ form-data "^3.0.0" "@types/node@*", "@types/node@^16.11.7": - version "16.18.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.13.tgz#c572f8837094c6e3b73918a68674c784f6877fc0" - integrity sha512-l0/3XZ153UTlNOnZK8xSNoJlQda9/WnYgiTdcKKPJSZjdjI9MU+A9oMXOesAWLSnqAaaJhj3qfQsU07Dr8OUwg== + version "16.18.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.16.tgz#09ff98b144abae2d7cce3e9fe9040ab2bf73222c" + integrity sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2654,11 +2345,6 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== -"@types/prop-types@*": - version "15.7.5" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" - integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== - "@types/qs@*": version "6.9.7" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" @@ -2669,22 +2355,6 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/react-native@^0.64.10": - version "0.64.31" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.31.tgz#589fa25676818f47d43e2fe16deca8c926c67b4d" - integrity sha512-U54mD5zsVade6nJidMEr8Zo7JGJytH+mH3Be3EeYrfgNMW1bHYr6U1HODFUC0wpprMdlQYvFXOSrBaWR2eefOA== - dependencies: - "@types/react" "^17" - -"@types/react@^17": - version "17.0.53" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.53.tgz#10d4d5999b8af3d6bc6a9369d7eb953da82442ab" - integrity sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - "@types/ref-array-di@^1.2.5": version "1.2.5" resolved "https://registry.yarnpkg.com/@types/ref-array-di/-/ref-array-di-1.2.5.tgz#822b9be5b934398fafd5c26264d8de80d487747d" @@ -2706,11 +2376,6 @@ dependencies: "@types/ref-napi" "*" -"@types/scheduler@*": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" - integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== - "@types/semver@^7.3.12": version "7.3.13" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" @@ -2736,15 +2401,15 @@ dependencies: "@types/node" "*" -"@types/uuid@^8.3.0", "@types/uuid@^8.3.1": - version "8.3.4" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" - integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== +"@types/uuid@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.1.tgz#98586dc36aee8dacc98cc396dbca8d0429647aa6" + integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== -"@types/validator@^13.1.3", "@types/validator@^13.7.10": - version "13.7.12" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.12.tgz#a285379b432cc8d103b69d223cbb159a253cf2f7" - integrity sha512-YVtyAPqpefU+Mm/qqnOANW6IkqKpCSrarcyV269C8MA8Ux0dbkEuQwM/4CjL47kVEM2LgBef/ETfkH+c6+moFA== +"@types/validator@^13.7.10": + version "13.7.14" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.14.tgz#5512aef43ba353ea2fe2d0d8c7ce71c75c2ad9e6" + integrity sha512-J6OAed6rhN6zyqL9Of6ZMamhlsOEU/poBVvbHr/dKOYKTeuYYMlDkMv+b6UUV0o2i0tw73cgyv/97WTWaUl0/g== "@types/varint@^6.0.0": version "6.0.1" @@ -2753,10 +2418,10 @@ dependencies: "@types/node" "*" -"@types/ws@^7.4.6": - version "7.4.7" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" - integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== +"@types/ws@^8.5.4": + version "8.5.4" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" + integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== dependencies: "@types/node" "*" @@ -2779,88 +2444,95 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.48.1": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz#24b8b4a952f3c615fe070e3c461dd852b5056734" - integrity sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw== +"@types/yargs@^17.0.8": + version "17.0.22" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a" + integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== dependencies: - "@typescript-eslint/scope-manager" "5.53.0" - "@typescript-eslint/type-utils" "5.53.0" - "@typescript-eslint/utils" "5.53.0" + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^5.48.1": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz#bc2400c3a23305e8c9a9c04aa40933868aaaeb47" + integrity sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.55.0" + "@typescript-eslint/type-utils" "5.55.0" + "@typescript-eslint/utils" "5.55.0" debug "^4.3.4" grapheme-splitter "^1.0.4" ignore "^5.2.0" natural-compare-lite "^1.4.0" - regexpp "^3.2.0" semver "^7.3.7" tsutils "^3.21.0" "@typescript-eslint/parser@^5.48.1": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.53.0.tgz#a1f2b9ae73b83181098747e96683f1b249ecab52" - integrity sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ== + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.55.0.tgz#8c96a0b6529708ace1dcfa60f5e6aec0f5ed2262" + integrity sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw== dependencies: - "@typescript-eslint/scope-manager" "5.53.0" - "@typescript-eslint/types" "5.53.0" - "@typescript-eslint/typescript-estree" "5.53.0" + "@typescript-eslint/scope-manager" "5.55.0" + "@typescript-eslint/types" "5.55.0" + "@typescript-eslint/typescript-estree" "5.55.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.53.0": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz#42b54f280e33c82939275a42649701024f3fafef" - integrity sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w== +"@typescript-eslint/scope-manager@5.55.0": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz#e863bab4d4183ddce79967fe10ceb6c829791210" + integrity sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw== dependencies: - "@typescript-eslint/types" "5.53.0" - "@typescript-eslint/visitor-keys" "5.53.0" + "@typescript-eslint/types" "5.55.0" + "@typescript-eslint/visitor-keys" "5.55.0" -"@typescript-eslint/type-utils@5.53.0": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz#41665449935ba9b4e6a1ba6e2a3f4b2c31d6cf97" - integrity sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw== +"@typescript-eslint/type-utils@5.55.0": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz#74bf0233523f874738677bb73cb58094210e01e9" + integrity sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA== dependencies: - "@typescript-eslint/typescript-estree" "5.53.0" - "@typescript-eslint/utils" "5.53.0" + "@typescript-eslint/typescript-estree" "5.55.0" + "@typescript-eslint/utils" "5.55.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.53.0": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.53.0.tgz#f79eca62b97e518ee124086a21a24f3be267026f" - integrity sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A== +"@typescript-eslint/types@5.55.0": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.55.0.tgz#9830f8d3bcbecf59d12f821e5bc6960baaed41fd" + integrity sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug== -"@typescript-eslint/typescript-estree@5.53.0": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz#bc651dc28cf18ab248ecd18a4c886c744aebd690" - integrity sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w== +"@typescript-eslint/typescript-estree@5.55.0": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz#8db7c8e47ecc03d49b05362b8db6f1345ee7b575" + integrity sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ== dependencies: - "@typescript-eslint/types" "5.53.0" - "@typescript-eslint/visitor-keys" "5.53.0" + "@typescript-eslint/types" "5.55.0" + "@typescript-eslint/visitor-keys" "5.55.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.53.0": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.53.0.tgz#e55eaad9d6fffa120575ffaa530c7e802f13bce8" - integrity sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g== +"@typescript-eslint/utils@5.55.0": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.55.0.tgz#34e97322e7ae5b901e7a870aabb01dad90023341" + integrity sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw== dependencies: + "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.53.0" - "@typescript-eslint/types" "5.53.0" - "@typescript-eslint/typescript-estree" "5.53.0" + "@typescript-eslint/scope-manager" "5.55.0" + "@typescript-eslint/types" "5.55.0" + "@typescript-eslint/typescript-estree" "5.55.0" eslint-scope "^5.1.1" - eslint-utils "^3.0.0" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.53.0": - version "5.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz#8a5126623937cdd909c30d8fa72f79fa56cc1a9f" - integrity sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w== +"@typescript-eslint/visitor-keys@5.55.0": + version "5.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz#01ad414fca8367706d76cdb94adf788dc5b664a2" + integrity sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw== dependencies: - "@typescript-eslint/types" "5.53.0" + "@typescript-eslint/types" "5.55.0" eslint-visitor-keys "^3.3.0" "@unimodules/core@*": @@ -2878,6 +2550,26 @@ expo-modules-autolinking "^0.0.3" invariant "^2.2.4" +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + +"@yarnpkg/parsers@^3.0.0-rc.18": + version "3.0.0-rc.40" + resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.40.tgz#972af4bb01d797ad20e12de8126ea2276ab8fdea" + integrity sha512-sKbi5XhHKXCjzb5m0ftGuQuODM2iUXEsrCSl8MkKexNWHepCmU3IPaGTPC5gHZy4sOvsb9JqTLaZEez+kDzG+Q== + dependencies: + js-yaml "^3.10.0" + tslib "^2.4.0" + +"@zkochan/js-yaml@0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz#975f0b306e705e28b8068a07737fa46d3fc04826" + integrity sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg== + dependencies: + argparse "^2.0.1" + JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -2886,12 +2578,7 @@ JSONStream@^1.0.4: jsonparse "^1.2.0" through ">=2.2.7 <3" -abab@^2.0.3, abab@^2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" - integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== - -abbrev@1: +abbrev@1, abbrev@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== @@ -2916,35 +2603,17 @@ accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.7, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - -acorn-jsx@^5.3.1: +acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^7.1.1, acorn@^7.4.0: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.2.4, acorn@^8.4.1: +acorn@^8.4.1, acorn@^8.5.0, acorn@^8.8.0: version "8.8.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== @@ -2961,13 +2630,13 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" -agentkeepalive@^4.1.3: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" - integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== +agentkeepalive@^4.1.3, agentkeepalive@^4.2.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255" + integrity sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg== dependencies: debug "^4.1.0" - depd "^1.1.2" + depd "^2.0.0" humanize-ms "^1.2.1" aggregate-error@^3.0.0: @@ -2978,7 +2647,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.10.0, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2988,16 +2657,6 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.1: - version "8.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - anser@^1.4.9: version "1.4.10" resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b" @@ -3058,14 +2717,6 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - anymatch@^3.0.3: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -3125,6 +2776,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -3150,16 +2806,19 @@ array-back@^4.0.1, array-back@^4.0.2: resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + array-differ@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== -array-filter@~0.0.0: - version "0.0.1" - resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" - integrity sha512-VW0FpCIhjZdarWjIz8Vpva7U95fl2Jn+b+mmFFMLn8PIVscOQcAgEznwUzTEuUHuqZqIxwzRlcaN/urTFFQoiw== - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -3189,33 +2848,11 @@ array-index@^1.0.0: debug "^2.2.0" es6-symbol "^3.0.2" -array-map@~0.0.0: - version "0.0.1" - resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.1.tgz#d1bf3cc8813a7daaa335e5c8eb21d9d06230c1a7" - integrity sha512-sxHIeJTGEsRC8/hYkZzdJNNPZ41EXHVys7pqMw1iwE/Kx8/hto0UbDuGQsSJ0ujPovj9qUZl6EOY/EiZ2g3d9Q== - -array-reduce@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" - integrity sha512-8jR+StqaC636u7h3ye1co3lQRefgVVUQUhuAmRbDqIMeR2yuXzRvkCNQiQ5J/wbREmoBLNtp13dhaaVpZQDRUw== - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== - dependencies: - array-uniq "^1.0.1" - array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== - array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -3241,17 +2878,6 @@ array.prototype.flatmap@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -array.prototype.reduce@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" - integrity sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - es-array-method-boxes-properly "^1.0.0" - is-string "^1.0.7" - arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -3272,13 +2898,6 @@ asmcrypto.js@^0.22.0: resolved "https://registry.yarnpkg.com/asmcrypto.js/-/asmcrypto.js-0.22.0.tgz#38fc1440884d802c7bd37d1d23c2b26a5cd5d2d2" integrity sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA== -asn1@~0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - asn1js@^3.0.1, asn1js@^3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" @@ -3288,11 +2907,6 @@ asn1js@^3.0.1, asn1js@^3.0.5: pvutils "^1.1.3" tslib "^2.4.0" -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== - assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -3310,29 +2924,22 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== -async-mutex@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.2.tgz#1485eda5bda1b0ec7c8df1ac2e815757ad1831df" - integrity sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA== +async-mutex@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.0.tgz#ae8048cd4d04ace94347507504b3cf15e631c25f" + integrity sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA== dependencies: - tslib "^2.3.1" + tslib "^2.4.0" -async@^2.4.0: - version "2.6.4" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" - integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== - dependencies: - lodash "^4.17.14" +async@^3.2.2, async@^3.2.3: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== asynckit@^0.4.0: version "0.4.0" @@ -3354,15 +2961,14 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== - -aws4@^1.8.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" - integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== +axios@^1.0.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024" + integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" b64-lite@^1.3.1, b64-lite@^1.4.0: version "1.4.0" @@ -3383,16 +2989,15 @@ babel-core@^7.0.0-bridge.0: resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== -babel-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" - integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== +babel-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" + integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q== dependencies: - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/transform" "^29.5.0" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^27.5.1" + babel-preset-jest "^29.5.0" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -3408,14 +3013,14 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" - integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" - "@types/babel__core" "^7.0.0" + "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" babel-plugin-polyfill-corejs2@^0.3.3: @@ -3465,7 +3070,7 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-fbjs@^3.3.0: +babel-preset-fbjs@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" integrity sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== @@ -3498,12 +3103,12 @@ babel-preset-fbjs@^3.3.0: "@babel/plugin-transform-template-literals" "^7.0.0" babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" -babel-preset-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" - integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== +babel-preset-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" + integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== dependencies: - babel-plugin-jest-hoist "^27.5.1" + babel-plugin-jest-hoist "^29.5.0" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -3523,7 +3128,7 @@ base-x@^3.0.2: dependencies: safe-buffer "^5.0.1" -base64-js@*, base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@*, base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -3541,28 +3146,28 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== - dependencies: - tweetnacl "^0.14.3" - before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== -big-integer@1.6.x: - version "1.6.51" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" - integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== - bignumber.js@^9.0.0: version "9.1.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== +bin-links@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-3.0.3.tgz#3842711ef3db2cd9f16a5f404a996a12db355a6e" + integrity sha512-zKdnMPWEdh4F5INR07/eBrodC7QrF5JKvqskjz/ZZRXg5YSAZIbn8zGhbhUrElzHBZ2fvEQdOU59RHcTG3GiwA== + dependencies: + cmd-shim "^5.0.0" + mkdirp-infer-owner "^2.0.0" + npm-normalize-package-bin "^2.0.0" + read-cmd-shim "^3.0.0" + rimraf "^3.0.0" + write-file-atomic "^4.0.0" + bindings@^1.3.1: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" @@ -3570,6 +3175,15 @@ bindings@^1.3.1: dependencies: file-uri-to-path "1.0.0" +bl@^4.0.3, bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" @@ -3606,20 +3220,6 @@ borc@^3.0.0: json-text-sequence "~0.3.0" readable-stream "^3.6.0" -bplist-creator@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e" - integrity sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg== - dependencies: - stream-buffers "2.2.x" - -bplist-parser@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.3.1.tgz#e1c90b2ca2a9f9474cc72f6862bbf3fee8341fd1" - integrity sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA== - dependencies: - big-integer "1.6.x" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3628,6 +3228,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" @@ -3651,11 +3258,6 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - browserslist@^4.21.3, browserslist@^4.21.5: version "4.21.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" @@ -3692,6 +3294,14 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + buffer@^6.0.0, buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -3705,15 +3315,17 @@ builtins@^1.0.3: resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" integrity sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ== -byline@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" - integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== +builtins@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" + integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== + dependencies: + semver "^7.0.0" -byte-size@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" - integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== +byte-size@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.0.tgz#36528cd1ca87d39bd9abd51f5715dc93b6ceb032" + integrity sha512-NNiBxKgxybMBtWdmvx7ZITJi4ZG+CYUgwOSZTfqB1qogkRHrhbQE/R2r5Fh94X+InN5MCYz6SvB/ejHMj/HbsQ== bytes@3.0.0: version "3.0.0" @@ -3725,7 +3337,7 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -cacache@^15.0.5, cacache@^15.2.0: +cacache@^15.2.0: version "15.3.0" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== @@ -3749,6 +3361,30 @@ cacache@^15.0.5, cacache@^15.2.0: tar "^6.0.2" unique-filename "^1.1.1" +cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: + version "16.1.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" + integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== + dependencies: + "@npmcli/fs" "^2.1.0" + "@npmcli/move-file" "^2.0.0" + chownr "^2.0.0" + fs-minipass "^2.1.0" + glob "^8.0.1" + infer-owner "^1.0.4" + lru-cache "^7.7.1" + minipass "^3.1.6" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + mkdirp "^1.0.4" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^9.0.0" + tar "^6.1.11" + unique-filename "^2.0.0" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -3816,28 +3452,24 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001449: - version "1.0.30001458" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz#871e35866b4654a7d25eccca86864f411825540c" - integrity sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w== + version "1.0.30001467" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001467.tgz#1afc9c16ed61f50dd87139da87ca43a3e0051c77" + integrity sha512-cEdN/5e+RPikvl9AHm4uuLXxeCNq8rFsQ+lPHTfe/OtypP3WwnVVbjn+6uBV7PaFL6xUFzTh+sSCOz1rKhcO+Q== canonicalize@^1.0.1: version "1.0.8" resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.8.tgz#24d1f1a00ed202faafd9bf8e63352cd4450c6df1" integrity sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A== -capture-exit@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" - integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== +chalk@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== dependencies: - rsvp "^4.8.4" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + ansi-styles "^4.1.0" + supports-color "^7.1.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3846,15 +3478,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3912,16 +3536,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -class-validator@0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.13.1.tgz#381b2001ee6b9e05afd133671fbdf760da7dec67" - integrity sha512-zWIeYFhUitvAHBwNhDdCRK09hWx+P0HUwFE8US8/CxFpMVzkUK8RJl7yOIE+BVu2lxyPNgeOaFv78tLE47jBIg== - dependencies: - "@types/validator" "^13.1.3" - libphonenumber-js "^1.9.7" - validator "^13.5.2" - -class-validator@^0.14.0: +class-validator@0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.0.tgz#40ed0ecf3c83b2a8a6a320f4edb607be0f0df159" integrity sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A== @@ -3940,21 +3555,19 @@ clear@^0.1.0: resolved "https://registry.yarnpkg.com/clear/-/clear-0.1.0.tgz#b81b1e03437a716984fd7ac97c87d73bdfe7048a" integrity sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw== -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== - dependencies: - restore-cursor "^2.0.0" - -cli-cursor@^3.1.0: +cli-cursor@3.1.0, cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.0.0: +cli-spinners@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== + +cli-spinners@^2.5.0: version "2.7.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== @@ -3982,7 +3595,16 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -clone-deep@^4.0.1: +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone-deep@4.0.1, clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== @@ -4001,10 +3623,10 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -cmd-shim@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-4.1.0.tgz#b3a904a6743e9fede4148c6f3800bf2a08135bdd" - integrity sha512-lb9L7EM4I/ZRVuljLPEtUJOP+xiQVknZ4ZMpMgEp4JzNldPb27HU03hi6K1/6CoIuit/Zm/LQXySErFeXxDprw== +cmd-shim@5.0.0, cmd-shim@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-5.0.0.tgz#8d0aaa1a6b0708630694c4dbde070ed94c707724" + integrity sha512-qkCtZ59BidfEwHltnJwkyVZn+XQojdAySM1D1gSeh11Z4pW1Kpolkyo53L5noc0nrxmIvyFwTmJRo4xs7FFLPw== dependencies: mkdirp-infer-owner "^2.0.0" @@ -4065,12 +3687,7 @@ colorette@^1.0.7: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== -colors@^1.1.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - -columnify@^1.5.4: +columnify@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3" integrity sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q== @@ -4078,7 +3695,7 @@ columnify@^1.5.4: strip-ansi "^6.0.1" wcwidth "^1.0.0" -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -4117,7 +3734,7 @@ command-line-usage@^6.1.0: table-layout "^1.0.2" typical "^5.2.0" -commander@^2.15.0, commander@^2.19.0: +commander@^2.15.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -4127,16 +3744,21 @@ commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -commander@^8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" - integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +commander@^9.4.1: + version "9.5.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== +common-ancestor-path@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" + integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -4195,10 +3817,10 @@ concat-stream@^2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" -config-chain@^1.1.12: - version "1.1.13" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" - integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== +config-chain@1.1.12: + version "1.1.12" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" + integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== dependencies: ini "^1.3.4" proto-list "~1.2.1" @@ -4230,10 +3852,10 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -conventional-changelog-angular@^5.0.12: - version "5.0.13" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c" - integrity sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA== +conventional-changelog-angular@5.0.12: + version "5.0.12" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz#c979b8b921cbfe26402eb3da5bbfda02d865a2b9" + integrity sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw== dependencies: compare-func "^2.0.0" q "^1.5.1" @@ -4247,7 +3869,7 @@ conventional-changelog-conventionalcommits@^5.0.0: lodash "^4.17.15" q "^1.5.1" -conventional-changelog-core@^4.2.2: +conventional-changelog-core@4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz#e50d047e8ebacf63fac3dc67bf918177001e1e9f" integrity sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg== @@ -4307,7 +3929,7 @@ conventional-commits-parser@^3.2.0: split2 "^3.0.0" through2 "^4.0.0" -conventional-recommended-bump@^6.1.0: +conventional-recommended-bump@6.1.0, conventional-recommended-bump@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" integrity sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw== @@ -4321,11 +3943,16 @@ conventional-recommended-bump@^6.1.0: meow "^8.0.0" q "^1.5.1" -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -4342,17 +3969,12 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.25.1: - version "3.29.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.0.tgz#1b8d9eb4191ab112022e7f6364b99b65ea52f528" - integrity sha512-ScMn3uZNAFhK2DGoEfErguoiAHhV2Ju+oJo/jK08p7B3f3UhocUrCCkTvnZaiS+edl5nlIoiBXKcwMc6elv4KQ== + version "3.29.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.1.tgz#15c0fb812ea27c973c18d425099afa50b934b41b" + integrity sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA== dependencies: browserslist "^4.21.5" -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== - core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -4366,20 +3988,10 @@ cors@^2.8.5: object-assign "^4" vary "^1" -cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - -cosmiconfig@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" - integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== +cosmiconfig@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== dependencies: "@types/parse-json" "^4.0.0" import-fresh "^3.2.1" @@ -4387,6 +3999,16 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -4424,28 +4046,6 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== - dependencies: - cssom "~0.3.6" - -csstype@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" - integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== - d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -4459,27 +4059,11 @@ dargs@^7.0.0: resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== - dependencies: - assert-plus "^1.0.0" - data-uri-to-buffer@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== -data-urls@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== - dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - dateformat@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -4497,7 +4081,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4529,17 +4113,12 @@ decamelize@^1.1.0, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== -decimal.js@^10.2.1: - version "10.4.3" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" - integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== - decode-uri-component@^0.2.0, decode-uri-component@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== -dedent@^0.7.0: +dedent@0.7.0, dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== @@ -4549,7 +4128,7 @@ deep-extend@^0.6.0, deep-extend@~0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-is@^0.1.3, deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -4560,9 +4139,9 @@ deepmerge@^3.2.0: integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA== deepmerge@^4.2.2: - version "4.3.0" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" - integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== defaults@^1.0.3: version "1.0.4" @@ -4571,6 +4150,11 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + define-properties@^1.1.3, define-properties@^1.1.4: version "1.2.0" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" @@ -4616,15 +4200,19 @@ denodeify@^1.2.1: resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" integrity sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg== -depd@2.0.0: +depd@2.0.0, depd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -depd@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== +deprecated-react-native-prop-types@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-3.0.1.tgz#a275f84cd8519cd1665e8df3c99e9067d57a23ec" + integrity sha512-J0jCJcsk4hMlIb7xwOZKLfMpuJn6l8UtrPEzzQV5ewz5gvKNYakhBuq9h2rWX7YwHHJZFhU5W8ye7dB9oN8VcQ== + dependencies: + "@react-native/normalize-color" "*" + invariant "*" + prop-types "*" deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" @@ -4641,11 +4229,6 @@ detect-indent@^5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g== -detect-indent@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" - integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== - detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" @@ -4669,38 +4252,21 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -did-resolver@^3.1.3: - version "3.2.2" - resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.2.2.tgz#6f4e252a810f785d1b28a10265fad6dffee25158" - integrity sha512-Eeo2F524VM5N3W4GwglZrnul2y6TLTwMQP3In62JdG34NZoqihYyOZLk+5wUW8sSgvIYIcJM8Dlt3xsdKZZ3tg== - -did-resolver@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.0.1.tgz#11bb3f19ed1c8f53f4af4702912fa9f7852fc305" - integrity sha512-eHs2VLKhcANmh08S87PKvOauIAmSOd7nb7AlhNxcvOyDAIGQY1UfbiqI1VOW5IDKvOO6aEWY+5edOt1qrCp1Eg== - -diff-sequences@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" - integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== +did-resolver@^4.0.0, did-resolver@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.1.0.tgz#740852083c4fd5bf9729d528eca5d105aff45eb6" + integrity sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA== -diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -dir-glob@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" - integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== - dependencies: - path-type "^3.0.0" - dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -4722,12 +4288,12 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -domexception@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== +dot-prop@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" + integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== dependencies: - webidl-conversions "^5.0.0" + is-obj "^2.0.0" dot-prop@^5.1.0: version "5.3.0" @@ -4736,14 +4302,7 @@ dot-prop@^5.1.0: dependencies: is-obj "^2.0.0" -dot-prop@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" - integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== - dependencies: - is-obj "^2.0.0" - -dotenv@^10.0.0: +dotenv@~10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== @@ -4753,28 +4312,27 @@ duplexer@^0.1.1: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== +ejs@^3.1.7: + version "3.1.9" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" + integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== + dependencies: + jake "^10.8.5" + electron-to-chromium@^1.4.284: - version "1.4.311" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.311.tgz#953bc9a4767f5ce8ec125f9a1ad8e00e8f67e479" - integrity sha512-RoDlZufvrtr2Nx3Yx5MB8jX3aHIxm8nRWPJm3yVvyHmyKaRvn90RjzB6hNnt0AkhS3IInJdyRfQb4mWhPvUjVw== + version "1.4.333" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.333.tgz#ebb21f860f8a29923717b06ec0cb54e77ed34c04" + integrity sha512-YyE8+GKyGtPEP1/kpvqsdhD6rA/TP1DUFDN4uiU/YI52NzDxmwHkEb3qjId8hLBa5siJvG0sfC3O66501jMruQ== -emittery@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" - integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^8.0.0: version "8.0.0" @@ -4786,21 +4344,29 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -encoding@^0.1.12: +encoding@^0.1.12, encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.1.0: +end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" -enquirer@^2.3.5: +enhanced-resolve@^5.10.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" + integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +enquirer@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== @@ -4845,17 +4411,17 @@ errorhandler@^1.5.0: escape-html "~1.0.3" es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.21.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" - integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== + version "1.21.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" + integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== dependencies: + array-buffer-byte-length "^1.0.0" available-typed-arrays "^1.0.5" call-bind "^1.0.2" es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" - function-bind "^1.1.1" function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" get-symbol-description "^1.0.0" globalthis "^1.0.3" gopd "^1.0.1" @@ -4863,8 +4429,8 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: has-property-descriptors "^1.0.0" has-proto "^1.0.1" has-symbols "^1.0.3" - internal-slot "^1.0.4" - is-array-buffer "^3.0.1" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" @@ -4872,22 +4438,18 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: is-string "^1.0.7" is-typed-array "^1.1.10" is-weakref "^1.0.2" - object-inspect "^1.12.2" + object-inspect "^1.12.3" object-keys "^1.1.1" object.assign "^4.1.4" regexp.prototype.flags "^1.4.3" safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.7" string.prototype.trimend "^1.0.6" string.prototype.trimstart "^1.0.6" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" which-typed-array "^1.1.9" -es-array-method-boxes-properly@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" - integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== - es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" @@ -4964,22 +4526,10 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - eslint-config-prettier@^8.3.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz#dec1d29ab728f4fa63061774e1672ac4e363d207" - integrity sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA== + version "8.7.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz#f1cc58a8afebc50980bd53475451df146c13182d" + integrity sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA== eslint-import-resolver-node@^0.3.7: version "0.3.7" @@ -4990,16 +4540,18 @@ eslint-import-resolver-node@^0.3.7: is-core-module "^2.11.0" resolve "^1.22.1" -eslint-import-resolver-typescript@^2.4.0: - version "2.7.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz#a90a4a1c80da8d632df25994c4c5fdcdd02b8751" - integrity sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ== +eslint-import-resolver-typescript@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.3.tgz#db5ed9e906651b7a59dd84870aaef0e78c663a05" + integrity sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ== dependencies: debug "^4.3.4" - glob "^7.2.0" + enhanced-resolve "^5.10.0" + get-tsconfig "^4.2.0" + globby "^13.1.2" + is-core-module "^2.10.0" is-glob "^4.0.3" - resolve "^1.22.0" - tsconfig-paths "^3.14.1" + synckit "^0.8.4" eslint-module-utils@^2.7.4: version "2.7.4" @@ -5029,19 +4581,19 @@ eslint-plugin-import@^2.23.4: semver "^6.3.0" tsconfig-paths "^3.14.1" -eslint-plugin-prettier@^3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz#e9ddb200efb6f3d05ffe83b1665a716af4a387e5" - integrity sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g== +eslint-plugin-prettier@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== dependencies: prettier-linter-helpers "^1.0.0" -eslint-plugin-workspaces@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-workspaces/-/eslint-plugin-workspaces-0.7.0.tgz#be97fad9d25ab7430074c526f046b0e5c037633f" - integrity sha512-1+qzAM/iFFJ4MR3IOSY7n6Kw9XW/Fc+eVzWfN9nCcneDHr21rccZWUtGyMesk35bFnOWyp9dmJbYL0v5KYZ14w== +eslint-plugin-workspaces@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-workspaces/-/eslint-plugin-workspaces-0.8.0.tgz#e723a1333a8ddddbc416220a9e03578603de0f85" + integrity sha512-8BhKZaGFpl0xAVo7KHaWffaBvvroaOeLuqLkVsMNZvMaN6ZHKYx7QZoaXC/Y299tG3wvN6v7hu27VBHmyg4q4g== dependencies: - "@joshuajaco/get-monorepo-packages" "^1.2.1" + find-workspaces "^0.1.0" eslint-scope@^5.1.1: version "5.1.1" @@ -5051,99 +4603,83 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== dependencies: - eslint-visitor-keys "^2.0.0" - -eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + esrecurse "^4.3.0" + estraverse "^5.2.0" eslint-visitor-keys@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@^7.28.0: - version "7.32.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" - integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== - dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.3" - "@humanwhocodes/config-array" "^0.5.0" +eslint@^8.36.0: + version "8.36.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.36.0.tgz#1bd72202200a5492f91803b113fb8a83b11285cf" + integrity sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.0.1" + "@eslint/js" "8.36.0" + "@humanwhocodes/config-array" "^0.11.8" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" - debug "^4.0.1" + debug "^4.3.2" doctrine "^3.0.0" - enquirer "^2.3.5" escape-string-regexp "^4.0.0" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - esquery "^1.4.0" + eslint-scope "^7.1.1" + eslint-visitor-keys "^3.3.0" + espree "^9.5.0" + esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.1.2" - globals "^13.6.0" - ignore "^4.0.6" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - js-yaml "^3.13.1" + is-path-inside "^3.0.3" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" - minimatch "^3.0.4" + minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" + strip-ansi "^6.0.1" strip-json-comments "^3.1.0" - table "^6.0.9" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== +espree@^9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113" + integrity sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw== dependencies: - acorn "^7.4.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" -esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: +esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.4.0: - version "1.4.2" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.2.tgz#c6d3fee05dd665808e2ad870631f221f5617b1d1" - integrity sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng== +esquery@^1.0.1, esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" @@ -5189,10 +4725,20 @@ events@^3.3.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -exec-sh@^0.3.2: - version "0.3.6" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" - integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== +execa@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" + integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" execa@^1.0.0: version "1.0.0" @@ -5240,15 +4786,16 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expect@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" - integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== +expect@^29.0.0, expect@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" + integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== dependencies: - "@jest/types" "^27.5.1" - jest-get-type "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" + "@jest/expect-utils" "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" expo-modules-autolinking@^0.0.3: version "0.0.3" @@ -5327,11 +4874,6 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" @@ -5355,16 +4897,6 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== - -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - fast-base64-decode@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" @@ -5380,7 +4912,18 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.2.5, fast-glob@^3.2.9: +fast-glob@3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" + integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.5, fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== @@ -5391,12 +4934,12 @@ fast-glob@^3.2.5, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== @@ -5406,6 +4949,13 @@ fast-text-encoding@^1.0.3: resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz#0aa25f7f638222e3396d72bf936afcf1d42d6867" integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== +fast-xml-parser@^4.0.12: + version "4.1.3" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.1.3.tgz#0254ad0d4d27f07e6b48254b068c0c137488dd97" + integrity sha512-LsNDahCiCcJPe8NO7HijcnukHB24tKbfDDA5IILx9dmW3Frb52lhbeX6MPNUSvyGNfav2VTYpJ/OqkRoVLrh2Q== + dependencies: + strnum "^1.0.5" + fastq@^1.6.0: version "1.15.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" @@ -5442,7 +4992,7 @@ figlet@^1.5.2: resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.5.2.tgz#dda34ff233c9a48e36fcff6741aeb5bafe49b634" integrity sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ== -figures@^3.0.0: +figures@3.2.0, figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== @@ -5461,6 +5011,13 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +filelist@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -5547,7 +5104,7 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -find-up@~5.0.0: +find-up@^5.0.0, find-up@~5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== @@ -5555,6 +5112,15 @@ find-up@~5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +find-workspaces@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/find-workspaces/-/find-workspaces-0.1.0.tgz#c01ddc81a1814b2c18927b26adb82afc97b63cea" + integrity sha512-DmHumOdSCtwY6qW6Syx3a/W6ZGYLhGiwqWCiPOsld4sxP9yeRh3LraKeu+G3l5ilgt8jOUAgjDHT4MOFZ8dQ3Q== + dependencies: + fast-glob "^3.2.12" + type-fest "^3.2.0" + yaml "^2.1.3" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -5563,20 +5129,30 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + flatted@^3.1.0: version "3.2.7" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.200.1" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.200.1.tgz#99a94b35b7d1815716e3db56bb797440ed340716" - integrity sha512-N6gxgo0iQx0G2m3aJjg3RLxNLUG3EBYgBN/xDDPGQXSjvqNkTdEd2t1myE36Xi7GndZQWngDP7jf0GvxdL6pRg== + version "0.202.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.202.0.tgz#534178266d3ceec5368415e59990db97eece5bd0" + integrity sha512-ZiXxSIXK3zPmY3zrzCofFonM2T+/3Jz5QZKJyPVtUERQEJUnYkXBQ+0H3FzyqiyJs+VXqb/UNU6/K6sziVYdxw== + +flow-parser@^0.185.0: + version "0.185.2" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.185.2.tgz#cb7ee57f77377d6c5d69a469e980f6332a15e492" + integrity sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ== -flow-parser@^0.121.0: - version "0.121.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.121.0.tgz#9f9898eaec91a9f7c323e9e992d81ab5c58e618f" - integrity sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg== +follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== for-each@^0.3.3: version "0.3.3" @@ -5590,11 +5166,6 @@ for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== - form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -5604,13 +5175,13 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" - combined-stream "^1.0.6" + combined-stream "^1.0.8" mime-types "^2.1.12" forwarded@0.2.0: @@ -5630,14 +5201,29 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== -fs-extra@^1.0.0: +fs-constants@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" - integrity sha512-VerQV6vEKuhDWD2HGOybV6v5I73syoc/cXAbKlgTC7M/oFVEtklWlp9QH2Ijw3IaWDOQcMkldSPa7zXy79Z/UQ== + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@9.1.0, fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== dependencies: - graceful-fs "^4.1.2" - jsonfile "^2.1.0" - klaw "^1.0.0" + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" + integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" fs-extra@^8.1.0: version "8.1.0" @@ -5648,16 +5234,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-minipass@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" @@ -5677,7 +5253,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.1.2, fsevents@^2.3.2: +fsevents@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -5697,11 +5273,6 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== - functions-have-names@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" @@ -5784,11 +5355,16 @@ get-pkg-repo@^4.0.0: through2 "^2.0.0" yargs "^16.2.0" -get-port@^5.1.1: +get-port@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== +get-stream@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.0.tgz#3e0012cb6827319da2706e601a1583e8629a6718" + integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg== + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -5814,6 +5390,11 @@ get-symbol-from-current-process-h@^1.0.1, get-symbol-from-current-process-h@^1.0 resolved "https://registry.yarnpkg.com/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.2.tgz#510af52eaef873f7028854c3377f47f7bb200265" integrity sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw== +get-tsconfig@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.4.0.tgz#64eee64596668a81b8fce18403f94f245ee0d4e5" + integrity sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ== + get-uv-event-loop-napi-h@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.6.tgz#42b0b06b74c3ed21fbac8e7c72845fdb7a200208" @@ -5826,13 +5407,6 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== - dependencies: - assert-plus "^1.0.0" - git-config@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/git-config/-/git-config-0.0.7.tgz#a9c8a3ef07a776c3d72261356d8b727b62202b28" @@ -5867,20 +5441,20 @@ git-semver-tags@^4.1.1: meow "^8.0.0" semver "^6.0.0" -git-up@^4.0.0: - version "4.0.5" - resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.5.tgz#e7bb70981a37ea2fb8fe049669800a1f9a01d759" - integrity sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA== +git-up@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/git-up/-/git-up-7.0.0.tgz#bace30786e36f56ea341b6f69adfd83286337467" + integrity sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ== dependencies: - is-ssh "^1.3.0" - parse-url "^6.0.0" + is-ssh "^1.4.0" + parse-url "^8.1.0" -git-url-parse@^11.4.4: - version "11.6.0" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.6.0.tgz#c634b8de7faa66498a2b88932df31702c67df605" - integrity sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g== +git-url-parse@13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-13.1.0.tgz#07e136b5baa08d59fabdf0e33170de425adf07b4" + integrity sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA== dependencies: - git-up "^4.0.0" + git-up "^7.0.0" gitconfiglocal@^1.0.0: version "1.0.0" @@ -5889,14 +5463,33 @@ gitconfiglocal@^1.0.0: dependencies: ini "^1.3.2" -glob-parent@^5.1.1, glob-parent@^5.1.2: +glob-parent@5.1.2, glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -5908,12 +5501,33 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, gl once "^1.3.0" path-is-absolute "^1.0.0" +glob@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +glob@^9.2.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.0.tgz#be6e50d172d025c3fcf87903ae25b36b787c0bb0" + integrity sha512-EAZejC7JvnQINayvB/7BJbpZpNOJ8Lrw2OZNEvQxe0vaLn1SuwMcfV7/MNaX8L/T0wmptBFI4YMtDvSBxYDc7w== + dependencies: + fs.realpath "^1.0.0" + minimatch "^7.4.1" + minipass "^4.2.4" + path-scurry "^1.6.1" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.6.0, globals@^13.9.0: +globals@^13.19.0: version "13.20.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== @@ -5927,7 +5541,12 @@ globalthis@^1.0.3: dependencies: define-properties "^1.1.3" -globby@^11.0.2, globby@^11.1.0: +globalyzer@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" + integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== + +globby@11.1.0, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -5939,17 +5558,21 @@ globby@^11.0.2, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" - integrity sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g== +globby@^13.1.2: + version "13.1.3" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.3.tgz#f62baf5720bcb2c1330c8d4ef222ee12318563ff" + integrity sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw== dependencies: - array-union "^1.0.1" - dir-glob "^2.0.0" - glob "^7.1.2" - ignore "^3.3.5" - pify "^3.0.0" - slash "^1.0.0" + dir-glob "^3.0.1" + fast-glob "^3.2.11" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^4.0.0" + +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== gopd@^1.0.1: version "1.0.1" @@ -5958,11 +5581,16 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@4.2.10: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + grapheme-splitter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" @@ -5980,19 +5608,6 @@ handlebars@^4.7.6, handlebars@^4.7.7: optionalDependencies: uglify-js "^3.1.4" -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -6037,7 +5652,7 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has-unicode@^2.0.0, has-unicode@^2.0.1: +has-unicode@2.0.1, has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== @@ -6080,10 +5695,17 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -hermes-engine@~0.7.0: - version "0.7.2" - resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.7.2.tgz#303cd99d23f68e708b223aec2d49d5872985388b" - integrity sha512-E2DkRaO97gwL98LPhgfkMqhHiNsrAjIfEk3wWYn2Y31xdkdWn0572H7RnVcGujMJVqZNJvtknxlpsUb8Wzc3KA== +hermes-estree@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.8.0.tgz#530be27243ca49f008381c1f3e8b18fb26bf9ec0" + integrity sha512-W6JDAOLZ5pMPMjEiQGLCXSSV7pIBEgRR5zGkxgmzGSXHOxqV5dC/M1Zevqpbm9TZDE5tu358qZf8Vkzmsc+u7Q== + +hermes-parser@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.8.0.tgz#116dceaba32e45b16d6aefb5c4c830eaeba2d257" + integrity sha512-yZKalg1fTYG5eOiToLUaw69rQfZq/fi+/NtEXRU7N87K/XobNRhRWorh80oSge2lWUiZfTgUvRJH+XgZWrhoqA== + dependencies: + hermes-estree "0.8.0" hermes-profile-transformer@^0.0.6: version "0.0.6" @@ -6097,6 +5719,13 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +hosted-git-info@^3.0.6: + version "3.0.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.8.tgz#6e35d4cc87af2c5f816e4cb9ce350ba87a3f370d" + integrity sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw== + dependencies: + lru-cache "^6.0.0" + hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" @@ -6104,12 +5733,12 @@ hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: dependencies: lru-cache "^6.0.0" -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== +hosted-git-info@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-5.2.1.tgz#0ba1c97178ef91f3ab30842ae63d6a272341156f" + integrity sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw== dependencies: - whatwg-encoding "^1.0.5" + lru-cache "^7.5.1" html-escaper@^2.0.0: version "2.0.2" @@ -6141,14 +5770,14 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" + "@tootallnate/once" "2" + agent-base "6" + debug "4" https-proxy-agent@^5.0.0: version "5.0.1" @@ -6189,24 +5818,21 @@ ieee754@^1.1.13, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore-walk@^3.0.1, ignore-walk@^3.0.3: +ignore-walk@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== dependencies: minimatch "^3.0.4" -ignore@^3.3.5: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" - integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== - -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore-walk@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-5.0.1.tgz#5f199e23e1288f518d90358d461387788a154776" + integrity sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw== + dependencies: + minimatch "^5.0.1" -ignore@^5.2.0: +ignore@^5.0.4, ignore@^5.2.0: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== @@ -6272,7 +5898,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -6287,18 +5913,18 @@ iniparser@~1.0.5: resolved "https://registry.yarnpkg.com/iniparser/-/iniparser-1.0.5.tgz#836d6befe6dfbfcee0bccf1cf9f2acc7027f783d" integrity sha512-i40MWqgTU6h/70NtMsDVVDLjDYWwcIR1yIEVDPfxZIJno9z9L4s83p/V7vAu2i48Vj0gpByrkGFub7ko9XvPrw== -init-package-json@^2.0.2: - version "2.0.5" - resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-2.0.5.tgz#78b85f3c36014db42d8f32117252504f68022646" - integrity sha512-u1uGAtEFu3VA6HNl/yUWw57jmKEMx8SKOxHhxjGnOFUiIlFnohKDFg4ZrPpv9wWqk44nDxGJAtqjdQFm+9XXQA== +init-package-json@3.0.2, init-package-json@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-3.0.2.tgz#f5bc9bac93f2bdc005778bc2271be642fecfcd69" + integrity sha512-YhlQPEjNFqlGdzrBfDNRLhvoSgX7iQRgSxgsNknRQ9ITXFT7UMfVMWhBTOh2Y+25lRnGrv5Xz8yZwQ3ACR6T3A== dependencies: - npm-package-arg "^8.1.5" + npm-package-arg "^9.0.1" promzard "^0.3.0" - read "~1.0.1" - read-package-json "^4.1.1" + read "^1.0.7" + read-package-json "^5.0.0" semver "^7.3.5" validate-npm-package-license "^3.0.4" - validate-npm-package-name "^3.0.0" + validate-npm-package-name "^4.0.0" inquirer@^7.3.3: version "7.3.3" @@ -6319,7 +5945,28 @@ inquirer@^7.3.3: strip-ansi "^6.0.0" through "^2.3.6" -internal-slot@^1.0.4: +inquirer@^8.2.4, inquirer@^8.2.5: + version "8.2.5" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.5.tgz#d8654a7542c35a9b9e069d27e2df4858784d54f8" + integrity sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.5.5" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + wrap-ansi "^7.0.0" + +internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== @@ -6328,12 +5975,7 @@ internal-slot@^1.0.4: has "^1.0.3" side-channel "^1.0.4" -interpret@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== - -invariant@^2.2.4: +invariant@*, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -6369,13 +6011,13 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-array-buffer@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" - integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== dependencies: call-bind "^1.0.2" - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" is-typed-array "^1.1.10" is-arrayish@^0.2.1: @@ -6408,14 +6050,14 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-ci@^2.0.0: +is-ci@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== dependencies: ci-info "^2.0.0" -is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.9.0: +is-core-module@^2.10.0, is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: version "2.11.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== @@ -6466,6 +6108,11 @@ is-directory@^0.3.1: resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -6512,6 +6159,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" @@ -6546,16 +6198,16 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== -is-plain-obj@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -6568,11 +6220,6 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== - is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -6588,13 +6235,18 @@ is-shared-array-buffer@^1.0.2: dependencies: call-bind "^1.0.2" -is-ssh@^1.3.0: +is-ssh@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.4.0.tgz#4f8220601d2839d8fa624b3106f8e8884f01b8b2" integrity sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ== dependencies: protocols "^2.0.1" +is-stream@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -6637,10 +6289,10 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" -is-typedarray@^1.0.0, is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== is-weakref@^1.0.2: version "1.0.2" @@ -6659,6 +6311,13 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -6704,11 +6363,6 @@ isomorphic-webcrypto@^2.3.8: expo-random "*" react-native-securerandom "^0.1.1" -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" @@ -6751,370 +6405,301 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jest-changed-files@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" - integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== +jake@^10.8.5: + version "10.8.5" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" + integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.1" + minimatch "^3.0.4" + +jest-changed-files@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" + integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== dependencies: - "@jest/types" "^27.5.1" execa "^5.0.0" - throat "^6.0.1" + p-limit "^3.1.0" -jest-circus@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" - integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== +jest-circus@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.5.0.tgz#b5926989449e75bff0d59944bae083c9d7fb7317" + integrity sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA== dependencies: - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" - expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" + jest-each "^29.5.0" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + p-limit "^3.1.0" + pretty-format "^29.5.0" + pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" - throat "^6.0.1" -jest-cli@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" - integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== +jest-cli@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.5.0.tgz#b34c20a6d35968f3ee47a7437ff8e53e086b4a67" + integrity sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw== dependencies: - "@jest/core" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/core" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-config "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" prompts "^2.0.1" - yargs "^16.2.0" + yargs "^17.3.1" -jest-config@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" - integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== +jest-config@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.5.0.tgz#3cc972faec8c8aaea9ae158c694541b79f3748da" + integrity sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA== dependencies: - "@babel/core" "^7.8.0" - "@jest/test-sequencer" "^27.5.1" - "@jest/types" "^27.5.1" - babel-jest "^27.5.1" + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.5.0" + "@jest/types" "^29.5.0" + babel-jest "^29.5.0" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" - glob "^7.1.1" + glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-get-type "^27.5.1" - jest-jasmine2 "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runner "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-circus "^29.5.0" + jest-environment-node "^29.5.0" + jest-get-type "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-runner "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^27.5.1" + pretty-format "^29.5.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^26.0.0: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" - integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== - dependencies: - chalk "^4.0.0" - diff-sequences "^26.6.2" - jest-get-type "^26.3.0" - pretty-format "^26.6.2" - -jest-diff@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== +jest-diff@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" + integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== dependencies: chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" -jest-docblock@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" - integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== +jest-docblock@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" + integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== dependencies: detect-newline "^3.0.0" -jest-each@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" - integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== +jest-each@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.5.0.tgz#fc6e7014f83eac68e22b7195598de8554c2e5c06" + integrity sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" chalk "^4.0.0" - jest-get-type "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - -jest-environment-jsdom@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" - integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" - jsdom "^16.6.0" - -jest-environment-node@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" - integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + jest-get-type "^29.4.3" + jest-util "^29.5.0" + pretty-format "^29.5.0" + +jest-environment-node@^29.2.1, jest-environment-node@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967" + integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" + jest-mock "^29.5.0" + jest-util "^29.5.0" jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-get-type@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== - -jest-haste-map@^26.5.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" - integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== - dependencies: - "@jest/types" "^26.6.2" - "@types/graceful-fs" "^4.1.2" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - jest-regex-util "^26.0.0" - jest-serializer "^26.6.2" - jest-util "^26.6.2" - jest-worker "^26.6.2" - micromatch "^4.0.2" - sane "^4.0.3" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.1.2" +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== -jest-haste-map@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" - integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== +jest-haste-map@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de" + integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA== dependencies: - "@jest/types" "^27.5.1" - "@types/graceful-fs" "^4.1.2" + "@jest/types" "^29.5.0" + "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^27.5.1" - jest-serializer "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" + jest-worker "^29.5.0" micromatch "^4.0.4" - walker "^1.0.7" + walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" - integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^27.5.1" - is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - throat "^6.0.1" - -jest-leak-detector@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" - integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== +jest-leak-detector@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c" + integrity sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow== dependencies: - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" -jest-matcher-utils@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" - integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== +jest-matcher-utils@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" + integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== dependencies: chalk "^4.0.0" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" -jest-message-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" - integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== +jest-message-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" + integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^27.5.1" + pretty-format "^29.5.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" - integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== +jest-mock@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" + integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" "@types/node" "*" + jest-util "^29.5.0" jest-pnp-resolver@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== -jest-regex-util@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" - integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== - -jest-regex-util@^27.5.1: +jest-regex-util@^27.0.6: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-resolve-dependencies@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" - integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== + +jest-resolve-dependencies@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz#f0ea29955996f49788bf70996052aa98e7befee4" + integrity sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg== dependencies: - "@jest/types" "^27.5.1" - jest-regex-util "^27.5.1" - jest-snapshot "^27.5.1" + jest-regex-util "^29.4.3" + jest-snapshot "^29.5.0" -jest-resolve@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" - integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== +jest-resolve@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.5.0.tgz#b053cc95ad1d5f6327f0ac8aae9f98795475ecdc" + integrity sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w== dependencies: - "@jest/types" "^27.5.1" chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" + jest-haste-map "^29.5.0" jest-pnp-resolver "^1.2.2" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-util "^29.5.0" + jest-validate "^29.5.0" resolve "^1.20.0" - resolve.exports "^1.1.0" + resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" - integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== +jest-runner@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.5.0.tgz#6a57c282eb0ef749778d444c1d758c6a7693b6f8" + integrity sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ== dependencies: - "@jest/console" "^27.5.1" - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.5.0" + "@jest/environment" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" - emittery "^0.8.1" + emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-haste-map "^27.5.1" - jest-leak-detector "^27.5.1" - jest-message-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runtime "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - source-map-support "^0.5.6" - throat "^6.0.1" - -jest-runtime@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" - integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/globals" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + jest-docblock "^29.4.3" + jest-environment-node "^29.5.0" + jest-haste-map "^29.5.0" + jest-leak-detector "^29.5.0" + jest-message-util "^29.5.0" + jest-resolve "^29.5.0" + jest-runtime "^29.5.0" + jest-util "^29.5.0" + jest-watcher "^29.5.0" + jest-worker "^29.5.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.5.0.tgz#c83f943ee0c1da7eb91fa181b0811ebd59b03420" + integrity sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/globals" "^29.5.0" + "@jest/source-map" "^29.4.3" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - execa "^5.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" slash "^3.0.0" strip-bom "^4.0.0" -jest-serializer@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" - integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== - dependencies: - "@types/node" "*" - graceful-fs "^4.2.4" - -jest-serializer@^27.5.1: +jest-serializer@^27.0.6: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== @@ -7122,47 +6707,36 @@ jest-serializer@^27.5.1: "@types/node" "*" graceful-fs "^4.2.9" -jest-snapshot@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" - integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== +jest-snapshot@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.5.0.tgz#c9c1ce0331e5b63cd444e2f95a55a73b84b1e8ce" + integrity sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g== dependencies: - "@babel/core" "^7.7.2" + "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" - "@babel/types" "^7.0.0" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/babel__traverse" "^7.0.4" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/babel__traverse" "^7.0.6" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^27.5.1" + expect "^29.5.0" graceful-fs "^4.2.9" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - jest-haste-map "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-util "^27.5.1" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" natural-compare "^1.4.0" - pretty-format "^27.5.1" - semver "^7.3.2" - -jest-util@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" - integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== - dependencies: - "@jest/types" "^26.6.2" - "@types/node" "*" - chalk "^4.0.0" - graceful-fs "^4.2.4" - is-ci "^2.0.0" - micromatch "^4.0.2" + pretty-format "^29.5.0" + semver "^7.3.5" -jest-util@^27.0.0, jest-util@^27.5.1: +jest-util@^27.2.0: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== @@ -7174,6 +6748,18 @@ jest-util@^27.0.0, jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.0.0, jest-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^26.5.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" @@ -7186,67 +6772,65 @@ jest-validate@^26.5.2: leven "^3.1.0" pretty-format "^26.6.2" -jest-validate@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" - integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== +jest-validate@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.5.0.tgz#8e5a8f36178d40e47138dc00866a5f3bd9916ffc" + integrity sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.5.0" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^27.5.1" + jest-get-type "^29.4.3" leven "^3.1.0" - pretty-format "^27.5.1" + pretty-format "^29.5.0" -jest-watcher@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" - integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== +jest-watcher@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.5.0.tgz#cf7f0f949828ba65ddbbb45c743a382a4d911363" + integrity sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA== dependencies: - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^27.5.1" + emittery "^0.13.1" + jest-util "^29.5.0" string-length "^4.0.1" -jest-worker@^26.0.0, jest-worker@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== +jest-worker@^27.2.0: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== dependencies: "@types/node" "*" merge-stream "^2.0.0" - supports-color "^7.0.0" + supports-color "^8.0.0" -jest-worker@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== +jest-worker@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" + integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== dependencies: "@types/node" "*" + jest-util "^29.5.0" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.0.4: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" - integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== +jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" + integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== dependencies: - "@jest/core" "^27.5.1" + "@jest/core" "^29.5.0" + "@jest/types" "^29.5.0" import-local "^3.0.2" - jest-cli "^27.5.1" - -jetifier@^1.6.2: - version "1.6.8" - resolved "https://registry.yarnpkg.com/jetifier/-/jetifier-1.6.8.tgz#e88068697875cbda98c32472902c4d3756247798" - integrity sha512-3Zi16h6L5tXDRQJTb221cnRoVG9/9OvreLdLU2/ZjRv/GILL+2Cemt0IKvkowwkDpvouAU1DQPOJ7qaiHeIdrw== + jest-cli "^29.5.0" joi@^17.2.1: - version "17.8.3" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.8.3.tgz#d772fe27a87a5cda21aace5cf11eee8671ca7e6f" - integrity sha512-q5Fn6Tj/jR8PfrLrx4fpGH4v9qM6o+vDUfD4/3vxxyg34OmKcNqYZ1qn2mpLza96S8tL0p0rIw2gOZX+/cTg9w== + version "17.8.4" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.8.4.tgz#f2d91ab8acd3cca4079ba70669c65891739234aa" + integrity sha512-jjdRHb5WtL+KgSHvOULQEPPv4kcl+ixd1ybOFQq3rWLgEEqc03QMmilodL0GVJE14U/SQDXkUhQUSZANGDH/AA== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -7254,12 +6838,24 @@ joi@^17.2.1: "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" +js-sdsl@^4.1.4: + version "4.3.0" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" + integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1: +js-yaml@4.1.0, js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +js-yaml@^3.10.0, js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -7267,74 +6863,36 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== - -jsc-android@^245459.0.0: - version "245459.0.0" - resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-245459.0.0.tgz#e584258dd0b04c9159a27fb104cd5d491fd202c9" - integrity sha512-wkjURqwaB1daNkDi2OYYbsLnIdC/lUM2nPXQKRs5pqEU9chDg435bjvo+LSaHotDENygHQDHe+ntUkkw2gwMtg== - -jscodeshift@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.11.0.tgz#4f95039408f3f06b0e39bb4d53bc3139f5330e2f" - integrity sha512-SdRK2C7jjs4k/kT2mwtO07KJN9RnjxtKn03d9JVj6c3j9WwaLcFYsICYDnLAzY0hp+wG2nxl+Cm2jWLiNVYb8g== - dependencies: - "@babel/core" "^7.1.6" - "@babel/parser" "^7.1.6" - "@babel/plugin-proposal-class-properties" "^7.1.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.1.0" - "@babel/plugin-proposal-optional-chaining" "^7.1.0" - "@babel/plugin-transform-modules-commonjs" "^7.1.0" - "@babel/preset-flow" "^7.0.0" - "@babel/preset-typescript" "^7.1.0" - "@babel/register" "^7.0.0" +jsc-android@^250231.0.0: + version "250231.0.0" + resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-250231.0.0.tgz#91720f8df382a108872fa4b3f558f33ba5e95262" + integrity sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw== + +jscodeshift@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.13.1.tgz#69bfe51e54c831296380585c6d9e733512aecdef" + integrity sha512-lGyiEbGOvmMRKgWk4vf+lUrCWO/8YR8sUR3FKF1Cq5fovjZDlIcw3Hu5ppLHAnEXshVffvaM0eyuY/AbOeYpnQ== + dependencies: + "@babel/core" "^7.13.16" + "@babel/parser" "^7.13.16" + "@babel/plugin-proposal-class-properties" "^7.13.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.13.8" + "@babel/plugin-proposal-optional-chaining" "^7.13.12" + "@babel/plugin-transform-modules-commonjs" "^7.13.8" + "@babel/preset-flow" "^7.13.13" + "@babel/preset-typescript" "^7.13.0" + "@babel/register" "^7.13.16" babel-core "^7.0.0-bridge.0" - colors "^1.1.2" + chalk "^4.1.2" flow-parser "0.*" graceful-fs "^4.2.4" micromatch "^3.1.10" neo-async "^2.5.0" node-dir "^0.1.17" - recast "^0.20.3" - temp "^0.8.1" + recast "^0.20.4" + temp "^0.8.4" write-file-atomic "^2.3.0" -jsdom@^16.6.0: - version "16.7.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -7350,7 +6908,7 @@ json-parse-better-errors@^1.0.1: resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== -json-parse-even-better-errors@^2.3.0: +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== @@ -7360,22 +6918,17 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - -json-schema@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" - integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: +json-stringify-nice@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67" + integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw== + +json-stringify-safe@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== @@ -7387,11 +6940,6 @@ json-text-sequence@~0.3.0: dependencies: "@sovpro/delimited-stream" "^1.1.0" -json5@2.x, json5@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" @@ -7399,12 +6947,15 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -jsonfile@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" - integrity sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw== - optionalDependencies: - graceful-fs "^4.1.6" +json5@^2.2.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonc-parser@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== jsonfile@^4.0.0: version "4.0.0" @@ -7422,25 +6973,20 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsonify@~0.0.0: - version "0.0.1" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" - integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== - jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== -jsprim@^1.2.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" - integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" +just-diff-apply@^5.2.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/just-diff-apply/-/just-diff-apply-5.5.0.tgz#771c2ca9fa69f3d2b54e7c3f5c1dfcbcc47f9f0f" + integrity sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw== + +just-diff@^5.0.1: + version "5.2.0" + resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.2.0.tgz#60dca55891cf24cd4a094e33504660692348a241" + integrity sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw== jwt-decode@^3.1.2: version "3.1.2" @@ -7471,13 +7017,6 @@ kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -klaw@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" - integrity sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw== - optionalDependencies: - graceful-fs "^4.1.9" - kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -7496,29 +7035,87 @@ ky@^0.25.1: resolved "https://registry.yarnpkg.com/ky/-/ky-0.25.1.tgz#0df0bd872a9cc57e31acd5dbc1443547c881bfbc" integrity sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA== -lerna@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-4.0.0.tgz#b139d685d50ea0ca1be87713a7c2f44a5b678e9e" - integrity sha512-DD/i1znurfOmNJb0OBw66NmNqiM8kF6uIrzrJ0wGE3VNdzeOhz9ziWLYiRaZDGGwgbcjOo6eIfcx9O5Qynz+kg== - dependencies: - "@lerna/add" "4.0.0" - "@lerna/bootstrap" "4.0.0" - "@lerna/changed" "4.0.0" - "@lerna/clean" "4.0.0" - "@lerna/cli" "4.0.0" - "@lerna/create" "4.0.0" - "@lerna/diff" "4.0.0" - "@lerna/exec" "4.0.0" - "@lerna/import" "4.0.0" - "@lerna/info" "4.0.0" - "@lerna/init" "4.0.0" - "@lerna/link" "4.0.0" - "@lerna/list" "4.0.0" - "@lerna/publish" "4.0.0" - "@lerna/run" "4.0.0" - "@lerna/version" "4.0.0" +lerna@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-6.5.1.tgz#eb89698e5b2891f5681f39d980f63d0519fc464f" + integrity sha512-Va1bysubwWdoWZ1ncKcoTGBXNAu/10/TwELb550TTivXmEWjCCdek4eX0BNLTEYKxu3tpV2UEeqVisUiWGn4WA== + dependencies: + "@lerna/child-process" "6.5.1" + "@lerna/create" "6.5.1" + "@npmcli/arborist" "5.3.0" + "@npmcli/run-script" "4.1.7" + "@nrwl/devkit" ">=15.5.2 < 16" + "@octokit/plugin-enterprise-rest" "6.0.1" + "@octokit/rest" "19.0.3" + byte-size "7.0.0" + chalk "4.1.0" + clone-deep "4.0.1" + cmd-shim "5.0.0" + columnify "1.6.0" + config-chain "1.1.12" + conventional-changelog-angular "5.0.12" + conventional-changelog-core "4.2.4" + conventional-recommended-bump "6.1.0" + cosmiconfig "7.0.0" + dedent "0.7.0" + dot-prop "6.0.1" + envinfo "^7.7.4" + execa "5.0.0" + fs-extra "9.1.0" + get-port "5.1.1" + get-stream "6.0.0" + git-url-parse "13.1.0" + glob-parent "5.1.2" + globby "11.1.0" + graceful-fs "4.2.10" + has-unicode "2.0.1" import-local "^3.0.2" - npmlog "^4.1.2" + init-package-json "3.0.2" + inquirer "^8.2.4" + is-ci "2.0.0" + is-stream "2.0.0" + js-yaml "^4.1.0" + libnpmaccess "6.0.3" + libnpmpublish "6.0.4" + load-json-file "6.2.0" + make-dir "3.1.0" + minimatch "3.0.5" + multimatch "5.0.0" + node-fetch "2.6.7" + npm-package-arg "8.1.1" + npm-packlist "5.1.1" + npm-registry-fetch "13.3.0" + npmlog "^6.0.2" + nx ">=15.5.2 < 16" + p-map "4.0.0" + p-map-series "2.1.0" + p-pipe "3.1.0" + p-queue "6.6.2" + p-reduce "2.1.0" + p-waterfall "2.1.1" + pacote "13.6.1" + path-exists "4.0.0" + pify "5.0.0" + read-cmd-shim "3.0.0" + read-package-json "5.0.1" + resolve-from "5.0.0" + rimraf "^3.0.2" + semver "7.3.4" + signal-exit "3.0.7" + slash "3.0.0" + ssri "9.0.1" + strong-log-transformer "2.1.0" + tar "6.1.11" + temp-dir "1.0.0" + typescript "^3 || ^4" + upath "^2.0.1" + uuid "8.3.2" + validate-npm-package-license "3.0.4" + validate-npm-package-name "4.0.0" + write-file-atomic "4.0.1" + write-pkg "4.0.0" + yargs "16.2.0" + yargs-parser "20.2.4" leven@^3.1.0: version "3.1.0" @@ -7533,56 +7130,43 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -libnpmaccess@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-4.0.3.tgz#dfb0e5b0a53c315a2610d300e46b4ddeb66e7eec" - integrity sha512-sPeTSNImksm8O2b6/pf3ikv4N567ERYEpeKRPSmqlNt1dTZbvgpJIzg5vAhXHpw2ISBsELFRelk0jEahj1c6nQ== +libnpmaccess@6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-6.0.3.tgz#473cc3e4aadb2bc713419d92e45d23b070d8cded" + integrity sha512-4tkfUZprwvih2VUZYMozL7EMKgQ5q9VW2NtRyxWtQWlkLTAWHRklcAvBN49CVqEkhUw7vTX2fNgB5LzgUucgYg== dependencies: aproba "^2.0.0" minipass "^3.1.1" - npm-package-arg "^8.1.2" - npm-registry-fetch "^11.0.0" + npm-package-arg "^9.0.1" + npm-registry-fetch "^13.0.0" -libnpmpublish@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-4.0.2.tgz#be77e8bf5956131bcb45e3caa6b96a842dec0794" - integrity sha512-+AD7A2zbVeGRCFI2aO//oUmapCwy7GHqPXFJh3qpToSRNU+tXKJ2YFUgjt04LPPAf2dlEH95s6EhIHM1J7bmOw== +libnpmpublish@6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-6.0.4.tgz#adb41ec6b0c307d6f603746a4d929dcefb8f1a0b" + integrity sha512-lvAEYW8mB8QblL6Q/PI/wMzKNvIrF7Kpujf/4fGS/32a2i3jzUXi04TNyIBcK6dQJ34IgywfaKGh+Jq4HYPFmg== dependencies: - normalize-package-data "^3.0.2" - npm-package-arg "^8.1.2" - npm-registry-fetch "^11.0.0" - semver "^7.1.3" - ssri "^8.0.1" + normalize-package-data "^4.0.0" + npm-package-arg "^9.0.1" + npm-registry-fetch "^13.0.0" + semver "^7.3.7" + ssri "^9.0.0" -libphonenumber-js@^1.10.14, libphonenumber-js@^1.9.7: - version "1.10.21" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.21.tgz#860312cbec1389e36e28389161025c7817a3ae32" - integrity sha512-/udZhx49av2r2gZR/+xXSrwcR8smX/sDNrVpOFrvW+CA26TfYTVZfwb3MIDvmwAYMLs7pXuJjZX0VxxGpqPhsA== +libphonenumber-js@^1.10.14: + version "1.10.24" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.24.tgz#a1744cf29df86d5a587562ea28dde12320eb6ab6" + integrity sha512-3Dk8f5AmrcWqg+oHhmm9hwSTqpWHBdSqsHmjCJGroULFubi0+x7JEIGmRZCuL3TI8Tx39xaKqfnhsDQ4ALa/Nw== lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw== - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" +lines-and-columns@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b" + integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w== -load-json-file@^6.2.0: +load-json-file@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1" integrity sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ== @@ -7592,6 +7176,16 @@ load-json-file@^6.2.0: strip-bom "^4.0.0" type-fest "^0.6.0" +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw== + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -7622,11 +7216,6 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA== - lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -7652,42 +7241,23 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.template@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.throttle@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ== -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== - -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: +lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: - chalk "^2.0.1" + chalk "^4.1.0" + is-unicode-supported "^0.1.0" logkitty@^0.7.1: version "0.7.1" @@ -7719,15 +7289,27 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.14.1, lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + lru_map@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.4.1.tgz#f7b4046283c79fb7370c36f8fca6aee4324b0a98" integrity sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg== -luxon@^1.27.0: - version "1.28.1" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.1.tgz#528cdf3624a54506d710290a2341aa8e6e6c61b0" - integrity sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw== +luxon@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.3.0.tgz#d73ab5b5d2b49a461c47cedbc7e73309b4805b48" + integrity sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg== + +make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" @@ -7737,40 +7319,34 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -make-dir@^3.0.0, make-dir@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - make-error@1.x, make-error@^1.1.1, make-error@^1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -make-fetch-happen@^8.0.9: - version "8.0.14" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" - integrity sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ== +make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6: + version "10.2.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" + integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== dependencies: - agentkeepalive "^4.1.3" - cacache "^15.0.5" + agentkeepalive "^4.2.1" + cacache "^16.1.0" http-cache-semantics "^4.1.0" - http-proxy-agent "^4.0.1" + http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0" is-lambda "^1.0.1" - lru-cache "^6.0.0" - minipass "^3.1.3" + lru-cache "^7.7.1" + minipass "^3.1.6" minipass-collect "^1.0.2" - minipass-fetch "^1.3.2" + minipass-fetch "^2.0.3" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" + negotiator "^0.6.3" promise-retry "^2.0.1" - socks-proxy-agent "^5.0.0" - ssri "^8.0.0" + socks-proxy-agent "^7.0.0" + ssri "^9.0.0" -make-fetch-happen@^9.0.1, make-fetch-happen@^9.1.0: +make-fetch-happen@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== @@ -7831,6 +7407,11 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== +memoize-one@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" + integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== + meow@^8.0.0: version "8.1.2" resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" @@ -7868,92 +7449,106 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -metro-babel-register@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.64.0.tgz#1a2d23f68da8b8ee42e78dca37ad21a5f4d3647d" - integrity sha512-Kf6YvE3kIRumGnjK0Q9LqGDIdnsX9eFGtNBmBuCVDuB9wGGA/5CgX8We8W7Y44dz1RGTcHJRhfw5iGg+pwC3aQ== - dependencies: - "@babel/core" "^7.0.0" - "@babel/plugin-proposal-class-properties" "^7.0.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-proposal-optional-chaining" "^7.0.0" - "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/register" "^7.0.0" - escape-string-regexp "^1.0.5" - -metro-babel-transformer@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.64.0.tgz#a21f8a989a5ea60c1109456e21bd4d9374194ea0" - integrity sha512-itZaxKTgmKGEZWxNzbSZBc22NngrMZzoUNuU92aHSTGkYi2WH4XlvzEHsstmIKHMsRVKl75cA+mNmgk4gBFJKw== +metro-babel-transformer@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.73.8.tgz#521374cb9234ba126f3f8d63588db5901308b4ed" + integrity sha512-GO6H/W2RjZ0/gm1pIvdO9EP34s3XN6kzoeyxqmfqKfYhJmYZf1SzXbyiIHyMbJNwJVrsKuHqu32+GopTlKscWw== dependencies: - "@babel/core" "^7.0.0" - metro-source-map "0.64.0" + "@babel/core" "^7.20.0" + hermes-parser "0.8.0" + metro-source-map "0.73.8" nullthrows "^1.1.1" -metro-cache-key@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.64.0.tgz#98d0a94332453c4c52b74f72c07cc62a5c264c4f" - integrity sha512-O9B65G8L/fopck45ZhdRosyVZdMtUQuX5mBWEC1NRj02iWBIUPLmYMjrunqIe8vHipCMp3DtTCm/65IlBmO8jg== +metro-cache-key@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.73.8.tgz#afc9f63454edbd9d207544445a66e8a4e119462d" + integrity sha512-VzFGu4kJGIkLjyDgVoM2ZxIHlMdCZWMqVIux9N+EeyMVMvGXTiXW8eGROgxzDhVjyR58IjfMsYpRCKz5dR+2ew== -metro-cache@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.64.0.tgz#a769503e12521d9e9d95ce5840ffb2efdb4e8703" - integrity sha512-QvGfxe/1QQYM9XOlR8W1xqE9eHDw/AgJIgYGn/TxZxBu9Zga+Rgs1omeSZju45D8w5VWgMr83ma5kACgzvOecg== +metro-cache@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.73.8.tgz#85e2d7f7c7c74d1f942b7ecd168f7aceb987d883" + integrity sha512-/uFbTIw813Rvb8kSAIHvax9gWl41dtgjY2SpJLNIBLdQ6oFZ3CVo3ahZIiEZOrCeHl9xfGn5tmvNb8CEFa/Q5w== dependencies: - metro-core "0.64.0" - mkdirp "^0.5.1" - rimraf "^2.5.4" + metro-core "0.73.8" + rimraf "^3.0.2" -metro-config@0.64.0, metro-config@^0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.64.0.tgz#b634fa05cffd06b1e50e4339c200f90a42924afb" - integrity sha512-QhM4asnX5KhlRWaugwVGNNXhX0Z85u5nK0UQ/A90bBb4xWyXqUe20e788VtdA75rkQiiI6wXTCIHWT0afbnjwQ== +metro-config@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.73.8.tgz#8f6c22c94528919635c6688ed8d2ad8a10c70b27" + integrity sha512-sAYq+llL6ZAfro64U99ske8HcKKswxX4wIZbll9niBKG7TkWm7tfMY1jO687XEmE4683rHncZeBRav9pLngIzg== dependencies: cosmiconfig "^5.0.5" jest-validate "^26.5.2" - metro "0.64.0" - metro-cache "0.64.0" - metro-core "0.64.0" - metro-runtime "0.64.0" + metro "0.73.8" + metro-cache "0.73.8" + metro-core "0.73.8" + metro-runtime "0.73.8" -metro-core@0.64.0, metro-core@^0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.64.0.tgz#7616b27acfe7baa476f6cd6bd9e70ae64fa62541" - integrity sha512-v8ZQ5j72EaUwamQ8pLfHlOHTyp7SbdazvHPzFGDpHnwIQqIT0Bw3Syg8R4regTlVG3ngpeSEAi005UITljmMcQ== +metro-core@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.73.8.tgz#a31ba7d7bfe3f4c2ac2c7a2493aa4229ecad701e" + integrity sha512-Aew4dthbZf8bRRjlYGL3cnai3+LKYTf6mc7YS2xLQRWtgGZ1b/H8nQtBvXZpfRYFcS84UeEQ10vwIf5eR3qPdQ== dependencies: - jest-haste-map "^26.5.2" lodash.throttle "^4.1.1" - metro-resolver "0.64.0" + metro-resolver "0.73.8" + +metro-file-map@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.73.8.tgz#88d666e7764e1b0adf5fd634d91e97e3135d2db7" + integrity sha512-CM552hUO9om02jJdLszOCIDADKNaaeVz8CjYXItndvgr5jmFlQYAR+UMvaDzeT8oYdAV1DXAljma2CS2UBymPg== + dependencies: + abort-controller "^3.0.0" + anymatch "^3.0.3" + debug "^2.2.0" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + invariant "^2.2.4" + jest-regex-util "^27.0.6" + jest-serializer "^27.0.6" + jest-util "^27.2.0" + jest-worker "^27.2.0" + micromatch "^4.0.4" + nullthrows "^1.1.1" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" -metro-hermes-compiler@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.64.0.tgz#e6043d7aa924e5b2be99bd3f602e693685d15386" - integrity sha512-CLAjVDWGAoGhbi2ZyPHnH5YDdfrDIx6+tzFWfHGIMTZkYBXsYta9IfYXBV8lFb6BIbrXLjlXZAOoosknetMPOA== +metro-hermes-compiler@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.73.8.tgz#c522e2c97afc8bdc249755d88146a75720bc2498" + integrity sha512-2d7t+TEoQLk+jyXgBykmAtPPJK2B46DB3qUYIMKDFDDaKzCljrojyVuGgQq6SM1f95fe6HDAQ3K9ihTjeB90yw== -metro-inspector-proxy@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.64.0.tgz#9a481b3f49773d5418e028178efec68f861bec88" - integrity sha512-KywbH3GNSz9Iqw4UH3smgaV2dBHHYMISeN7ORntDL/G+xfgPc6vt13d+zFb907YpUcXj5N0vdoiAHI5V/0y8IA== +metro-inspector-proxy@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.73.8.tgz#67d5aadfc33fe97f61c716eb168db4bd5d0e3c96" + integrity sha512-F0QxwDTox0TDeXVRN7ZmI7BknBjPDVKQ1ZeKznFBiMa0SXiD1kzoksfpDbZ6hTEKrhVM9Ep0YQmC7avwZouOnA== dependencies: connect "^3.6.5" debug "^2.2.0" - ws "^1.1.5" - yargs "^15.3.1" + ws "^7.5.1" + yargs "^17.5.1" + +metro-minify-terser@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.73.8.tgz#a0fe857d6aaf99cba3a2aef59ee06ac409682c6b" + integrity sha512-pnagyXAoMPhihWrHRIWqCxrP6EJ8Hfugv5RXBb6HbOANmwajn2uQuzeu18+dXaN1yPoDCMCgpg/UA4ibFN5jtQ== + dependencies: + terser "^5.15.0" -metro-minify-uglify@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.64.0.tgz#da6ab4dda030e3211f5924e7f41ed308d466068f" - integrity sha512-DRwRstqXR5qfte9Nuwoov5dRXxL7fJeVlO5fGyOajWeO3+AgPjvjXh/UcLJqftkMWTPGUFuzAD5/7JC5v5FLWw== +metro-minify-uglify@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.73.8.tgz#b2e2430014c340479db4fc393a2ea4c5bad75ecd" + integrity sha512-9wZqKfraVfmtMXdOzRyan+6r1woQXqqa4KeXfVh7+Mxl+5+J0Lmw6EvTrWawsaOEpvpn32q9MfoHC1d8plDJwA== dependencies: uglify-es "^3.1.9" -metro-react-native-babel-preset@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.64.0.tgz#76861408681dfda3c1d962eb31a8994918c976f8" - integrity sha512-HcZ0RWQRuJfpPiaHyFQJzcym+/dDIVUPwUAXWoub/C4GkGu+mPjp8vqK6g0FxokCnnI2TK0gZTza2IDfiNNscQ== +metro-react-native-babel-preset@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.73.8.tgz#04908f264f5d99c944ae20b5b11f659431328431" + integrity sha512-spNrcQJTbQntEIqJnCA6yL4S+dzV9fXCk7U+Rm7yJasZ4o4Frn7jP23isu7FlZIp1Azx1+6SbP7SgQM+IP5JgQ== dependencies: - "@babel/core" "^7.0.0" + "@babel/core" "^7.20.0" + "@babel/plugin-proposal-async-generator-functions" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-export-default-from" "^7.0.0" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" @@ -7962,27 +7557,25 @@ metro-react-native-babel-preset@0.64.0: "@babel/plugin-proposal-optional-chaining" "^7.0.0" "@babel/plugin-syntax-dynamic-import" "^7.0.0" "@babel/plugin-syntax-export-default-from" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.2.0" + "@babel/plugin-syntax-flow" "^7.18.0" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0" "@babel/plugin-syntax-optional-chaining" "^7.0.0" "@babel/plugin-transform-arrow-functions" "^7.0.0" + "@babel/plugin-transform-async-to-generator" "^7.0.0" "@babel/plugin-transform-block-scoping" "^7.0.0" "@babel/plugin-transform-classes" "^7.0.0" "@babel/plugin-transform-computed-properties" "^7.0.0" "@babel/plugin-transform-destructuring" "^7.0.0" - "@babel/plugin-transform-exponentiation-operator" "^7.0.0" "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-for-of" "^7.0.0" "@babel/plugin-transform-function-name" "^7.0.0" "@babel/plugin-transform-literals" "^7.0.0" "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/plugin-transform-object-assign" "^7.0.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.0.0" "@babel/plugin-transform-parameters" "^7.0.0" "@babel/plugin-transform-react-display-name" "^7.0.0" "@babel/plugin-transform-react-jsx" "^7.0.0" "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" - "@babel/plugin-transform-regenerator" "^7.0.0" "@babel/plugin-transform-runtime" "^7.0.0" "@babel/plugin-transform-shorthand-properties" "^7.0.0" "@babel/plugin-transform-spread" "^7.0.0" @@ -7993,144 +7586,147 @@ metro-react-native-babel-preset@0.64.0: "@babel/template" "^7.0.0" react-refresh "^0.4.0" -metro-react-native-babel-transformer@0.64.0, metro-react-native-babel-transformer@^0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.64.0.tgz#eafef756972f20efdc51bd5361d55f8598355623" - integrity sha512-K1sHO3ODBFCr7uEiCQ4RvVr+cQg0EHQF8ChVPnecGh/WDD8udrTq9ECwB0dRfMjAvlsHtRUlJm6ZSI8UPgum2w== - dependencies: - "@babel/core" "^7.0.0" - babel-preset-fbjs "^3.3.0" - metro-babel-transformer "0.64.0" - metro-react-native-babel-preset "0.64.0" - metro-source-map "0.64.0" +metro-react-native-babel-transformer@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.73.8.tgz#cbcd4b243216878431dc4311ce46f02a928e3991" + integrity sha512-oH/LCCJPauteAE28c0KJAiSrkV+1VJbU0PwA9UwaWnle+qevs/clpKQ8LrIr33YbBj4CiI1kFoVRuNRt5h4NFg== + dependencies: + "@babel/core" "^7.20.0" + babel-preset-fbjs "^3.4.0" + hermes-parser "0.8.0" + metro-babel-transformer "0.73.8" + metro-react-native-babel-preset "0.73.8" + metro-source-map "0.73.8" nullthrows "^1.1.1" -metro-resolver@0.64.0, metro-resolver@^0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.64.0.tgz#21126b44f31346ac2ce0b06b77ef65e8c9e2294a" - integrity sha512-cJ26Id8Zf+HmS/1vFwu71K3u7ep/+HeXXAJIeVDYf+niE7AWB9FijyMtAlQgbD8elWqv1leJCnQ/xHRFBfGKYA== +metro-resolver@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.73.8.tgz#65cc158575d130363296f66a33257c7971228640" + integrity sha512-GiBWont7/OgAftkkj2TiEp+Gf1PYZUk8xV4MbtnQjIKyy3MlGY3GbpMQ1BHih9GUQqlF0n9jsUlC2K5P0almXQ== dependencies: absolute-path "^0.0.0" -metro-runtime@0.64.0, metro-runtime@^0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.64.0.tgz#cdaa1121d91041bf6345f2a69eb7c2fb289eff7b" - integrity sha512-m7XbWOaIOeFX7YcxUhmnOi6Pg8EaeL89xyZ+quZyZVF1aNoTr4w8FfbKxvijpjsytKHIZtd+43m2Wt5JrqyQmQ== +metro-runtime@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.73.8.tgz#dadae7c154fbbde24390cf7f7e7d934a2768cd18" + integrity sha512-M+Bg9M4EN5AEpJ8NkiUsawD75ifYvYfHi05w6QzHXaqOrsTeaRbbeLuOGCYxU2f/tPg17wQV97/rqUQzs9qEtA== + dependencies: + "@babel/runtime" "^7.0.0" + react-refresh "^0.4.0" -metro-source-map@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.64.0.tgz#4310e17c3d4539c6369688022494ad66fa4d39a1" - integrity sha512-OCG2rtcp5cLEGYvAbfkl6mEc0J2FPRP4/UCEly+juBk7hawS9bCBMBfhJm/HIsvY1frk6nT2Vsl1O8YBbwyx2g== +metro-source-map@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.73.8.tgz#5134174e3d43de26ad331b95f637944c6547d441" + integrity sha512-wozFXuBYMAy7b8BCYwC+qoXsvayVJBHWtSTlSLva99t+CoUSG9JO9kg1umzbOz28YYPxKmvb/wbnLMkHdas2cA== dependencies: - "@babel/traverse" "^7.0.0" - "@babel/types" "^7.0.0" + "@babel/traverse" "^7.20.0" + "@babel/types" "^7.20.0" invariant "^2.2.4" - metro-symbolicate "0.64.0" + metro-symbolicate "0.73.8" nullthrows "^1.1.1" - ob1 "0.64.0" + ob1 "0.73.8" source-map "^0.5.6" vlq "^1.0.0" -metro-symbolicate@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.64.0.tgz#405c21438ab553c29f6841da52ca76ee87bb06ac" - integrity sha512-qIi+YRrDWnLVmydj6gwidYLPaBsakZRibGWSspuXgHAxOI3UuLwlo4dpQ73Et0gyHjI7ZvRMRY8JPiOntf9AQQ== +metro-symbolicate@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.73.8.tgz#96920f607bce484283d822ee5fe18d932f69c03d" + integrity sha512-xkBAcceYYp0GGdCCuMzkCF1ejHsd0lYlbKBkjSRgM0Nlj80VapPaSwumYoAvSaDxcbkvS7/sCjURGp5DsSFgRQ== dependencies: invariant "^2.2.4" - metro-source-map "0.64.0" + metro-source-map "0.73.8" nullthrows "^1.1.1" source-map "^0.5.6" through2 "^2.0.1" vlq "^1.0.0" -metro-transform-plugins@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.64.0.tgz#41d3dce0f2966bbd79fea1ecff61bcc8a00e4665" - integrity sha512-iTIRBD/wBI98plfxj8jAoNUUXfXLNlyvcjPtshhpGvdwu9pzQilGfnDnOaaK+vbITcOk9w5oQectXyJwAqTr1A== +metro-transform-plugins@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.73.8.tgz#07be7fd94a448ea1b245ab02ce7d277d757f9a32" + integrity sha512-IxjlnB5eA49M0WfvPEzvRikK3Rr6bECUUfcZt/rWpSphq/mttgyLYcHQ+VTZZl0zHolC3cTLwgoDod4IIJBn1A== dependencies: - "@babel/core" "^7.0.0" - "@babel/generator" "^7.5.0" + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" "@babel/template" "^7.0.0" - "@babel/traverse" "^7.0.0" + "@babel/traverse" "^7.20.0" nullthrows "^1.1.1" -metro-transform-worker@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.64.0.tgz#f94429b2c42b13cb1c93be4c2e25e97f2d27ca60" - integrity sha512-wegRtK8GyLF6IPZRBJp+zsORgA4iX0h1DRpknyAMDCtSbJ4VU2xV/AojteOgAsDvY3ucAGsvfuZLNDJHUdUNHQ== +metro-transform-worker@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.73.8.tgz#701a006c2b4d93f1bb24802f3f2834c963153db9" + integrity sha512-B8kR6lmcvyG4UFSF2QDfr/eEnWJvg0ZadooF8Dg6m/3JSm9OAqfSoC0YrWqAuvtWImNDnbeKWN7/+ns44Hv6tg== dependencies: - "@babel/core" "^7.0.0" - "@babel/generator" "^7.5.0" - "@babel/parser" "^7.0.0" - "@babel/types" "^7.0.0" - babel-preset-fbjs "^3.3.0" - metro "0.64.0" - metro-babel-transformer "0.64.0" - metro-cache "0.64.0" - metro-cache-key "0.64.0" - metro-hermes-compiler "0.64.0" - metro-source-map "0.64.0" - metro-transform-plugins "0.64.0" + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" + "@babel/parser" "^7.20.0" + "@babel/types" "^7.20.0" + babel-preset-fbjs "^3.4.0" + metro "0.73.8" + metro-babel-transformer "0.73.8" + metro-cache "0.73.8" + metro-cache-key "0.73.8" + metro-hermes-compiler "0.73.8" + metro-source-map "0.73.8" + metro-transform-plugins "0.73.8" nullthrows "^1.1.1" -metro@0.64.0, metro@^0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.64.0.tgz#0091a856cfbcc94dd576da563eee466e96186195" - integrity sha512-G2OC08Rzfs0kqnSEuKo2yZxR+/eNUpA93Ru45c60uN0Dw3HPrDi+ZBipgFftC6iLE0l+6hu8roFFIofotWxybw== +metro@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.73.8.tgz#25f014e4064eb34a4833c316e0a9094528061a8c" + integrity sha512-2EMJME9w5x7Uzn+DnQ4hzWr33u/aASaOBGdpf4lxbrlk6/vl4UBfX1sru6KU535qc/0Z1BMt4Vq9qsP3ZGFmWg== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/core" "^7.0.0" - "@babel/generator" "^7.5.0" - "@babel/parser" "^7.0.0" + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" + "@babel/parser" "^7.20.0" "@babel/template" "^7.0.0" - "@babel/traverse" "^7.0.0" - "@babel/types" "^7.0.0" + "@babel/traverse" "^7.20.0" + "@babel/types" "^7.20.0" absolute-path "^0.0.0" accepts "^1.3.7" - async "^2.4.0" + async "^3.2.2" chalk "^4.0.0" ci-info "^2.0.0" connect "^3.6.5" debug "^2.2.0" denodeify "^1.2.1" error-stack-parser "^2.0.6" - fs-extra "^1.0.0" - graceful-fs "^4.1.3" + graceful-fs "^4.2.4" + hermes-parser "0.8.0" image-size "^0.6.0" invariant "^2.2.4" - jest-haste-map "^26.5.2" - jest-worker "^26.0.0" + jest-worker "^27.2.0" lodash.throttle "^4.1.1" - metro-babel-register "0.64.0" - metro-babel-transformer "0.64.0" - metro-cache "0.64.0" - metro-cache-key "0.64.0" - metro-config "0.64.0" - metro-core "0.64.0" - metro-hermes-compiler "0.64.0" - metro-inspector-proxy "0.64.0" - metro-minify-uglify "0.64.0" - metro-react-native-babel-preset "0.64.0" - metro-resolver "0.64.0" - metro-runtime "0.64.0" - metro-source-map "0.64.0" - metro-symbolicate "0.64.0" - metro-transform-plugins "0.64.0" - metro-transform-worker "0.64.0" + metro-babel-transformer "0.73.8" + metro-cache "0.73.8" + metro-cache-key "0.73.8" + metro-config "0.73.8" + metro-core "0.73.8" + metro-file-map "0.73.8" + metro-hermes-compiler "0.73.8" + metro-inspector-proxy "0.73.8" + metro-minify-terser "0.73.8" + metro-minify-uglify "0.73.8" + metro-react-native-babel-preset "0.73.8" + metro-resolver "0.73.8" + metro-runtime "0.73.8" + metro-source-map "0.73.8" + metro-symbolicate "0.73.8" + metro-transform-plugins "0.73.8" + metro-transform-worker "0.73.8" mime-types "^2.1.27" - mkdirp "^0.5.1" node-fetch "^2.2.0" nullthrows "^1.1.1" - rimraf "^2.5.4" + rimraf "^3.0.2" serialize-error "^2.1.0" source-map "^0.5.6" strip-ansi "^6.0.0" temp "0.8.3" throat "^5.0.0" - ws "^1.1.5" - yargs "^15.3.1" + ws "^7.5.1" + yargs "^17.5.1" -micromatch@^3.1.10, micromatch@^3.1.4: +micromatch@^3.1.10: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -8149,7 +7745,7 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -8162,7 +7758,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -8179,11 +7775,6 @@ mime@^2.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -8194,13 +7785,34 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.5.tgz#4da8f1290ee0f0f8e83d60ca69f8f134068604a3" + integrity sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^7.4.1: + version "7.4.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.2.tgz#157e847d79ca671054253b840656720cb733f10f" + integrity sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -8210,7 +7822,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -8222,7 +7834,7 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" -minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: +minipass-fetch@^1.3.2: version "1.4.1" resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== @@ -8233,6 +7845,17 @@ minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: optionalDependencies: encoding "^0.1.12" +minipass-fetch@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" + integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== + dependencies: + minipass "^3.1.6" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" @@ -8270,17 +7893,17 @@ minipass@^2.6.0, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" -minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: +minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3, minipass@^3.1.6: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== dependencies: yallist "^4.0.0" -minipass@^4.0.0: - version "4.2.4" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.4.tgz#7d0d97434b6a19f59c5c3221698b48bbf3b2cd06" - integrity sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ== +minipass@^4.0.0, minipass@^4.0.2, minipass@^4.2.4: + version "4.2.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb" + integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q== minizlib@^1.3.3: version "1.3.3" @@ -8289,7 +7912,7 @@ minizlib@^1.3.3: dependencies: minipass "^2.9.0" -minizlib@^2.0.0, minizlib@^2.1.1: +minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -8356,7 +7979,7 @@ multiformats@^9.4.2: resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== -multimatch@^5.0.0: +multimatch@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== @@ -8413,7 +8036,7 @@ needle@^2.5.2: iconv-lite "^0.4.4" sax "^1.2.4" -negotiator@0.6.3, negotiator@^0.6.2: +negotiator@0.6.3, negotiator@^0.6.2, negotiator@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== @@ -8473,10 +8096,10 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -nocache@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" - integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== +nocache@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/nocache/-/nocache-3.0.4.tgz#5b37a56ec6e09fc7d401dceaed2eab40c8bfdf79" + integrity sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw== nock@^13.3.0: version "13.3.0" @@ -8488,7 +8111,7 @@ nock@^13.3.0: lodash "^4.17.21" propagate "^2.0.0" -node-addon-api@^3.0.0: +node-addon-api@^3.0.0, node-addon-api@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== @@ -8529,54 +8152,37 @@ node-fetch@^2.0, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-f dependencies: whatwg-url "^5.0.0" -node-gyp-build@^4.2.1: +node-gyp-build@^4.2.1, node-gyp-build@^4.3.0: version "4.6.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== -node-gyp@^5.0.2: - version "5.1.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e" - integrity sha512-WH0WKGi+a4i4DUt2mHnvocex/xPLp9pYt5R6M2JdFB7pJ7Z34hveZ4nDTGTiLXCkitA9T8HFZjhinBCiVHYcWw== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.2" - mkdirp "^0.5.1" - nopt "^4.0.1" - npmlog "^4.1.2" - request "^2.88.0" - rimraf "^2.6.3" - semver "^5.7.1" - tar "^4.4.12" - which "^1.3.1" - -node-gyp@^7.1.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" - integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== +node-gyp@^8.0.0: + version "8.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" + integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== dependencies: env-paths "^2.2.0" glob "^7.1.4" - graceful-fs "^4.2.3" + graceful-fs "^4.2.6" + make-fetch-happen "^9.1.0" nopt "^5.0.0" - npmlog "^4.1.2" - request "^2.88.2" + npmlog "^6.0.0" rimraf "^3.0.2" - semver "^7.3.2" - tar "^6.0.2" + semver "^7.3.5" + tar "^6.1.2" which "^2.0.2" -node-gyp@^8.0.0: - version "8.4.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" - integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== +node-gyp@^9.0.0: + version "9.3.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.3.1.tgz#1e19f5f290afcc9c46973d68700cbd21a96192e4" + integrity sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg== dependencies: env-paths "^2.2.0" glob "^7.1.4" graceful-fs "^4.2.6" - make-fetch-happen "^9.1.0" - nopt "^5.0.0" + make-fetch-happen "^10.0.3" + nopt "^6.0.0" npmlog "^6.0.0" rimraf "^3.0.2" semver "^7.3.5" @@ -8614,7 +8220,7 @@ node-stream-zip@^1.9.1: resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== -nopt@^4.0.1, nopt@^4.0.3: +nopt@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== @@ -8629,7 +8235,14 @@ nopt@^5.0.0: dependencies: abbrev "1" -normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: +nopt@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" + integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== + dependencies: + abbrev "^1.0.0" + +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -8639,7 +8252,7 @@ normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package- semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: +normalize-package-data@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== @@ -8649,65 +8262,81 @@ normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: semver "^7.3.4" validate-npm-package-license "^3.0.1" -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== +normalize-package-data@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-4.0.1.tgz#b46b24e0616d06cadf9d5718b29b6d445a82a62c" + integrity sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg== dependencies: - remove-trailing-separator "^1.0.1" + hosted-git-info "^5.0.0" + is-core-module "^2.8.1" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== - -npm-bundled@^1.0.1, npm-bundled@^1.1.1: +npm-bundled@^1.0.1, npm-bundled@^1.1.1, npm-bundled@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== dependencies: npm-normalize-package-bin "^1.0.1" -npm-install-checks@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" - integrity sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w== +npm-bundled@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-2.0.1.tgz#94113f7eb342cd7a67de1e789f896b04d2c600f4" + integrity sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw== dependencies: - semver "^7.1.1" + npm-normalize-package-bin "^2.0.0" -npm-lifecycle@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz#9882d3642b8c82c815782a12e6a1bfeed0026309" - integrity sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g== +npm-install-checks@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-5.0.0.tgz#5ff27d209a4e3542b8ac6b0c1db6063506248234" + integrity sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA== dependencies: - byline "^5.0.0" - graceful-fs "^4.1.15" - node-gyp "^5.0.2" - resolve-from "^4.0.0" - slide "^1.1.6" - uid-number "0.0.6" - umask "^1.1.0" - which "^1.3.1" + semver "^7.1.1" -npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: +npm-normalize-package-bin@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== -npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.2, npm-package-arg@^8.1.5: - version "8.1.5" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44" - integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== +npm-normalize-package-bin@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz#9447a1adaaf89d8ad0abe24c6c84ad614a675fff" + integrity sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ== + +npm-package-arg@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.1.tgz#00ebf16ac395c63318e67ce66780a06db6df1b04" + integrity sha512-CsP95FhWQDwNqiYS+Q0mZ7FAEDytDZAkNxQqea6IaAFJTAY9Lhhqyl0irU/6PMc7BGfUmnsbHcqxJD7XuVM/rg== dependencies: - hosted-git-info "^4.0.1" - semver "^7.3.4" + hosted-git-info "^3.0.6" + semver "^7.0.0" validate-npm-package-name "^3.0.0" +npm-package-arg@^9.0.0, npm-package-arg@^9.0.1: + version "9.1.2" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-9.1.2.tgz#fc8acecb00235f42270dda446f36926ddd9ac2bc" + integrity sha512-pzd9rLEx4TfNJkovvlBSLGhq31gGu2QDexFPWT19yCDh0JgnRhlBLNo5759N0AJmBk+kQ9Y/hXoLnlgFD+ukmg== + dependencies: + hosted-git-info "^5.0.0" + proc-log "^2.0.1" + semver "^7.3.5" + validate-npm-package-name "^4.0.0" + +npm-packlist@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-5.1.1.tgz#79bcaf22a26b6c30aa4dd66b976d69cc286800e0" + integrity sha512-UfpSvQ5YKwctmodvPPkK6Fwk603aoVsf8AEbmVKAEECrfvL8SSe1A2YIwrJ6xmTHAITKPwwZsWo7WwEbNk0kxw== + dependencies: + glob "^8.0.1" + ignore-walk "^5.0.1" + npm-bundled "^1.1.2" + npm-normalize-package-bin "^1.0.1" + npm-packlist@^1.4.8: version "1.4.8" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" @@ -8717,51 +8346,51 @@ npm-packlist@^1.4.8: npm-bundled "^1.0.1" npm-normalize-package-bin "^1.0.1" -npm-packlist@^2.1.4: - version "2.2.2" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.2.2.tgz#076b97293fa620f632833186a7a8f65aaa6148c8" - integrity sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg== +npm-packlist@^5.1.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-5.1.3.tgz#69d253e6fd664b9058b85005905012e00e69274b" + integrity sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg== dependencies: - glob "^7.1.6" - ignore-walk "^3.0.3" - npm-bundled "^1.1.1" - npm-normalize-package-bin "^1.0.1" + glob "^8.0.1" + ignore-walk "^5.0.1" + npm-bundled "^2.0.0" + npm-normalize-package-bin "^2.0.0" -npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" - integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== +npm-pick-manifest@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-7.0.2.tgz#1d372b4e7ea7c6712316c0e99388a73ed3496e84" + integrity sha512-gk37SyRmlIjvTfcYl6RzDbSmS9Y4TOBXfsPnoYqTHARNgWbyDiCSMLUpmALDj4jjcTZpURiEfsSHJj9k7EV4Rw== dependencies: - npm-install-checks "^4.0.0" - npm-normalize-package-bin "^1.0.1" - npm-package-arg "^8.1.2" - semver "^7.3.4" + npm-install-checks "^5.0.0" + npm-normalize-package-bin "^2.0.0" + npm-package-arg "^9.0.0" + semver "^7.3.5" -npm-registry-fetch@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz#68c1bb810c46542760d62a6a965f85a702d43a76" - integrity sha512-jmlgSxoDNuhAtxUIG6pVwwtz840i994dL14FoNVZisrmZW5kWd63IUTNv1m/hyRSGSqWjCUp/YZlS1BJyNp9XA== +npm-registry-fetch@13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-13.3.0.tgz#0ce10fa4a699a1e70685ecf41bbfb4150d74231b" + integrity sha512-10LJQ/1+VhKrZjIuY9I/+gQTvumqqlgnsCufoXETHAPFTS3+M+Z5CFhZRDHGavmJ6rOye3UvNga88vl8n1r6gg== dependencies: - make-fetch-happen "^9.0.1" - minipass "^3.1.3" - minipass-fetch "^1.3.0" + make-fetch-happen "^10.0.6" + minipass "^3.1.6" + minipass-fetch "^2.0.3" minipass-json-stream "^1.0.1" - minizlib "^2.0.0" - npm-package-arg "^8.0.0" - -npm-registry-fetch@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" - integrity sha512-PuFYYtnQ8IyVl6ib9d3PepeehcUeHN9IO5N/iCRhyg9tStQcqGQBRVHmfmMWPDERU3KwZoHFvbJ4FPXPspvzbA== - dependencies: - "@npmcli/ci-detect" "^1.0.0" - lru-cache "^6.0.0" - make-fetch-happen "^8.0.9" - minipass "^3.1.3" - minipass-fetch "^1.3.0" + minizlib "^2.1.2" + npm-package-arg "^9.0.1" + proc-log "^2.0.0" + +npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1: + version "13.3.1" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz#bb078b5fa6c52774116ae501ba1af2a33166af7e" + integrity sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw== + dependencies: + make-fetch-happen "^10.0.6" + minipass "^3.1.6" + minipass-fetch "^2.0.3" minipass-json-stream "^1.0.1" - minizlib "^2.0.0" - npm-package-arg "^8.0.0" + minizlib "^2.1.2" + npm-package-arg "^9.0.1" + proc-log "^2.0.0" npm-run-path@^2.0.0: version "2.0.2" @@ -8797,7 +8426,7 @@ npmlog@^5.0.1: gauge "^3.0.0" set-blocking "^2.0.0" -npmlog@^6.0.0: +npmlog@^6.0.0, npmlog@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== @@ -8817,20 +8446,61 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== -nwsapi@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" - integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -ob1@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.64.0.tgz#f254a55a53ca395c4f9090e28a85483eac5eba19" - integrity sha512-CO1N+5dhvy+MoAwxz8+fymEUcwsT4a+wHhrHFb02LppcJdHxgcBWviwEhUwKOD2kLMQ7ijrrzybOqpGcqEtvpQ== +nx@15.8.7, "nx@>=15.5.2 < 16": + version "15.8.7" + resolved "https://registry.yarnpkg.com/nx/-/nx-15.8.7.tgz#a89156244f6f94407d7603375ae2f52733c7aff4" + integrity sha512-u6p/1gU20WU61orxK7hcXBsVspPHy3X66XVAAakkYcaOBlsJhJrR7Og191qIyjEkqEWmcekiDQVw3D6XfagL4Q== + dependencies: + "@nrwl/cli" "15.8.7" + "@nrwl/tao" "15.8.7" + "@parcel/watcher" "2.0.4" + "@yarnpkg/lockfile" "^1.1.0" + "@yarnpkg/parsers" "^3.0.0-rc.18" + "@zkochan/js-yaml" "0.0.6" + axios "^1.0.0" + chalk "^4.1.0" + cli-cursor "3.1.0" + cli-spinners "2.6.1" + cliui "^7.0.2" + dotenv "~10.0.0" + enquirer "~2.3.6" + fast-glob "3.2.7" + figures "3.2.0" + flat "^5.0.2" + fs-extra "^11.1.0" + glob "7.1.4" + ignore "^5.0.4" + js-yaml "4.1.0" + jsonc-parser "3.2.0" + lines-and-columns "~2.0.3" + minimatch "3.0.5" + npm-run-path "^4.0.1" + open "^8.4.0" + semver "7.3.4" + string-width "^4.2.3" + strong-log-transformer "^2.1.0" + tar-stream "~2.2.0" + tmp "~0.2.1" + tsconfig-paths "^4.1.2" + tslib "^2.3.0" + v8-compile-cache "2.3.0" + yargs "^17.6.2" + yargs-parser "21.1.1" + optionalDependencies: + "@nrwl/nx-darwin-arm64" "15.8.7" + "@nrwl/nx-darwin-x64" "15.8.7" + "@nrwl/nx-linux-arm-gnueabihf" "15.8.7" + "@nrwl/nx-linux-arm64-gnu" "15.8.7" + "@nrwl/nx-linux-arm64-musl" "15.8.7" + "@nrwl/nx-linux-x64-gnu" "15.8.7" + "@nrwl/nx-linux-x64-musl" "15.8.7" + "@nrwl/nx-win32-arm64-msvc" "15.8.7" + "@nrwl/nx-win32-x64-msvc" "15.8.7" + +ob1@0.73.8: + version "0.73.8" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.73.8.tgz#c569f1a15ce2d04da6fd70293ad44b5a93b11978" + integrity sha512-1F7j+jzD+edS6ohQP7Vg5f3yiIk5i3x1uLrNIHOmLHWzWK1t3zrDpjnoXghccdVlsU+UjbyURnDynm4p0GgXeA== object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" @@ -8846,7 +8516,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.10.3, object-inspect@^1.12.2, object-inspect@^1.9.0: +object-inspect@^1.10.3, object-inspect@^1.12.3, object-inspect@^1.9.0: version "1.12.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== @@ -8873,16 +8543,6 @@ object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" -object.getownpropertydescriptors@^2.0.3: - version "2.1.5" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz#db5a9002489b64eef903df81d6623c07e5b4b4d3" - integrity sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw== - dependencies: - array.prototype.reduce "^1.0.5" - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -8925,13 +8585,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== - dependencies: - mimic-fn "^1.0.0" - onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" @@ -8946,17 +8599,14 @@ open@^6.2.0: dependencies: is-wsl "^1.1.0" -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== +open@^8.4.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" optionator@^0.9.1: version "0.9.1" @@ -8970,21 +8620,19 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -options@>=0.0.5: - version "0.0.6" - resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" - integrity sha512-bOj3L1ypm++N+n7CEbbe473A414AB7z+amKYshRb//iuL3MpdDCLhPnw6aVTdKB9g5ZRVHIEp8eUln6L2NUStg== - -ora@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" - integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== dependencies: - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-spinners "^2.0.0" - log-symbols "^2.2.0" - strip-ansi "^5.2.0" + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" wcwidth "^1.0.1" os-homedir@^1.0.0: @@ -9024,7 +8672,7 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -9059,24 +8707,24 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-map-series@^2.1.0: +p-map-series@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== -p-map@^4.0.0: +p-map@4.0.0, p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" -p-pipe@^3.1.0: +p-pipe@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== -p-queue@^6.6.2: +p-queue@6.6.2: version "6.6.2" resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== @@ -9084,7 +8732,7 @@ p-queue@^6.6.2: eventemitter3 "^4.0.4" p-timeout "^3.2.0" -p-reduce@^2.0.0, p-reduce@^2.1.0: +p-reduce@2.1.0, p-reduce@^2.0.0, p-reduce@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" integrity sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw== @@ -9106,37 +8754,66 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -p-waterfall@^2.1.1: +p-waterfall@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/p-waterfall/-/p-waterfall-2.1.1.tgz#63153a774f472ccdc4eb281cdb2967fcf158b2ee" integrity sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw== dependencies: p-reduce "^2.0.0" -pacote@^11.2.6: - version "11.3.5" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.3.5.tgz#73cf1fc3772b533f575e39efa96c50be8c3dc9d2" - integrity sha512-fT375Yczn4zi+6Hkk2TBe1x1sP8FgFsEIZ2/iWaXY2r/NkhDJfxbcn5paz1+RTFCyNf+dPnaoBDJoAxXSU8Bkg== +pacote@13.6.1: + version "13.6.1" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-13.6.1.tgz#ac6cbd9032b4c16e5c1e0c60138dfe44e4cc589d" + integrity sha512-L+2BI1ougAPsFjXRyBhcKmfT016NscRFLv6Pz5EiNf1CCFJFU0pSKKQwsZTyAQB+sTuUL4TyFyp6J1Ork3dOqw== dependencies: - "@npmcli/git" "^2.1.0" - "@npmcli/installed-package-contents" "^1.0.6" - "@npmcli/promise-spawn" "^1.2.0" - "@npmcli/run-script" "^1.8.2" - cacache "^15.0.5" + "@npmcli/git" "^3.0.0" + "@npmcli/installed-package-contents" "^1.0.7" + "@npmcli/promise-spawn" "^3.0.0" + "@npmcli/run-script" "^4.1.0" + cacache "^16.0.0" chownr "^2.0.0" fs-minipass "^2.1.0" infer-owner "^1.0.4" - minipass "^3.1.3" - mkdirp "^1.0.3" - npm-package-arg "^8.0.1" - npm-packlist "^2.1.4" - npm-pick-manifest "^6.0.0" - npm-registry-fetch "^11.0.0" + minipass "^3.1.6" + mkdirp "^1.0.4" + npm-package-arg "^9.0.0" + npm-packlist "^5.1.0" + npm-pick-manifest "^7.0.0" + npm-registry-fetch "^13.0.1" + proc-log "^2.0.0" promise-retry "^2.0.1" - read-package-json-fast "^2.0.1" + read-package-json "^5.0.0" + read-package-json-fast "^2.0.3" rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.1.0" + ssri "^9.0.0" + tar "^6.1.11" + +pacote@^13.0.3, pacote@^13.6.1: + version "13.6.2" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-13.6.2.tgz#0d444ba3618ab3e5cd330b451c22967bbd0ca48a" + integrity sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg== + dependencies: + "@npmcli/git" "^3.0.0" + "@npmcli/installed-package-contents" "^1.0.7" + "@npmcli/promise-spawn" "^3.0.0" + "@npmcli/run-script" "^4.1.0" + cacache "^16.0.0" + chownr "^2.0.0" + fs-minipass "^2.1.0" + infer-owner "^1.0.4" + minipass "^3.1.6" + mkdirp "^1.0.4" + npm-package-arg "^9.0.0" + npm-packlist "^5.1.0" + npm-pick-manifest "^7.0.0" + npm-registry-fetch "^13.0.1" + proc-log "^2.0.0" + promise-retry "^2.0.1" + read-package-json "^5.0.0" + read-package-json-fast "^2.0.3" + rimraf "^3.0.2" + ssri "^9.0.0" + tar "^6.1.11" parent-module@^1.0.0: version "1.0.1" @@ -9145,6 +8822,15 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-conflict-json@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/parse-conflict-json/-/parse-conflict-json-2.0.2.tgz#3d05bc8ffe07d39600dc6436c6aefe382033d323" + integrity sha512-jDbRGb00TAPFsKWCpZZOT93SxVP9nONOSgES3AevqRq/CHvavEBvKAjxX9p5Y5F0RZLxH9Ufd9+RwtCsa+lFDA== + dependencies: + json-parse-even-better-errors "^2.3.1" + just-diff "^5.0.1" + just-diff-apply "^5.2.0" + parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" @@ -9163,30 +8849,19 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -parse-path@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.4.tgz#4bf424e6b743fb080831f03b536af9fc43f0ffea" - integrity sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw== +parse-path@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-7.0.0.tgz#605a2d58d0a749c8594405d8cc3a2bf76d16099b" + integrity sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog== dependencies: - is-ssh "^1.3.0" - protocols "^1.4.0" - qs "^6.9.4" - query-string "^6.13.8" + protocols "^2.0.0" -parse-url@^6.0.0: - version "6.0.5" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.5.tgz#4acab8982cef1846a0f8675fa686cef24b2f6f9b" - integrity sha512-e35AeLTSIlkw/5GFq70IN7po8fmDUjpDPY1rIK+VubRfsUvBonjQ+PBZG+vWMACnQSmNlvl524IucoDmcioMxA== +parse-url@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-8.1.0.tgz#972e0827ed4b57fc85f0ea6b0d839f0d8a57a57d" + integrity sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w== dependencies: - is-ssh "^1.3.0" - normalize-url "^6.1.0" - parse-path "^4.0.0" - protocols "^1.4.0" - -parse5@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + parse-path "^7.0.0" parseurl@~1.3.3: version "1.3.3" @@ -9198,16 +8873,16 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== +path-exists@4.0.0, path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -9228,6 +8903,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.1.tgz#dab45f7bb1d3f45a0e271ab258999f4ab7e23132" + integrity sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA== + dependencies: + lru-cache "^7.14.1" + minipass "^4.0.2" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -9245,11 +8928,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -9260,6 +8938,11 @@ picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pify@5.0.0, pify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" + integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== + pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -9275,11 +8958,6 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" - integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== - pirates@^4.0.4, pirates@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" @@ -9299,14 +8977,6 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -plist@^3.0.1, plist@^3.0.5: - version "3.0.6" - resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.6.tgz#7cfb68a856a7834bca6dbfe3218eb9c7740145d3" - integrity sha512-WiIVYyrp8TD4w8yCvyeIr+lkmrGRd5u0VbRnU+tP/aRLxP/YadJUYOMZJ/6hIa3oUyVCsycXvtNRgd5XBJIbiA== - dependencies: - base64-js "^1.5.1" - xmlbuilder "^15.1.1" - posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -9317,11 +8987,6 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== - prettier-linter-helpers@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" @@ -9334,7 +8999,7 @@ prettier@^2.3.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== -pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: +pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== @@ -9344,24 +9009,34 @@ pretty-format@^26.0.0, pretty-format@^26.5.2, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" -pretty-format@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" - integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== +pretty-format@^29.0.0, pretty-format@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" + integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== dependencies: - ansi-regex "^5.0.1" + "@jest/schemas" "^29.4.3" ansi-styles "^5.0.0" - react-is "^17.0.1" + react-is "^18.0.0" + +proc-log@^2.0.0, proc-log@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" + integrity sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw== process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +promise-all-reject-late@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" + integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== + +promise-call-limit@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-call-limit/-/promise-call-limit-1.0.1.tgz#4bdee03aeb85674385ca934da7114e9bcd3c6e24" + integrity sha512-3+hgaa19jzCGLuSCbieeRsu5C2joKfYn8pY6JAuXFRVfF4IO+L7UPpFWNTeWT9pM7uhskvbPPd/oEOktCn317Q== promise-inflight@^1.0.1: version "1.0.1" @@ -9376,7 +9051,7 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" -promise@^8.0.3: +promise@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== @@ -9398,7 +9073,7 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@^15.7.2: +prop-types@*: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -9417,12 +9092,7 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== -protocols@^1.4.0: - version "1.4.8" - resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" - integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== - -protocols@^2.0.1: +protocols@^2.0.0, protocols@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== @@ -9435,10 +9105,10 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -psl@^1.1.28, psl@^1.1.33: - version "1.9.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" - integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== pump@^3.0.0: version "3.0.0" @@ -9448,11 +9118,16 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.1.0, punycode@^2.1.1: +punycode@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +pure-rand@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.1.tgz#31207dddd15d43f299fdcdb2f572df65030c19af" + integrity sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg== + pvtsutils@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.2.tgz#9f8570d132cdd3c27ab7d51a2799239bf8d8d5de" @@ -9470,28 +9145,13 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== -qs@6.11.0, qs@^6.9.4: +qs@6.11.0: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== dependencies: side-channel "^1.0.4" -qs@~6.5.2: - version "6.5.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" - integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== - -query-string@^6.13.8: - version "6.14.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" - integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== - dependencies: - decode-uri-component "^0.2.0" - filter-obj "^1.1.0" - split-on-first "^1.0.0" - strict-uri-encode "^2.0.0" - query-string@^7.0.1: version "7.1.3" resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" @@ -9502,11 +9162,6 @@ query-string@^7.0.1: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -9542,7 +9197,7 @@ rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-devtools-core@^4.6.0: +react-devtools-core@^4.26.1: version "4.27.2" resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.2.tgz#d20fc57e258c656eedabafc2c851d38b33583148" integrity sha512-8SzmIkpO87alD7Xr6gWIEa1jHkMjawOZ+6egjazlnjB4UUcbnzGDf/vBJ4BzGuWWEM+pzrxuzsPpcMqlQkYK2g== @@ -9550,6 +9205,11 @@ react-devtools-core@^4.6.0: shell-quote "^1.6.1" ws "^7" +"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + react-is@^16.13.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -9560,16 +9220,17 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-native-codegen@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.6.tgz#b3173faa879cf71bfade8d030f9c4698388f6909" - integrity sha512-cMvrUelD81wiPitEPiwE/TCNscIVauXxmt4NTGcy18HrUd0WRWXfYzAQGXm0eI87u3NMudNhqFj2NISJenxQHg== +react-native-codegen@^0.71.5: + version "0.71.5" + resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.71.5.tgz#454a42a891cd4ca5fc436440d301044dc1349c14" + integrity sha512-rfsuc0zkuUuMjFnrT55I1mDZ+pBRp2zAiRwxck3m6qeGJBGK5OV5JH66eDQ4aa+3m0of316CqrJDRzVlYufzIg== dependencies: - flow-parser "^0.121.0" - jscodeshift "^0.11.0" + "@babel/parser" "^7.14.0" + flow-parser "^0.185.0" + jscodeshift "^0.13.1" nullthrows "^1.1.1" -react-native-fs@^2.18.0: +react-native-fs@^2.20.0: version "2.20.0" resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.20.0.tgz#05a9362b473bfc0910772c0acbb73a78dbc810f6" integrity sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ== @@ -9577,13 +9238,18 @@ react-native-fs@^2.18.0: base-64 "^0.1.0" utf8 "^3.0.0" -react-native-get-random-values@^1.7.0: +react-native-get-random-values@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.8.0.tgz#1cb4bd4bd3966a356e59697b8f372999fe97cb16" integrity sha512-H/zghhun0T+UIJLmig3+ZuBCvF66rdbiWUfRSNS6kv5oDSpa1ZiVyvRWtuPesQpT8dXj+Bv7WJRQOUP+5TB1sA== dependencies: fast-base64-decode "^1.0.0" +react-native-gradle-plugin@^0.71.16: + version "0.71.16" + resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.16.tgz#822bb0c680e03b5df5aa65f2e5ffc2bc2930854a" + integrity sha512-H2BjG2zk7B7Wii9sXvd9qhCVRQYDAHSWdMw9tscmZBqSP62DkIWEQSk4/B2GhQ4aK9ydVXgtqR6tBeg3yy8TSA== + react-native-securerandom@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/react-native-securerandom/-/react-native-securerandom-0.1.1.tgz#f130623a412c338b0afadedbc204c5cbb8bf2070" @@ -9591,63 +9257,70 @@ react-native-securerandom@^0.1.1: dependencies: base64-js "*" -react-native@0.64.2: - version "0.64.2" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.64.2.tgz#233b6ed84ac4749c8bc2a2d6cf63577a1c437d18" - integrity sha512-Ty/fFHld9DcYsFZujXYdeVjEhvSeQcwuTGXezyoOkxfiGEGrpL/uwUZvMzwShnU4zbbTKDu2PAm/uwuOittRGA== +react-native@^0.71.4: + version "0.71.4" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.4.tgz#f03f600efe68f745d19454ab17f9c1a9ef304790" + integrity sha512-3hSYqvWrOdKhpV3HpEKp1/CkWx8Sr/N/miCrmUIAsVTSJUR7JW0VvIsrV9urDhUj/s6v2WF4n7qIEEJsmTCrPw== dependencies: - "@jest/create-cache-key-function" "^26.5.0" - "@react-native-community/cli" "^5.0.1-alpha.1" - "@react-native-community/cli-platform-android" "^5.0.1-alpha.1" - "@react-native-community/cli-platform-ios" "^5.0.1-alpha.1" + "@jest/create-cache-key-function" "^29.2.1" + "@react-native-community/cli" "10.2.0" + "@react-native-community/cli-platform-android" "10.2.0" + "@react-native-community/cli-platform-ios" "10.2.0" "@react-native/assets" "1.0.0" - "@react-native/normalize-color" "1.0.0" - "@react-native/polyfills" "1.0.0" + "@react-native/normalize-color" "2.1.0" + "@react-native/polyfills" "2.0.0" abort-controller "^3.0.0" anser "^1.4.9" base64-js "^1.1.2" + deprecated-react-native-prop-types "^3.0.1" event-target-shim "^5.0.1" - hermes-engine "~0.7.0" invariant "^2.2.4" - jsc-android "^245459.0.0" - metro-babel-register "0.64.0" - metro-react-native-babel-transformer "0.64.0" - metro-runtime "0.64.0" - metro-source-map "0.64.0" + jest-environment-node "^29.2.1" + jsc-android "^250231.0.0" + memoize-one "^5.0.0" + metro-react-native-babel-transformer "0.73.8" + metro-runtime "0.73.8" + metro-source-map "0.73.8" + mkdirp "^0.5.1" nullthrows "^1.1.1" pretty-format "^26.5.2" - promise "^8.0.3" - prop-types "^15.7.2" - react-devtools-core "^4.6.0" - react-native-codegen "^0.0.6" + promise "^8.3.0" + react-devtools-core "^4.26.1" + react-native-codegen "^0.71.5" + react-native-gradle-plugin "^0.71.16" react-refresh "^0.4.0" + react-shallow-renderer "^16.15.0" regenerator-runtime "^0.13.2" - scheduler "^0.20.1" - shelljs "^0.8.4" + scheduler "^0.23.0" stacktrace-parser "^0.1.3" - use-subscription "^1.0.0" + use-sync-external-store "^1.0.0" whatwg-fetch "^3.0.0" - ws "^6.1.4" + ws "^6.2.2" react-refresh@^0.4.0: version "0.4.3" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53" integrity sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA== -react@17.0.1: - version "17.0.1" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127" - integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w== +react-shallow-renderer@^16.15.0: + version "16.15.0" + resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" + integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== dependencies: - loose-envify "^1.1.0" object-assign "^4.1.1" + react-is "^16.12.0 || ^17.0.0 || ^18.0.0" -read-cmd-shim@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" - integrity sha512-HJpV9bQpkl6KwjxlJcBoqu9Ba0PQg8TqSNIOrulGt54a0uup0HtevreFHzYzkm0lpnleRdNBzXznKrgxglEHQw== +read-cmd-shim@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.0.tgz#62b8c638225c61e6cc607f8f4b779f3b8238f155" + integrity sha512-KQDVjGqhZk92PPNRj9ZEXEuqg8bUobSKRw+q0YQ3TKI5xkce7bUJobL4Z/OtiEbAAv70yEpYIXp4iQ9L8oPVog== + +read-cmd-shim@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.1.tgz#868c235ec59d1de2db69e11aec885bc095aea087" + integrity sha512-kEmDUoYf/CDy8yZbLTmhB1X9kkjf9Q80PCNsDMb7ufrGd6zZSQA1+UyjrO+pZm5K/S4OXCWJeiIt1JA8kAsa6g== -read-package-json-fast@^2.0.1: +read-package-json-fast@^2.0.2, read-package-json-fast@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== @@ -9655,44 +9328,25 @@ read-package-json-fast@^2.0.1: json-parse-even-better-errors "^2.3.0" npm-normalize-package-bin "^1.0.1" -read-package-json@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" - integrity sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA== - dependencies: - glob "^7.1.1" - json-parse-even-better-errors "^2.3.0" - normalize-package-data "^2.0.0" - npm-normalize-package-bin "^1.0.0" - -read-package-json@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-3.0.1.tgz#c7108f0b9390257b08c21e3004d2404c806744b9" - integrity sha512-aLcPqxovhJTVJcsnROuuzQvv6oziQx4zd3JvG0vGCL5MjTONUc4uJ90zCBC6R7W7oUKBNoR/F8pkyfVwlbxqng== - dependencies: - glob "^7.1.1" - json-parse-even-better-errors "^2.3.0" - normalize-package-data "^3.0.0" - npm-normalize-package-bin "^1.0.0" - -read-package-json@^4.1.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-4.1.2.tgz#b444d047de7c75d4a160cb056d00c0693c1df703" - integrity sha512-Dqer4pqzamDE2O4M55xp1qZMuLPqi4ldk2ya648FOMHRjwMzFhuxVrG04wd0c38IsvkVdr3vgHI6z+QTPdAjrQ== +read-package-json@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-5.0.1.tgz#1ed685d95ce258954596b13e2e0e76c7d0ab4c26" + integrity sha512-MALHuNgYWdGW3gKzuNMuYtcSSZbGQm94fAp16xt8VsYTLBjUSc55bLMKe6gzpWue0Tfi6CBgwCSdDAqutGDhMg== dependencies: - glob "^7.1.1" - json-parse-even-better-errors "^2.3.0" - normalize-package-data "^3.0.0" - npm-normalize-package-bin "^1.0.0" + glob "^8.0.1" + json-parse-even-better-errors "^2.3.1" + normalize-package-data "^4.0.0" + npm-normalize-package-bin "^1.0.1" -read-package-tree@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" - integrity sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw== +read-package-json@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-5.0.2.tgz#b8779ccfd169f523b67208a89cc912e3f663f3fa" + integrity sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q== dependencies: - read-package-json "^2.0.0" - readdir-scoped-modules "^1.0.0" - util-promisify "^2.1.0" + glob "^8.0.1" + json-parse-even-better-errors "^2.3.1" + normalize-package-data "^4.0.0" + npm-normalize-package-bin "^2.0.0" read-pkg-up@^3.0.0: version "3.0.0" @@ -9730,17 +9384,17 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -read@1, read@~1.0.1: +read@1, read@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" integrity sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ== dependencies: mute-stream "~0.0.4" -readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.6.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.1.tgz#f9f9b5f536920253b3d26e7660e7da4ccff9bb62" - integrity sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ== +readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -9759,7 +9413,7 @@ readable-stream@^2.0.6, readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readdir-scoped-modules@^1.0.0: +readdir-scoped-modules@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== @@ -9769,7 +9423,12 @@ readdir-scoped-modules@^1.0.0: graceful-fs "^4.1.2" once "^1.3.0" -recast@^0.20.3: +readline@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c" + integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== + +recast@^0.20.4: version "0.20.5" resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" integrity sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ== @@ -9779,13 +9438,6 @@ recast@^0.20.3: source-map "~0.6.1" tslib "^2.0.1" -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== - dependencies: - resolve "^1.1.6" - redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -9846,13 +9498,6 @@ regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.2: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regenerator-transform@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" - integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== - dependencies: - "@babel/runtime" "^7.8.4" - regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -9870,15 +9515,10 @@ regexp.prototype.flags@^1.4.3: define-properties "^1.1.3" functions-have-names "^1.2.2" -regexpp@^3.1.0, regexpp@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - regexpu-core@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.1.tgz#66900860f88def39a5cb79ebd9490e84f17bcdfb" - integrity sha512-nCOzW2V/X15XpLsK2rlgdwrysrBq+AauCn+omItIz4R1pIcmeot5zvjdmOBRLzEH/CkC6IxMJVmxDe3QcMuNVQ== + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== dependencies: "@babel/regjsgen" "^0.8.0" regenerate "^1.4.2" @@ -9894,11 +9534,6 @@ regjsparser@^0.9.1: dependencies: jsesc "~0.5.0" -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== - repeat-element@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" @@ -9909,52 +9544,16 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== -request@^2.88.0, request@^2.88.2: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -9962,6 +9561,11 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-from@5.0.0, resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" @@ -9972,22 +9576,17 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve.exports@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" - integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== +resolve.exports@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.1.tgz#cee884cd4e3f355660e501fa3276b27d7ffe5a20" + integrity sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1: +resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -9996,14 +9595,6 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -10032,7 +9623,7 @@ rfc4648@1.4.0: resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.4.0.tgz#c75b2856ad2e2d588b6ddb985d556f1f7f2a2abd" integrity sha512-3qIzGhHlMHA6PoT6+cdPKZ+ZqtxkIvg8DZGKA5z6PQ33/uuhoJ+Ws/D/J9rXW6gXodgH8QYlz2UCl+sdUDmNIg== -rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1: +rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -10046,10 +9637,12 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@^4.0.7: - version "4.1.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.1.2.tgz#20dfbc98083bdfaa28b01183162885ef213dbf7c" - integrity sha512-BlIbgFryTbw3Dz6hyoWFhKk+unCcHMSkZGrTFVAx2WmttdBSonsdtRlwiuTbDqTKr+UlXIUqJVS4QT5tUzGENQ== +rimraf@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.0.tgz#c7a9f45bb2ec058d2e60ef9aca5167974313d605" + integrity sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ== + dependencies: + glob "^9.2.0" rimraf@~2.2.6: version "2.2.8" @@ -10063,11 +9656,6 @@ rimraf@~2.6.2: dependencies: glob "^7.1.3" -rsvp@^4.8.4: - version "4.8.5" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" - integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== - run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -10087,7 +9675,7 @@ rxjs@^6.6.0: dependencies: tslib "^1.9.0" -rxjs@^7.2.0, rxjs@^7.8.0: +rxjs@^7.2.0, rxjs@^7.5.5, rxjs@^7.8.0: version "7.8.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== @@ -10120,52 +9708,36 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sane@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" - integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== - dependencies: - "@cnakazawa/watch" "^1.0.3" - anymatch "^2.0.0" - capture-exit "^2.0.0" - exec-sh "^0.3.2" - execa "^1.0.0" - fb-watchman "^2.0.0" - micromatch "^3.1.4" - minimist "^1.1.1" - walker "~1.0.5" - sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -saxes@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== - dependencies: - xmlchars "^2.2.0" - -scheduler@^0.20.1: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" "semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: +semver@7.3.4: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" + +semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== @@ -10269,30 +9841,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" - integrity sha512-V0iQEZ/uoem3NmD91rD8XiuozJnq9/ZJnbHVXHnWqP1ucAhS3yJ7sLIIzEi57wFFcK3oi3kFUC46uSyWr35mxg== - dependencies: - array-filter "~0.0.0" - array-map "~0.0.0" - array-reduce "~0.0.0" - jsonify "~0.0.0" - -shell-quote@^1.6.1: +shell-quote@^1.6.1, shell-quote@^1.7.3: version "1.8.0" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.0.tgz#20d078d0eaf71d54f43bd2ba14a1b5b9bfa5c8ba" integrity sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ== -shelljs@^0.8.4: - version "0.8.5" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" - integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== - dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" - side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -10302,35 +9855,26 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -simple-plist@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.3.1.tgz#16e1d8f62c6c9b691b8383127663d834112fb017" - integrity sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw== - dependencies: - bplist-creator "0.1.0" - bplist-parser "0.3.1" - plist "^3.0.5" - sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - integrity sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg== - -slash@^3.0.0: +slash@3.0.0, slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + slice-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" @@ -10340,20 +9884,6 @@ slice-ansi@^2.0.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -slide@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" - integrity sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw== - smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" @@ -10389,15 +9919,6 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socks-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" - integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== - dependencies: - agent-base "^6.0.2" - debug "4" - socks "^2.3.3" - socks-proxy-agent@^6.0.0: version "6.2.1" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce" @@ -10407,7 +9928,16 @@ socks-proxy-agent@^6.0.0: debug "^4.3.3" socks "^2.6.2" -socks@^2.3.3, socks@^2.6.2: +socks-proxy-agent@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" + integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== + dependencies: + agent-base "^6.0.2" + debug "^4.3.3" + socks "^2.6.2" + +socks@^2.6.2: version "2.7.1" resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== @@ -10422,13 +9952,6 @@ sort-keys@^2.0.0: dependencies: is-plain-obj "^1.0.0" -sort-keys@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" - integrity sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg== - dependencies: - is-plain-obj "^2.0.0" - source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -10440,7 +9963,15 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.16, source-map-support@^0.5.21, source-map-support@^0.5.6: +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@^0.5.16, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -10469,9 +10000,9 @@ source-map@^0.7.3: integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" @@ -10490,9 +10021,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.12" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz#69077835abe2710b65f03969898b6637b505a779" - integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA== + version "3.0.13" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz#7189a474c46f8d47c7b0da4b987bb45e908bd2d5" + integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w== split-on-first@^1.0.0: version "1.1.0" @@ -10525,20 +10056,12 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== -sshpk@^1.7.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" - integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" +ssri@9.0.1, ssri@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" + integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== + dependencies: + minipass "^3.1.1" ssri@^8.0.0, ssri@^8.0.1: version "8.0.1" @@ -10589,11 +10112,6 @@ str2buf@^1.3.0: resolved "https://registry.yarnpkg.com/str2buf/-/str2buf-1.3.0.tgz#a4172afff4310e67235178e738a2dbb573abead0" integrity sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA== -stream-buffers@2.2.x: - version "2.2.0" - resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" - integrity sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg== - strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -10625,6 +10143,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string.prototype.trim@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string.prototype.trimend@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" @@ -10715,7 +10242,12 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== -strong-log-transformer@^2.1.0: +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + +strong-log-transformer@2.1.0, strong-log-transformer@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" integrity sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA== @@ -10736,7 +10268,7 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -10750,23 +10282,18 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" - integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== - dependencies: - has-flag "^4.0.0" - supports-color "^7.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -symbol-tree@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +synckit@^0.8.4: + version "0.8.5" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" + integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== + dependencies: + "@pkgr/utils" "^2.3.1" + tslib "^2.5.0" table-layout@^1.0.2: version "1.0.2" @@ -10778,18 +10305,35 @@ table-layout@^1.0.2: typical "^5.2.0" wordwrapjs "^4.0.0" -table@^6.0.9: - version "6.8.1" - resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" - integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA== +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tar-stream@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== dependencies: - ajv "^8.0.1" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar@6.1.11: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" -tar@^4.4.12, tar@^4.4.13: +tar@^4.4.13: version "4.4.19" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== @@ -10802,7 +10346,7 @@ tar@^4.4.12, tar@^4.4.13: safe-buffer "^5.2.1" yallist "^3.1.1" -tar@^6.0.2, tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: +tar@^6.0.2, tar@^6.1.11, tar@^6.1.2: version "6.1.13" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== @@ -10814,22 +10358,11 @@ tar@^6.0.2, tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" -temp-dir@^1.0.0: +temp-dir@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" integrity sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ== -temp-write@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320" - integrity sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw== - dependencies: - graceful-fs "^4.1.15" - is-stream "^2.0.0" - make-dir "^3.0.0" - temp-dir "^1.0.0" - uuid "^3.3.2" - temp@0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" @@ -10838,20 +10371,22 @@ temp@0.8.3: os-tmpdir "^1.0.0" rimraf "~2.2.6" -temp@^0.8.1: +temp@^0.8.4: version "0.8.4" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== dependencies: rimraf "~2.6.2" -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== +terser@^5.15.0: + version "5.16.6" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.6.tgz#f6c7a14a378ee0630fbe3ac8d1f41b4681109533" + integrity sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg== dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" test-exclude@^6.0.0: version "6.0.0" @@ -10877,11 +10412,6 @@ throat@^5.0.0: resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== -throat@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" - integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== - through2@^2.0.0, through2@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -10902,6 +10432,14 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +tiny-glob@^0.2.9: + version "0.2.9" + resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" + integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== + dependencies: + globalyzer "0.1.0" + globrex "^0.1.2" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -10909,6 +10447,13 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +tmp@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -10961,54 +10506,34 @@ toml@^3.0.0: resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== -tough-cookie@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" - integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.2.0" - url-parse "^1.5.3" - -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== - dependencies: - punycode "^2.1.1" - tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +treeverse@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-2.0.0.tgz#036dcef04bc3fd79a9b79a68d4da03e882d8a9ca" + integrity sha512-N5gJCkLu1aXccpOTtqV6ddSEi6ZmGkh3hjmbu1IjcavJK4qyOVQmi0myQKM7z5jVGmD68SJoliaVrMmVObhj6A== + trim-newlines@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -ts-jest@^27.0.3: - version "27.1.5" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.5.tgz#0ddf1b163fbaae3d5b7504a1e65c914a95cff297" - integrity sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA== +ts-jest@^29.0.5: + version "29.0.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.0.5.tgz#c5557dcec8fe434fcb8b70c3e21c6b143bfce066" + integrity sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" - jest-util "^27.0.0" - json5 "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" lodash.memoize "4.x" make-error "1.x" semver "7.x" - yargs-parser "20.x" + yargs-parser "^21.0.1" ts-node@^10.0.0, ts-node@^10.4.0: version "10.9.1" @@ -11058,17 +10583,15 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== -tslog@^3.2.0: - version "3.3.4" - resolved "https://registry.yarnpkg.com/tslog/-/tslog-3.3.4.tgz#083197a908c97b3b714a0576b9dac293f223f368" - integrity sha512-N0HHuHE0e/o75ALfkioFObknHR5dVchUad4F0XyFf3gXJYB++DewEzwGI/uIOM216E5a43ovnRNEeQIq9qgm4Q== - dependencies: - source-map-support "^0.5.21" +tslog@^4.8.2: + version "4.8.2" + resolved "https://registry.yarnpkg.com/tslog/-/tslog-4.8.2.tgz#dbb0c96249e387e8a711ae6e077330ba1ef102c9" + integrity sha512-eAKIRjxfSKYLs06r1wT7oou6Uv9VN6NW9g0JPidBlqQwPBBl5+84dm7r8zSOPVq1kyfEw1P6B3/FLSpZCorAgA== tsutils@^3.21.0: version "3.21.0" @@ -11084,18 +10607,6 @@ tsyringe@^4.7.0: dependencies: tslib "^1.9.3" -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -11103,13 +10614,6 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== - dependencies: - prelude-ls "~1.1.2" - type-detect@4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -11150,6 +10654,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^3.2.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.6.1.tgz#cf8025edeebfd6cf48de73573a5e1423350b9993" + integrity sha512-htXWckxlT6U4+ilVgweNliPqlsVSSucbxVexRYllyMVJDtf5rTjv6kF/s+qAd4QSL1BZcnJPEJavYBPQiWuZDA== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -11177,19 +10686,12 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@~4.9.4: +"typescript@^3 || ^4", typescript@~4.9.5: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== @@ -11217,11 +10719,6 @@ uglify-js@^3.1.4: resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== -uid-number@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" - integrity sha512-c461FXIljswCuscZn67xq9PpszkPT6RjheWFQTgCyabJrTUozElanb0YEqv2UGgk247YpcJkFBuSGNvBlpXM9w== - uint8arrays@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" @@ -11229,16 +10726,6 @@ uint8arrays@^3.1.1: dependencies: multiformats "^9.4.2" -ultron@1.0.x: - version "1.0.2" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" - integrity sha512-QMpnpVtYaWEeY+MwKDN/UdKlE/LsFZXM5lO1u7GaZzNgmIbGixHEmVMIKT+vqYOALu3m5GYQy9kz4Xu4IVn7Ow== - -umask@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" - integrity sha512-lE/rxOhmiScJu9L6RTNVgB/zZbF+vGC0/p6D3xnkAePI2o0sMyFG966iR5Ki50OI/0mNi2yaRnxfLsPmEZF/JA== - unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" @@ -11289,6 +10776,13 @@ unique-filename@^1.1.1: dependencies: unique-slug "^2.0.0" +unique-filename@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" + integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== + dependencies: + unique-slug "^3.0.0" + unique-slug@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" @@ -11296,6 +10790,13 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +unique-slug@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" + integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== + dependencies: + imurmurhash "^0.1.4" + universal-user-agent@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" @@ -11306,11 +10807,6 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -universalify@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" - integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== - universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" @@ -11354,22 +10850,7 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== -url-parse@^1.5.3: - version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -use-subscription@^1.0.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.8.0.tgz#f118938c29d263c2bce12fc5585d3fe694d4dbce" - integrity sha512-LISuG0/TmmoDoCRmV5XAqYkd3UCBNM0ML3gGBndze65WITcsExCD3DTvXXTLyNcOC0heFQZzluW88bN/oC1DQQ== - dependencies: - use-sync-external-store "^1.2.0" - -use-sync-external-store@^1.2.0: +use-sync-external-store@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== @@ -11389,48 +10870,41 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -util-promisify@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53" - integrity sha512-K+5eQPYs14b3+E+hmE2J6gCZ4JmMl9DbYS6BeP2CHq6WMuNxErxf5B/n0fz85L8zUuoO6rIzNNmIQDu/j+1OcA== - dependencies: - object.getownpropertydescriptors "^2.0.3" - utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.3.2: +uuid@8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-compile-cache@^2.0.3: +v8-compile-cache@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== -v8-to-istanbul@^8.1.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" - integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== +v8-to-istanbul@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== dependencies: + "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" - source-map "^0.7.3" -validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: +validate-npm-package-license@3.0.4, validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== @@ -11438,6 +10912,13 @@ validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +validate-npm-package-name@4.0.0, validate-npm-package-name@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz#fe8f1c50ac20afdb86f177da85b3600f0ac0d747" + integrity sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q== + dependencies: + builtins "^5.0.0" + validate-npm-package-name@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" @@ -11445,7 +10926,7 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" -validator@^13.5.2, validator@^13.7.0: +validator@^13.7.0: version "13.9.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA== @@ -11460,35 +10941,17 @@ vary@^1, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - vlq@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468" integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== - dependencies: - xml-name-validator "^3.0.0" +walk-up-path@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-1.0.0.tgz#d4745e893dd5fd0dbb58dd0a4c6a33d9c9fec53e" + integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg== -walker@^1.0.7, walker@~1.0.5: +walker@^1.0.7, walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== @@ -11502,7 +10965,7 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -web-did-resolver@^2.0.8: +web-did-resolver@^2.0.21: version "2.0.21" resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.21.tgz#065797dee3e37cd9f19261d04a90144fe576e5df" integrity sha512-vKYz0s9spYfYrKhrF88F44lkofS1yj6TCF40+i077a7boru2BNROl5VZEIVL9jJRUDsNzvmVSKkq3kS8kZnB2Q== @@ -11531,33 +10994,11 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== - -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" - whatwg-fetch@^3.0.0: version "3.6.2" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -11566,15 +11007,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== - dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -11603,7 +11035,7 @@ which-typed-array@^1.1.9: has-tostringtag "^1.0.0" is-typed-array "^1.1.10" -which@^1.2.9, which@^1.3.1: +which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -11624,7 +11056,7 @@ wide-align@^1.1.0, wide-align@^1.1.2, wide-align@^1.1.5: dependencies: string-width "^1.0.2 || 2 || 3 || 4" -word-wrap@^1.2.3, word-wrap@~1.2.3: +word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== @@ -11665,6 +11097,14 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +write-file-atomic@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" + integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: version "2.4.3" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" @@ -11674,15 +11114,13 @@ write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: imurmurhash "^0.1.4" signal-exit "^3.0.2" -write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== +write-file-atomic@^4.0.0, write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" + signal-exit "^3.0.7" write-json-file@^3.2.0: version "3.2.0" @@ -11696,19 +11134,7 @@ write-json-file@^3.2.0: sort-keys "^2.0.0" write-file-atomic "^2.4.2" -write-json-file@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-4.3.0.tgz#908493d6fd23225344af324016e4ca8f702dd12d" - integrity sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ== - dependencies: - detect-indent "^6.0.0" - graceful-fs "^4.1.15" - is-plain-obj "^2.0.0" - make-dir "^3.0.0" - sort-keys "^4.0.0" - write-file-atomic "^3.0.0" - -write-pkg@^4.0.0: +write-pkg@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-4.0.0.tgz#675cc04ef6c11faacbbc7771b24c0abbf2a20039" integrity sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA== @@ -11717,55 +11143,22 @@ write-pkg@^4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" -ws@^1.1.0, ws@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" - integrity sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w== - dependencies: - options ">=0.0.5" - ultron "1.0.x" - -ws@^6.1.4: +ws@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== dependencies: async-limiter "~1.0.0" -ws@^7, ws@^7.4.6, ws@^7.5.3: +ws@^7, ws@^7.5.1: version "7.5.9" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== -xcode@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/xcode/-/xcode-2.1.0.tgz#bab64a7e954bb50ca8d19da7e09531c65a43ecfe" - integrity sha512-uCrmPITrqTEzhn0TtT57fJaNaw8YJs1aCzs+P/QqxsDbvPZSv7XMPPwXrKvHtD6pLjBM/NaVwraWJm8q83Y4iQ== - dependencies: - simple-plist "^1.0.0" - uuid "^3.3.2" - -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== - -xmlbuilder@^15.1.1: - version "15.1.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" - integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== - -xmlchars@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - -xmldoc@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.2.0.tgz#7554371bfd8c138287cff01841ae4566d26e5541" - integrity sha512-2eN8QhjBsMW2uVj7JHLHkMytpvGHLHxKXBy4J3fAT/HujsEtM6yU84iGjpESYGHg6XwK0Vu4l+KgqQ2dv2cCqg== - dependencies: - sax "^1.2.4" +ws@^8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== xtend@~4.0.1: version "4.0.2" @@ -11797,15 +11190,20 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.1.3: + version "2.2.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.1.tgz#3014bf0482dcd15147aa8e56109ce8632cd60ce4" + integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw== + yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@21.1.1, yargs-parser@^21.0.1, yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs-parser@^18.1.2: version "18.1.3" @@ -11815,7 +11213,25 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs@^15.1.0, yargs@^15.3.1: +yargs-parser@^20.2.2, yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs@16.2.0, yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@^15.1.0: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== @@ -11832,18 +11248,18 @@ yargs@^15.1.0, yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== +yargs@^17.3.1, yargs@^17.5.1, yargs@^17.6.2: + version "17.7.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" + integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== dependencies: - cliui "^7.0.2" + cliui "^8.0.1" escalade "^3.1.1" get-caller-file "^2.0.5" require-directory "^2.1.1" - string-width "^4.2.0" + string-width "^4.2.3" y18n "^5.0.5" - yargs-parser "^20.2.2" + yargs-parser "^21.1.1" yn@3.1.1: version "3.1.1" From 806ccd9fd846352bb0d54d4e1392b336d6f12d14 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 31 Mar 2023 17:38:29 +0200 Subject: [PATCH 109/139] refactor!: remove module specific config options (#1393) Signed-off-by: Timo Glastra --- demo/src/BaseAgent.ts | 8 +- .../v1-connectionless-proofs.e2e.test.ts | 49 ++++++---- .../anoncreds/tests/legacyAnonCredsSetup.ts | 4 +- packages/askar/tests/helpers.ts | 18 +++- packages/core/src/agent/Agent.ts | 2 +- packages/core/src/agent/AgentConfig.ts | 95 ------------------- packages/core/src/agent/AgentModules.ts | 45 ++------- packages/core/src/agent/BaseAgent.ts | 6 +- .../core/src/agent/__tests__/Agent.test.ts | 10 +- .../src/agent/__tests__/AgentConfig.test.ts | 2 - .../src/agent/__tests__/AgentModules.test.ts | 17 ++-- .../connections/ConnectionsModuleConfig.ts | 9 +- .../__tests__/connection-manual.e2e.test.ts | 25 +++-- .../v2-indy-connectionless-proofs.e2e.test.ts | 53 +++++++---- ...cipientApi.ts => MediationRecipientApi.ts} | 22 +---- ...tModule.ts => MediationRecipientModule.ts} | 20 ++-- ...g.ts => MediationRecipientModuleConfig.ts} | 22 ++--- .../modules/routing/MediatorModuleConfig.ts | 4 +- ...st.ts => MediationRecipientModule.test.ts} | 10 +- .../routing/__tests__/mediation.test.ts | 59 +++++++----- .../modules/routing/__tests__/pickup.test.ts | 9 +- packages/core/src/modules/routing/index.ts | 4 +- .../services/MediationRecipientService.ts | 10 +- .../MediationRecipientService.test.ts | 4 +- packages/core/src/types.ts | 83 ---------------- packages/core/tests/connections.test.ts | 9 +- packages/core/tests/helpers.ts | 31 ++++-- .../tests/oob-mediation-provision.test.ts | 20 +++- packages/core/tests/oob-mediation.test.ts | 17 ++-- .../tenants/tests/tenant-sessions.e2e.test.ts | 6 +- packages/tenants/tests/tenants.e2e.test.ts | 10 +- samples/extension-module/requester.ts | 5 +- samples/extension-module/responder.ts | 6 +- samples/mediator.ts | 18 +++- .../e2e-askar-indy-sdk-wallet-subject.test.ts | 51 ++++++---- tests/e2e-http.test.ts | 54 +++++++---- tests/e2e-subject.test.ts | 51 ++++++---- tests/e2e-ws-pickup-v2.test.ts | 53 +++++++---- tests/e2e-ws.test.ts | 52 ++++++---- 39 files changed, 476 insertions(+), 497 deletions(-) rename packages/core/src/modules/routing/{RecipientApi.ts => MediationRecipientApi.ts} (96%) rename packages/core/src/modules/routing/{RecipientModule.ts => MediationRecipientModule.ts} (58%) rename packages/core/src/modules/routing/{RecipientModuleConfig.ts => MediationRecipientModuleConfig.ts} (81%) rename packages/core/src/modules/routing/__tests__/{RecipientModule.test.ts => MediationRecipientModule.test.ts} (80%) diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index 18f51f8c78..f7b252c36a 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -12,6 +12,7 @@ import { import { AnonCredsRsModule } from '@aries-framework/anoncreds-rs' import { AskarModule } from '@aries-framework/askar' import { + ConnectionsModule, DidsModule, V2ProofProtocol, V2CredentialProtocol, @@ -75,7 +76,6 @@ export class BaseAgent { key: name, }, endpoints: [`http://localhost:${this.port}`], - autoAcceptConnections: true, } satisfies InitConfig this.config = config @@ -103,6 +103,9 @@ function getAskarAnonCredsIndyModules() { const legacyIndyProofFormatService = new LegacyIndyProofFormatService() return { + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), credentials: new CredentialsModule({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, credentialProtocols: [ @@ -149,6 +152,9 @@ function getLegacyIndySdkModules() { const legacyIndyProofFormatService = new LegacyIndyProofFormatService() return { + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), credentials: new CredentialsModule({ autoAcceptCredentials: AutoAcceptCredential.ContentApproved, credentialProtocols: [ diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index d8a5a1b3dc..47bb04e2d4 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -1,4 +1,5 @@ import type { SubjectMessage } from '../../../../../../../tests/transport/SubjectInboundTransport' +import type { AnonCredsTestsAgent } from '../../../../../tests/legacyAnonCredsSetup' import { Subject } from 'rxjs' @@ -15,6 +16,8 @@ import { Attachment, AttachmentData, ProofEventTypes, + MediatorModule, + MediationRecipientModule, } from '../../../../../../core/src' import { uuid } from '../../../../../../core/src/utils/uuid' import { @@ -361,10 +364,14 @@ describe('V1 Proofs - Connectionless - Indy', () => { const mediatorAgentOptions = getAgentOptions( `Connectionless proofs with mediator Mediator-${unique}`, { - autoAcceptMediationRequests: true, endpoints: ['rxjs:mediator'], }, - getIndySdkModules() + { + ...getIndySdkModules(), + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + }), + } ) const mediatorMessages = new Subject() @@ -388,28 +395,34 @@ describe('V1 Proofs - Connectionless - Indy', () => { const faberAgentOptions = getAgentOptions( `Connectionless proofs with mediator Faber-${unique}`, + {}, { - mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', + ...getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptProofs: AutoAcceptProof.Always, - }) + mediationRecipient: new MediationRecipientModule({ + mediatorInvitationUrl: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const aliceAgentOptions = getAgentOptions( `Connectionless proofs with mediator Alice-${unique}`, + {}, { - mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', + ...getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptProofs: AutoAcceptProof.Always, - }) + mediationRecipient: new MediationRecipientModule({ + mediatorInvitationUrl: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const faberAgent = new Agent(faberAgentOptions) @@ -441,10 +454,10 @@ describe('V1 Proofs - Connectionless - Indy', () => { }) await issueLegacyAnonCredsCredential({ - issuerAgent: faberAgent, + issuerAgent: faberAgent as AnonCredsTestsAgent, issuerReplay: faberReplay, issuerHolderConnectionId: faberConnection.id, - holderAgent: aliceAgent, + holderAgent: aliceAgent as AnonCredsTestsAgent, holderReplay: aliceReplay, offer: { credentialDefinitionId: credentialDefinition.credentialDefinitionId, diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index d0f408b069..571ecc74eb 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -76,8 +76,8 @@ import { // Helper type to get the type of the agents (with the custom modules) for the credential tests export type AnonCredsTestsAgent = - | Agent> - | Agent> + | Agent & { mediationRecipient?: any; mediator?: any }> + | Agent & { mediationRecipient?: any; mediator?: any }> export const getLegacyAnonCredsModules = ({ autoAcceptCredentials, diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index acae3d2d14..b182c66abc 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -1,7 +1,7 @@ import type { AskarWalletPostgresStorageConfig } from '../src/wallet' import type { InitConfig } from '@aries-framework/core' -import { LogLevel, utils } from '@aries-framework/core' +import { ConnectionsModule, LogLevel, utils } from '@aries-framework/core' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import path from 'path' @@ -31,7 +31,6 @@ export function getPostgresAgentOptions( key: `Key${name}`, storage: storageConfig, }, - autoAcceptConnections: true, autoUpdateStorageOnStartup: false, logger: new TestLogger(LogLevel.off, name), ...extraConfig, @@ -39,7 +38,12 @@ export function getPostgresAgentOptions( return { config, dependencies: agentDependencies, - modules: { askar: new AskarModule(askarModuleConfig) }, + modules: { + askar: new AskarModule(askarModuleConfig), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), + }, } as const } @@ -52,7 +56,6 @@ export function getSqliteAgentOptions(name: string, extraConfig: Partial extends BaseAge public constructor(options: AgentOptions, dependencyManager = new DependencyManager()) { const agentConfig = new AgentConfig(options.config, options.dependencies) - const modulesWithDefaultModules = extendModulesWithDefaultModules(agentConfig, options.modules) + const modulesWithDefaultModules = extendModulesWithDefaultModules(options.modules) // Register internal dependencies dependencyManager.registerSingleton(MessageHandlerRegistry) diff --git a/packages/core/src/agent/AgentConfig.ts b/packages/core/src/agent/AgentConfig.ts index 971bff8d6d..7e43029088 100644 --- a/packages/core/src/agent/AgentConfig.ts +++ b/packages/core/src/agent/AgentConfig.ts @@ -3,10 +3,7 @@ import type { Logger } from '../logger' import type { InitConfig } from '../types' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' -import { AriesFrameworkError } from '../error' import { ConsoleLogger, LogLevel } from '../logger' -import { AutoAcceptCredential } from '../modules/credentials/models/CredentialAutoAcceptType' -import { AutoAcceptProof } from '../modules/proofs/models/ProofAutoAcceptType' import { DidCommMimeType } from '../types' export class AgentConfig { @@ -22,15 +19,6 @@ export class AgentConfig { this.label = initConfig.label this.logger = initConfig.logger ?? new ConsoleLogger(LogLevel.off) this.agentDependencies = agentDependencies - - const { mediatorConnectionsInvite, clearDefaultMediator, defaultMediatorId } = this.initConfig - - const allowOne = [mediatorConnectionsInvite, clearDefaultMediator, defaultMediatorId].filter((e) => e !== undefined) - if (allowOne.length > 1) { - throw new AriesFrameworkError( - `Only one of 'mediatorConnectionsInvite', 'clearDefaultMediator' and 'defaultMediatorId' can be set as they negate each other` - ) - } } /** @@ -40,65 +28,10 @@ export class AgentConfig { return this.initConfig.walletConfig } - /** - * @deprecated use autoAcceptConnections from the `ConnectionsModuleConfig` class - */ - public get autoAcceptConnections() { - return this.initConfig.autoAcceptConnections ?? false - } - - /** - * @deprecated use autoAcceptProofs from the `ProofsModuleConfig` class - */ - public get autoAcceptProofs() { - return this.initConfig.autoAcceptProofs ?? AutoAcceptProof.Never - } - - /** - * @deprecated use autoAcceptCredentials from the `CredentialsModuleConfig` class - */ - public get autoAcceptCredentials() { - return this.initConfig.autoAcceptCredentials ?? AutoAcceptCredential.Never - } - public get didCommMimeType() { return this.initConfig.didCommMimeType ?? DidCommMimeType.V1 } - /** - * @deprecated use mediatorPollingInterval from the `RecipientModuleConfig` class - */ - public get mediatorPollingInterval() { - return this.initConfig.mediatorPollingInterval ?? 5000 - } - - /** - * @deprecated use mediatorPickupStrategy from the `RecipientModuleConfig` class - */ - public get mediatorPickupStrategy() { - return this.initConfig.mediatorPickupStrategy - } - - /** - * @deprecated use maximumMessagePickup from the `RecipientModuleConfig` class - */ - public get maximumMessagePickup() { - return this.initConfig.maximumMessagePickup ?? 10 - } - /** - * @deprecated use baseMediatorReconnectionIntervalMs from the `RecipientModuleConfig` class - */ - public get baseMediatorReconnectionIntervalMs() { - return this.initConfig.baseMediatorReconnectionIntervalMs ?? 100 - } - - /** - * @deprecated use maximumMediatorReconnectionIntervalMs from the `RecipientModuleConfig` class - */ - public get maximumMediatorReconnectionIntervalMs() { - return this.initConfig.maximumMediatorReconnectionIntervalMs ?? Number.POSITIVE_INFINITY - } - /** * Encode keys in did:key format instead of 'naked' keys, as stated in Aries RFC 0360. * @@ -123,34 +56,6 @@ export class AgentConfig { this._endpoints = endpoints } - /** - * @deprecated use mediatorInvitationUrl from the `RecipientModuleConfig` class - */ - public get mediatorConnectionsInvite() { - return this.initConfig.mediatorConnectionsInvite - } - - /** - * @deprecated use autoAcceptMediationRequests from the `MediatorModuleConfig` class - */ - public get autoAcceptMediationRequests() { - return this.initConfig.autoAcceptMediationRequests ?? false - } - - /** - * @deprecated you can use `RecipientApi.setDefaultMediator` to set the default mediator. - */ - public get defaultMediatorId() { - return this.initConfig.defaultMediatorId - } - - /** - * @deprecated you can set the `default` tag to `false` (or remove it completely) to clear the default mediator. - */ - public get clearDefaultMediator() { - return this.initConfig.clearDefaultMediator ?? false - } - public get useDidSovPrefixWhereAllowed() { return this.initConfig.useDidSovPrefixWhereAllowed ?? false } diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index aa3ab239c6..7259eeb092 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -1,4 +1,3 @@ -import type { AgentConfig } from './AgentConfig' import type { Module, DependencyManager, ApiModule } from '../plugins' import type { IsAny } from '../types' import type { Constructor } from '../utils/mixins' @@ -12,7 +11,7 @@ import { DiscoverFeaturesModule } from '../modules/discover-features' import { GenericRecordsModule } from '../modules/generic-records' import { OutOfBandModule } from '../modules/oob' import { ProofsModule } from '../modules/proofs' -import { MediatorModule, RecipientModule } from '../modules/routing' +import { MediatorModule, MediationRecipientModule } from '../modules/routing' import { W3cVcModule } from '../modules/vc' import { WalletModule } from '../wallet' @@ -112,39 +111,16 @@ export type CustomOrDefaultApi< : InstanceType /** - * Method to get the default agent modules to be registered on any agent instance. - * - * @note This implementation is quite ugly and is meant to be temporary. It extracts the module specific config from the agent config - * and will only construct the module if the method is called. This prevents the modules from being initialized if they are already configured by the end - * user using the `module` property in the agent constructor. + * Method to get the default agent modules to be registered on any agent instance. It doens't configure the modules in any way, + * and if that's needed the user needs to provide the module in the agent constructor */ -function getDefaultAgentModules(agentConfig: AgentConfig) { +function getDefaultAgentModules() { return { - connections: () => - new ConnectionsModule({ - autoAcceptConnections: agentConfig.autoAcceptConnections, - }), - credentials: () => - new CredentialsModule({ - autoAcceptCredentials: agentConfig.autoAcceptCredentials, - }), - proofs: () => - new ProofsModule({ - autoAcceptProofs: agentConfig.autoAcceptProofs, - }), - mediator: () => - new MediatorModule({ - autoAcceptMediationRequests: agentConfig.autoAcceptMediationRequests, - }), - mediationRecipient: () => - new RecipientModule({ - maximumMessagePickup: agentConfig.maximumMessagePickup, - mediatorInvitationUrl: agentConfig.mediatorConnectionsInvite, - mediatorPickupStrategy: agentConfig.mediatorPickupStrategy, - baseMediatorReconnectionIntervalMs: agentConfig.baseMediatorReconnectionIntervalMs, - maximumMediatorReconnectionIntervalMs: agentConfig.maximumMediatorReconnectionIntervalMs, - mediatorPollingInterval: agentConfig.mediatorPollingInterval, - }), + connections: () => new ConnectionsModule(), + credentials: () => new CredentialsModule(), + proofs: () => new ProofsModule(), + mediator: () => new MediatorModule(), + mediationRecipient: () => new MediationRecipientModule(), basicMessages: () => new BasicMessagesModule(), genericRecords: () => new GenericRecordsModule(), discovery: () => new DiscoverFeaturesModule(), @@ -163,11 +139,10 @@ function getDefaultAgentModules(agentConfig: AgentConfig) { * on the default agent. */ export function extendModulesWithDefaultModules( - agentConfig: AgentConfig, modules?: AgentModules ): AgentModules & DefaultAgentModules { const extendedModules: Record = { ...modules } - const defaultAgentModules = getDefaultAgentModules(agentConfig) + const defaultAgentModules = getDefaultAgentModules() // Register all default modules, if not registered yet for (const [moduleKey, getConfiguredModule] of Object.entries(defaultAgentModules)) { diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 27a97be56b..444eba3f9d 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -15,7 +15,7 @@ import { DiscoverFeaturesApi } from '../modules/discover-features' import { GenericRecordsApi } from '../modules/generic-records' import { OutOfBandApi } from '../modules/oob' import { ProofsApi } from '../modules/proofs' -import { MediatorApi, RecipientApi } from '../modules/routing' +import { MediatorApi, MediationRecipientApi } from '../modules/routing' import { StorageUpdateService } from '../storage' import { UpdateAssistant } from '../storage/migration/UpdateAssistant' import { DEFAULT_UPDATE_CONFIG } from '../storage/migration/updates' @@ -46,7 +46,7 @@ export abstract class BaseAgent public readonly proofs: CustomOrDefaultApi public readonly mediator: MediatorApi - public readonly mediationRecipient: RecipientApi + public readonly mediationRecipient: MediationRecipientApi public readonly basicMessages: BasicMessagesApi public readonly genericRecords: GenericRecordsApi public readonly discovery: DiscoverFeaturesApi @@ -89,7 +89,7 @@ export abstract class BaseAgent this.proofs = this.dependencyManager.resolve(ProofsApi) as CustomOrDefaultApi this.mediator = this.dependencyManager.resolve(MediatorApi) - this.mediationRecipient = this.dependencyManager.resolve(RecipientApi) + this.mediationRecipient = this.dependencyManager.resolve(MediationRecipientApi) this.basicMessages = this.dependencyManager.resolve(BasicMessagesApi) this.genericRecords = this.dependencyManager.resolve(GenericRecordsApi) this.discovery = this.dependencyManager.resolve(DiscoverFeaturesApi) diff --git a/packages/core/src/agent/__tests__/Agent.test.ts b/packages/core/src/agent/__tests__/Agent.test.ts index 7423f35c3c..fc56877afd 100644 --- a/packages/core/src/agent/__tests__/Agent.test.ts +++ b/packages/core/src/agent/__tests__/Agent.test.ts @@ -20,8 +20,8 @@ import { MediationRepository, MediatorApi, MediatorService, - RecipientApi, - RecipientModule, + MediationRecipientApi, + MediationRecipientModule, } from '../../modules/routing' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { WalletError } from '../../wallet/error' @@ -75,7 +75,7 @@ describe('Agent', () => { ...agentOptions, modules: { myModule: new MyModule(), - mediationRecipient: new RecipientModule({ + mediationRecipient: new MediationRecipientModule({ maximumMessagePickup: 42, }), ...getIndySdkModules(), @@ -170,7 +170,7 @@ describe('Agent', () => { expect(container.resolve(BasicMessageRepository)).toBeInstanceOf(BasicMessageRepository) expect(container.resolve(MediatorApi)).toBeInstanceOf(MediatorApi) - expect(container.resolve(RecipientApi)).toBeInstanceOf(RecipientApi) + expect(container.resolve(MediationRecipientApi)).toBeInstanceOf(MediationRecipientApi) expect(container.resolve(MediationRepository)).toBeInstanceOf(MediationRepository) expect(container.resolve(MediatorService)).toBeInstanceOf(MediatorService) expect(container.resolve(MediationRecipientService)).toBeInstanceOf(MediationRecipientService) @@ -207,7 +207,7 @@ describe('Agent', () => { expect(container.resolve(BasicMessageRepository)).toBe(container.resolve(BasicMessageRepository)) expect(container.resolve(MediatorApi)).toBe(container.resolve(MediatorApi)) - expect(container.resolve(RecipientApi)).toBe(container.resolve(RecipientApi)) + expect(container.resolve(MediationRecipientApi)).toBe(container.resolve(MediationRecipientApi)) expect(container.resolve(MediationRepository)).toBe(container.resolve(MediationRepository)) expect(container.resolve(MediatorService)).toBe(container.resolve(MediatorService)) expect(container.resolve(MediationRecipientService)).toBe(container.resolve(MediationRecipientService)) diff --git a/packages/core/src/agent/__tests__/AgentConfig.test.ts b/packages/core/src/agent/__tests__/AgentConfig.test.ts index fced194ef5..59a32da7f3 100644 --- a/packages/core/src/agent/__tests__/AgentConfig.test.ts +++ b/packages/core/src/agent/__tests__/AgentConfig.test.ts @@ -73,12 +73,10 @@ describe('AgentConfig', () => { const newAgentConfig = agentConfig.extend({ label: 'anotherLabel', - autoAcceptConnections: true, }) expect(newAgentConfig).toMatchObject({ label: 'anotherLabel', - autoAcceptConnections: true, }) }) }) diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts index 7ee76dfe6f..25466501d7 100644 --- a/packages/core/src/agent/__tests__/AgentModules.test.ts +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -1,6 +1,5 @@ import type { Module } from '../../plugins' -import { getAgentConfig } from '../../../tests/helpers' import { BasicMessagesModule } from '../../modules/basic-messages' import { CacheModule } from '../../modules/cache' import { ConnectionsModule } from '../../modules/connections' @@ -10,14 +9,12 @@ import { DiscoverFeaturesModule } from '../../modules/discover-features' import { GenericRecordsModule } from '../../modules/generic-records' import { OutOfBandModule } from '../../modules/oob' import { ProofsModule } from '../../modules/proofs' -import { MediatorModule, RecipientModule } from '../../modules/routing' +import { MediatorModule, MediationRecipientModule } from '../../modules/routing' import { W3cVcModule } from '../../modules/vc' import { DependencyManager, injectable } from '../../plugins' import { WalletModule } from '../../wallet' import { extendModulesWithDefaultModules, getAgentApi } from '../AgentModules' -const agentConfig = getAgentConfig('AgentModules Test') - @injectable() class MyApi {} @@ -54,14 +51,14 @@ describe('AgentModules', () => { describe('extendModulesWithDefaultModules', () => { test('returns default modules if no modules were provided', () => { - const extendedModules = extendModulesWithDefaultModules(agentConfig) + const extendedModules = extendModulesWithDefaultModules() expect(extendedModules).toEqual({ connections: expect.any(ConnectionsModule), credentials: expect.any(CredentialsModule), proofs: expect.any(ProofsModule), mediator: expect.any(MediatorModule), - mediationRecipient: expect.any(RecipientModule), + mediationRecipient: expect.any(MediationRecipientModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), @@ -75,7 +72,7 @@ describe('AgentModules', () => { test('returns custom and default modules if custom modules are provided', () => { const myModule = new MyModuleWithApi() - const extendedModules = extendModulesWithDefaultModules(agentConfig, { + const extendedModules = extendModulesWithDefaultModules({ myModule, }) @@ -84,7 +81,7 @@ describe('AgentModules', () => { credentials: expect.any(CredentialsModule), proofs: expect.any(ProofsModule), mediator: expect.any(MediatorModule), - mediationRecipient: expect.any(RecipientModule), + mediationRecipient: expect.any(MediationRecipientModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), @@ -100,7 +97,7 @@ describe('AgentModules', () => { test('does not override default module if provided as custom module', () => { const myModule = new MyModuleWithApi() const connections = new ConnectionsModule() - const extendedModules = extendModulesWithDefaultModules(agentConfig, { + const extendedModules = extendModulesWithDefaultModules({ myModule, connections, }) @@ -110,7 +107,7 @@ describe('AgentModules', () => { credentials: expect.any(CredentialsModule), proofs: expect.any(ProofsModule), mediator: expect.any(MediatorModule), - mediationRecipient: expect.any(RecipientModule), + mediationRecipient: expect.any(MediationRecipientModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), diff --git a/packages/core/src/modules/connections/ConnectionsModuleConfig.ts b/packages/core/src/modules/connections/ConnectionsModuleConfig.ts index b4b69edacf..59aaf8cb5b 100644 --- a/packages/core/src/modules/connections/ConnectionsModuleConfig.ts +++ b/packages/core/src/modules/connections/ConnectionsModuleConfig.ts @@ -13,14 +13,21 @@ export interface ConnectionsModuleConfigOptions { } export class ConnectionsModuleConfig { + #autoAcceptConnections?: boolean private options: ConnectionsModuleConfigOptions public constructor(options?: ConnectionsModuleConfigOptions) { this.options = options ?? {} + this.#autoAcceptConnections = this.options.autoAcceptConnections } /** See {@link ConnectionsModuleConfigOptions.autoAcceptConnections} */ public get autoAcceptConnections() { - return this.options.autoAcceptConnections ?? false + return this.#autoAcceptConnections ?? false + } + + /** See {@link ConnectionsModuleConfigOptions.autoAcceptConnections} */ + public set autoAcceptConnections(autoAcceptConnections: boolean) { + this.#autoAcceptConnections = autoAcceptConnections } } diff --git a/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts b/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts index 457e5f7b7e..9e029a27df 100644 --- a/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts +++ b/packages/core/src/modules/connections/__tests__/connection-manual.e2e.test.ts @@ -9,6 +9,7 @@ import { setupSubjectTransports } from '../../../../tests' import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { ConnectionEventTypes } from '../ConnectionEvents' +import { ConnectionsModule } from '../ConnectionsModule' import { DidExchangeState } from '../models' function waitForRequest(agent: Agent, theirLabel: string) { @@ -49,27 +50,39 @@ describe('Manual Connection Flow', () => { 'Manual Connection Flow Alice', { label: 'alice', - autoAcceptConnections: false, endpoints: ['rxjs:alice'], }, - getIndySdkModules() + { + ...getIndySdkModules(), + connections: new ConnectionsModule({ + autoAcceptConnections: false, + }), + } ) const bobAgentOptions = getAgentOptions( 'Manual Connection Flow Bob', { label: 'bob', - autoAcceptConnections: false, endpoints: ['rxjs:bob'], }, - getIndySdkModules() + { + ...getIndySdkModules(), + connections: new ConnectionsModule({ + autoAcceptConnections: false, + }), + } ) const faberAgentOptions = getAgentOptions( 'Manual Connection Flow Faber', { - autoAcceptConnections: false, endpoints: ['rxjs:faber'], }, - getIndySdkModules() + { + ...getIndySdkModules(), + connections: new ConnectionsModule({ + autoAcceptConnections: false, + }), + } ) const aliceAgent = new Agent(aliceAgentOptions) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index a261e07989..3312a27f19 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -1,4 +1,5 @@ import type { SubjectMessage } from '../../../../../../../../tests/transport/SubjectInboundTransport' +import type { AnonCredsTestsAgent } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import { Subject } from 'rxjs' @@ -24,7 +25,7 @@ import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' import { uuid } from '../../../../../utils/uuid' import { HandshakeProtocol } from '../../../../connections' import { CredentialEventTypes } from '../../../../credentials' -import { MediatorPickupStrategy } from '../../../../routing' +import { MediatorModule, MediatorPickupStrategy, MediationRecipientModule } from '../../../../routing' import { ProofEventTypes } from '../../../ProofEvents' import { AutoAcceptProof, ProofState } from '../../../models' @@ -266,12 +267,16 @@ describe('V2 Connectionless Proofs - Indy', () => { const mediatorOptions = getAgentOptions( `Connectionless proofs with mediator Mediator-${unique}`, { - autoAcceptMediationRequests: true, endpoints: ['rxjs:mediator'], }, - getLegacyAnonCredsModules({ - autoAcceptProofs: AutoAcceptProof.Always, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, + }), + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + }), + } ) const mediatorMessages = new Subject() @@ -295,28 +300,34 @@ describe('V2 Connectionless Proofs - Indy', () => { const faberOptions = getAgentOptions( `Connectionless proofs with mediator Faber-${unique}`, + {}, { - mediatorConnectionsInvite: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', + ...getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptProofs: AutoAcceptProof.Always, - }) + mediationRecipient: new MediationRecipientModule({ + mediatorInvitationUrl: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const aliceOptions = getAgentOptions( `Connectionless proofs with mediator Alice-${unique}`, + {}, { - mediatorConnectionsInvite: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com', + ...getLegacyAnonCredsModules({ + autoAcceptProofs: AutoAcceptProof.Always, }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptProofs: AutoAcceptProof.Always, - }) + mediationRecipient: new MediationRecipientModule({ + mediatorInvitationUrl: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const faberAgent = new Agent(faberOptions) @@ -346,10 +357,10 @@ describe('V2 Connectionless Proofs - Indy', () => { // issue credential with two linked attachments await issueLegacyAnonCredsCredential({ - issuerAgent: faberAgent, + issuerAgent: faberAgent as AnonCredsTestsAgent, issuerReplay: faberReplay, issuerHolderConnectionId: faberConnection.id, - holderAgent: aliceAgent, + holderAgent: aliceAgent as AnonCredsTestsAgent, holderReplay: aliceReplay, offer: { credentialDefinitionId: credentialDefinition.credentialDefinitionId, diff --git a/packages/core/src/modules/routing/RecipientApi.ts b/packages/core/src/modules/routing/MediationRecipientApi.ts similarity index 96% rename from packages/core/src/modules/routing/RecipientApi.ts rename to packages/core/src/modules/routing/MediationRecipientApi.ts index 65216881b6..7643542ee5 100644 --- a/packages/core/src/modules/routing/RecipientApi.ts +++ b/packages/core/src/modules/routing/MediationRecipientApi.ts @@ -24,8 +24,8 @@ import { DidsApi } from '../dids' import { verkeyToDidKey } from '../dids/helpers' import { DiscoverFeaturesApi } from '../discover-features' +import { MediationRecipientModuleConfig } from './MediationRecipientModuleConfig' import { MediatorPickupStrategy } from './MediatorPickupStrategy' -import { RecipientModuleConfig } from './RecipientModuleConfig' import { RoutingEventTypes } from './RoutingEvents' import { KeylistUpdateResponseHandler } from './handlers/KeylistUpdateResponseHandler' import { MediationDenyHandler } from './handlers/MediationDenyHandler' @@ -39,8 +39,8 @@ import { MediationRecipientService } from './services/MediationRecipientService' import { RoutingService } from './services/RoutingService' @injectable() -export class RecipientApi { - public config: RecipientModuleConfig +export class MediationRecipientApi { + public config: MediationRecipientModuleConfig private mediationRecipientService: MediationRecipientService private connectionService: ConnectionService @@ -70,7 +70,7 @@ export class RecipientApi { @inject(InjectionSymbols.Logger) logger: Logger, agentContext: AgentContext, @inject(InjectionSymbols.Stop$) stop$: Subject, - recipientModuleConfig: RecipientModuleConfig + mediationRecipientModuleConfig: MediationRecipientModuleConfig ) { this.connectionService = connectionService this.dids = dids @@ -83,23 +83,11 @@ export class RecipientApi { this.routingService = routingService this.agentContext = agentContext this.stop$ = stop$ - this.config = recipientModuleConfig + this.config = mediationRecipientModuleConfig this.registerMessageHandlers(messageHandlerRegistry) } public async initialize() { - const { defaultMediatorId, clearDefaultMediator } = this.agentContext.config - - // Set default mediator by id - if (defaultMediatorId) { - const mediatorRecord = await this.mediationRecipientService.getById(this.agentContext, defaultMediatorId) - await this.mediationRecipientService.setDefaultMediator(this.agentContext, mediatorRecord) - } - // Clear the stored default mediator - else if (clearDefaultMediator) { - await this.mediationRecipientService.clearDefaultMediator(this.agentContext) - } - // Poll for messages from mediator const defaultMediator = await this.findDefaultMediator() if (defaultMediator) { diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/MediationRecipientModule.ts similarity index 58% rename from packages/core/src/modules/routing/RecipientModule.ts rename to packages/core/src/modules/routing/MediationRecipientModule.ts index 8ba9364a9d..f27da353c9 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/MediationRecipientModule.ts @@ -1,21 +1,21 @@ -import type { RecipientModuleConfigOptions } from './RecipientModuleConfig' +import type { MediationRecipientModuleConfigOptions } from './MediationRecipientModuleConfig' import type { FeatureRegistry } from '../../agent/FeatureRegistry' import type { DependencyManager, Module } from '../../plugins' import { Protocol } from '../../agent/models' -import { RecipientApi } from './RecipientApi' -import { RecipientModuleConfig } from './RecipientModuleConfig' +import { MediationRecipientApi } from './MediationRecipientApi' +import { MediationRecipientModuleConfig } from './MediationRecipientModuleConfig' import { MediationRole } from './models' import { MediationRepository } from './repository' import { MediationRecipientService, RoutingService } from './services' -export class RecipientModule implements Module { - public readonly config: RecipientModuleConfig - public readonly api = RecipientApi +export class MediationRecipientModule implements Module { + public readonly config: MediationRecipientModuleConfig + public readonly api = MediationRecipientApi - public constructor(config?: RecipientModuleConfigOptions) { - this.config = new RecipientModuleConfig(config) + public constructor(config?: MediationRecipientModuleConfigOptions) { + this.config = new MediationRecipientModuleConfig(config) } /** @@ -23,10 +23,10 @@ export class RecipientModule implements Module { */ public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { // Api - dependencyManager.registerContextScoped(RecipientApi) + dependencyManager.registerContextScoped(MediationRecipientApi) // Config - dependencyManager.registerInstance(RecipientModuleConfig, this.config) + dependencyManager.registerInstance(MediationRecipientModuleConfig, this.config) // Services dependencyManager.registerSingleton(MediationRecipientService) diff --git a/packages/core/src/modules/routing/RecipientModuleConfig.ts b/packages/core/src/modules/routing/MediationRecipientModuleConfig.ts similarity index 81% rename from packages/core/src/modules/routing/RecipientModuleConfig.ts rename to packages/core/src/modules/routing/MediationRecipientModuleConfig.ts index 4463289936..6f94234fc5 100644 --- a/packages/core/src/modules/routing/RecipientModuleConfig.ts +++ b/packages/core/src/modules/routing/MediationRecipientModuleConfig.ts @@ -1,10 +1,10 @@ import type { MediatorPickupStrategy } from './MediatorPickupStrategy' /** - * RecipientModuleConfigOptions defines the interface for the options of the RecipientModuleConfig class. + * MediationRecipientModuleConfigOptions defines the interface for the options of the MediationRecipientModuleConfig class. * This can contain optional parameters that have default values in the config class itself. */ -export interface RecipientModuleConfigOptions { +export interface MediationRecipientModuleConfigOptions { /** * Strategy to use for picking up messages from the mediator. If no strategy is provided, the agent will use the discover * features protocol to determine the best strategy. @@ -68,39 +68,39 @@ export interface RecipientModuleConfigOptions { mediatorInvitationUrl?: string } -export class RecipientModuleConfig { - private options: RecipientModuleConfigOptions +export class MediationRecipientModuleConfig { + private options: MediationRecipientModuleConfigOptions - public constructor(options?: RecipientModuleConfigOptions) { + public constructor(options?: MediationRecipientModuleConfigOptions) { this.options = options ?? {} } - /** See {@link RecipientModuleConfigOptions.mediatorPollingInterval} */ + /** See {@link MediationRecipientModuleConfigOptions.mediatorPollingInterval} */ public get mediatorPollingInterval() { return this.options.mediatorPollingInterval ?? 5000 } - /** See {@link RecipientModuleConfigOptions.mediatorPickupStrategy} */ + /** See {@link MediationRecipientModuleConfigOptions.mediatorPickupStrategy} */ public get mediatorPickupStrategy() { return this.options.mediatorPickupStrategy } - /** See {@link RecipientModuleConfigOptions.maximumMessagePickup} */ + /** See {@link MediationRecipientModuleConfigOptions.maximumMessagePickup} */ public get maximumMessagePickup() { return this.options.maximumMessagePickup ?? 10 } - /** See {@link RecipientModuleConfigOptions.baseMediatorReconnectionIntervalMs} */ + /** See {@link MediationRecipientModuleConfigOptions.baseMediatorReconnectionIntervalMs} */ public get baseMediatorReconnectionIntervalMs() { return this.options.baseMediatorReconnectionIntervalMs ?? 100 } - /** See {@link RecipientModuleConfigOptions.maximumMediatorReconnectionIntervalMs} */ + /** See {@link MediationRecipientModuleConfigOptions.maximumMediatorReconnectionIntervalMs} */ public get maximumMediatorReconnectionIntervalMs() { return this.options.maximumMediatorReconnectionIntervalMs ?? Number.POSITIVE_INFINITY } - /** See {@link RecipientModuleConfigOptions.mediatorInvitationUrl} */ + /** See {@link MediationRecipientModuleConfigOptions.mediatorInvitationUrl} */ public get mediatorInvitationUrl() { return this.options.mediatorInvitationUrl } diff --git a/packages/core/src/modules/routing/MediatorModuleConfig.ts b/packages/core/src/modules/routing/MediatorModuleConfig.ts index 2e781fbc85..8b70d9591a 100644 --- a/packages/core/src/modules/routing/MediatorModuleConfig.ts +++ b/packages/core/src/modules/routing/MediatorModuleConfig.ts @@ -1,5 +1,5 @@ /** - * MediatorModuleConfigOptions defines the interface for the options of the RecipientModuleConfig class. + * MediatorModuleConfigOptions defines the interface for the options of the MediatorModuleConfig class. * This can contain optional parameters that have default values in the config class itself. */ export interface MediatorModuleConfigOptions { @@ -18,7 +18,7 @@ export class MediatorModuleConfig { this.options = options ?? {} } - /** See {@link RecipientModuleConfigOptions.autoAcceptMediationRequests} */ + /** See {@link MediatorModuleConfigOptions.autoAcceptMediationRequests} */ public get autoAcceptMediationRequests() { return this.options.autoAcceptMediationRequests ?? false } diff --git a/packages/core/src/modules/routing/__tests__/RecipientModule.test.ts b/packages/core/src/modules/routing/__tests__/MediationRecipientModule.test.ts similarity index 80% rename from packages/core/src/modules/routing/__tests__/RecipientModule.test.ts rename to packages/core/src/modules/routing/__tests__/MediationRecipientModule.test.ts index 0008d36f8d..dad1f499be 100644 --- a/packages/core/src/modules/routing/__tests__/RecipientModule.test.ts +++ b/packages/core/src/modules/routing/__tests__/MediationRecipientModule.test.ts @@ -1,7 +1,7 @@ import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' -import { RecipientApi } from '../RecipientApi' -import { RecipientModule } from '../RecipientModule' +import { MediationRecipientApi } from '../MediationRecipientApi' +import { MediationRecipientModule } from '../MediationRecipientModule' import { MediationRepository } from '../repository' import { MediationRecipientService, RoutingService } from '../services' @@ -15,12 +15,12 @@ const FeatureRegistryMock = FeatureRegistry as jest.Mock const featureRegistry = new FeatureRegistryMock() -describe('RecipientModule', () => { +describe('MediationRecipientModule', () => { test('registers dependencies on the dependency manager', () => { - new RecipientModule().register(dependencyManager, featureRegistry) + new MediationRecipientModule().register(dependencyManager, featureRegistry) expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(RecipientApi) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(MediationRecipientApi) expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediationRecipientService) diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 8fd1661838..574f999297 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../../../../tests/transport/SubjectInboundTransport' import type { AgentDependencies } from '../../../agent/AgentDependencies' +import type { AgentModulesInput } from '../../../agent/AgentModules' import type { InitConfig } from '../../../types' import { Subject } from 'rxjs' @@ -12,6 +13,8 @@ import { getAgentOptions, waitForBasicMessage } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { sleep } from '../../../utils/sleep' import { ConnectionRecord, HandshakeProtocol } from '../../connections' +import { MediationRecipientModule } from '../MediationRecipientModule' +import { MediatorModule } from '../MediatorModule' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' import { MediationState } from '../models/MediationState' @@ -19,10 +22,14 @@ const recipientAgentOptions = getAgentOptions('Mediation: Recipient', {}, getInd const mediatorAgentOptions = getAgentOptions( 'Mediation: Mediator', { - autoAcceptMediationRequests: true, endpoints: ['rxjs:mediator'], }, - getIndySdkModules() + { + ...getIndySdkModules(), + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + }), + } ) const senderAgentOptions = getAgentOptions( @@ -51,10 +58,12 @@ describe('mediator establishment', () => { mediatorAgentOptions: { readonly config: InitConfig readonly dependencies: AgentDependencies + modules: AgentModulesInput }, recipientAgentOptions: { config: InitConfig dependencies: AgentDependencies + modules: AgentModulesInput } ) => { const mediatorMessages = new Subject() @@ -82,10 +91,13 @@ describe('mediator establishment', () => { // Initialize recipient with mediation connections invitation recipientAgent = new Agent({ ...recipientAgentOptions, - config: { - ...recipientAgentOptions.config, - mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com/ssi', + modules: { + ...recipientAgentOptions.modules, + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + mediatorInvitationUrl: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com/ssi', + }), }), }, }) @@ -159,14 +171,7 @@ describe('mediator establishment', () => { 5. Assert endpoint in recipient invitation for sender is mediator endpoint 6. Send basic message from sender to recipient and assert it is received on the recipient side `, async () => { - await e2eMediationTest(mediatorAgentOptions, { - ...recipientAgentOptions, - config: { - ...recipientAgentOptions.config, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - mediatorPollingInterval: 100, - }, - }) + await e2eMediationTest(mediatorAgentOptions, recipientAgentOptions) }) test('Mediation end-to-end flow (not using did:key)', async () => { @@ -179,9 +184,7 @@ describe('mediator establishment', () => { ...recipientAgentOptions, config: { ...recipientAgentOptions.config, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, useDidKeyInProtocols: false, - mediatorPollingInterval: 100, }, } ) @@ -213,12 +216,14 @@ describe('mediator establishment', () => { // Initialize recipient with mediation connections invitation recipientAgent = new Agent({ ...recipientAgentOptions, - config: { - ...recipientAgentOptions.config, - mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com/ssi', + modules: { + ...recipientAgentOptions.modules, + mediationRecipient: new MediationRecipientModule({ + mediatorInvitationUrl: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com/ssi', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, }) recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) @@ -244,12 +249,14 @@ describe('mediator establishment', () => { await recipientAgent.shutdown() recipientAgent = new Agent({ ...recipientAgentOptions, - config: { - ...recipientAgentOptions.config, - mediatorConnectionsInvite: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ - domain: 'https://example.com/ssi', + modules: { + ...recipientAgentOptions.modules, + mediationRecipient: new MediationRecipientModule({ + mediatorInvitationUrl: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ + domain: 'https://example.com/ssi', + }), + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }), - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, }) recipientAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts b/packages/core/src/modules/routing/__tests__/pickup.test.ts index 54a37efd81..2879dacca8 100644 --- a/packages/core/src/modules/routing/__tests__/pickup.test.ts +++ b/packages/core/src/modules/routing/__tests__/pickup.test.ts @@ -11,17 +11,10 @@ import { Agent } from '../../../agent/Agent' import { HandshakeProtocol } from '../../connections' import { MediatorPickupStrategy } from '../MediatorPickupStrategy' -const recipientOptions = getAgentOptions( - 'Mediation: Recipient Pickup', - { - autoAcceptConnections: true, - }, - getIndySdkModules() -) +const recipientOptions = getAgentOptions('Mediation: Recipient Pickup', {}, getIndySdkModules()) const mediatorOptions = getAgentOptions( 'Mediation: Mediator Pickup', { - autoAcceptConnections: true, endpoints: ['wss://mediator'], }, getIndySdkModules() diff --git a/packages/core/src/modules/routing/index.ts b/packages/core/src/modules/routing/index.ts index 6032d26ecd..a644af607a 100644 --- a/packages/core/src/modules/routing/index.ts +++ b/packages/core/src/modules/routing/index.ts @@ -5,7 +5,7 @@ export * from './repository' export * from './models' export * from './RoutingEvents' export * from './MediatorApi' -export * from './RecipientApi' +export * from './MediationRecipientApi' export * from './MediatorPickupStrategy' export * from './MediatorModule' -export * from './RecipientModule' +export * from './MediationRecipientModule' diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index aac33420d6..f5149a3828 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -28,7 +28,7 @@ import { ConnectionService } from '../../connections/services/ConnectionService' import { DidKey } from '../../dids' import { didKeyToVerkey, isDidKey } from '../../dids/helpers' import { ProblemReportError } from '../../problem-reports' -import { RecipientModuleConfig } from '../RecipientModuleConfig' +import { MediationRecipientModuleConfig } from '../MediationRecipientModuleConfig' import { RoutingEventTypes } from '../RoutingEvents' import { RoutingProblemReportReason } from '../error' import { @@ -49,20 +49,20 @@ export class MediationRecipientService { private eventEmitter: EventEmitter private connectionService: ConnectionService private messageSender: MessageSender - private recipientModuleConfig: RecipientModuleConfig + private mediationRecipientModuleConfig: MediationRecipientModuleConfig public constructor( connectionService: ConnectionService, messageSender: MessageSender, mediatorRepository: MediationRepository, eventEmitter: EventEmitter, - recipientModuleConfig: RecipientModuleConfig + mediationRecipientModuleConfig: MediationRecipientModuleConfig ) { this.mediationRepository = mediatorRepository this.eventEmitter = eventEmitter this.connectionService = connectionService this.messageSender = messageSender - this.recipientModuleConfig = recipientModuleConfig + this.mediationRecipientModuleConfig = mediationRecipientModuleConfig } public async createStatusRequest( @@ -344,7 +344,7 @@ export class MediationRecipientService { return null } - const { maximumMessagePickup } = this.recipientModuleConfig + const { maximumMessagePickup } = this.mediationRecipientModuleConfig const limit = messageCount < maximumMessagePickup ? messageCount : maximumMessagePickup const deliveryRequestMessage = new DeliveryRequestMessage({ diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index e00b530afc..e958a3d587 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -16,7 +16,7 @@ import { ConnectionRepository } from '../../../connections/repository/Connection import { ConnectionService } from '../../../connections/services/ConnectionService' import { DidRepository } from '../../../dids/repository/DidRepository' import { DidRegistrarService } from '../../../dids/services/DidRegistrarService' -import { RecipientModuleConfig } from '../../RecipientModuleConfig' +import { MediationRecipientModuleConfig } from '../../MediationRecipientModuleConfig' import { RoutingEventTypes } from '../../RoutingEvents' import { KeylistUpdateAction, @@ -106,7 +106,7 @@ describe('MediationRecipientService', () => { messageSender, mediationRepository, eventEmitter, - new RecipientModuleConfig() + new MediationRecipientModuleConfig() ) }) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 7a62a494fa..0143c5c406 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,7 +1,4 @@ import type { Logger } from './logger' -import type { AutoAcceptCredential } from './modules/credentials/models/CredentialAutoAcceptType' -import type { AutoAcceptProof } from './modules/proofs' -import type { MediatorPickupStrategy } from './modules/routing' export enum KeyDerivationMethod { /** default value in indy-sdk. Will be used when no value is provided */ @@ -59,86 +56,6 @@ export interface InitConfig { useDidSovPrefixWhereAllowed?: boolean connectionImageUrl?: string autoUpdateStorageOnStartup?: boolean - - /** - * @deprecated configure `autoAcceptConnections` on the `ConnectionsModule` class - * @note This setting will be ignored if the `ConnectionsModule` is manually configured as - * a module - */ - autoAcceptConnections?: boolean - - /** - * @deprecated configure `autoAcceptProofs` on the `ProofModule` class - * @note This setting will be ignored if the `ProofsModule` is manually configured as - * a module - */ - autoAcceptProofs?: AutoAcceptProof - - /** - * @deprecated configure `autoAcceptCredentials` on the `CredentialsModule` class - * @note This setting will be ignored if the `CredentialsModule` is manually configured as - * a module - */ - autoAcceptCredentials?: AutoAcceptCredential - - /** - * @deprecated configure `autoAcceptMediationRequests` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - autoAcceptMediationRequests?: boolean - - /** - * @deprecated configure `mediatorConnectionsInvite` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - mediatorConnectionsInvite?: string - - /** - * @deprecated you can use `RecipientApi.setDefaultMediator` to set the default mediator. - */ - defaultMediatorId?: string - - /** - * @deprecated you can set the `default` tag to `false` (or remove it completely) to clear the default mediator. - */ - clearDefaultMediator?: boolean - - /** - * @deprecated configure `mediatorPollingInterval` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - mediatorPollingInterval?: number - - /** - * @deprecated configure `mediatorPickupStrategy` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - mediatorPickupStrategy?: MediatorPickupStrategy - - /** - * @deprecated configure `maximumMessagePickup` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - maximumMessagePickup?: number - - /** - * @deprecated configure `baseMediatorReconnectionIntervalMs` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - baseMediatorReconnectionIntervalMs?: number - - /** - * @deprecated configure `maximumMediatorReconnectionIntervalMs` on the `RecipientModule` class - * @note This setting will be ignored if the `RecipientModule` is manually configured as - * a module - */ - maximumMediatorReconnectionIntervalMs?: number } export type ProtocolVersion = `${number}.${number}` diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 2525879598..0f64b31d2c 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -5,6 +5,7 @@ import { filter, firstValueFrom, map, timeout } from 'rxjs' import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' import { + MediatorModule, Key, AgentEventTypes, KeylistUpdateMessage, @@ -51,9 +52,13 @@ describe('connections', () => { 'Mediator Agent Connections', { endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, }, - getIndySdkModules() + { + ...getIndySdkModules(), + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + }), + } ) faberAgent = new Agent(faberAgentOptions) diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 2da09477b7..8537d5c35f 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -28,6 +28,7 @@ import { catchError, filter, map, take, timeout } from 'rxjs/operators' import { agentDependencies, IndySdkPostgresWalletScheme } from '../../node/src' import { + ConnectionsModule, ConnectionEventTypes, TypedArrayEncoder, AgentConfig, @@ -69,7 +70,7 @@ export { agentDependencies } export function getAgentOptions( name: string, extraConfig: Partial = {}, - modules?: AgentModules + inputModules?: AgentModules ): { config: InitConfig; modules: AgentModules; dependencies: AgentDependencies } { const random = uuid().slice(0, 4) const config: InitConfig = { @@ -79,20 +80,30 @@ export function getAgentOptions( name: string, extraConfig: Partial = {}, - modules?: AgentModules + inputModules?: AgentModules ) { const random = uuid().slice(0, 4) const config: InitConfig = { @@ -115,13 +126,21 @@ export function getPostgresAgentOptions { diff --git a/packages/core/tests/oob-mediation.test.ts b/packages/core/tests/oob-mediation.test.ts index 72d92ea0de..272c468d3b 100644 --- a/packages/core/tests/oob-mediation.test.ts +++ b/packages/core/tests/oob-mediation.test.ts @@ -18,6 +18,8 @@ import { KeylistUpdateAction, MediationState, MediatorPickupStrategy, + MediationRecipientModule, + MediatorModule, } from '../src/modules/routing' import { getAgentOptions, waitForBasicMessage } from './helpers' @@ -33,19 +35,22 @@ const aliceAgentOptions = getAgentOptions( 'OOB mediation - Alice Recipient Agent', { endpoints: ['rxjs:alice'], - // FIXME: discover features returns that we support this protocol, but we don't support all roles - // we should return that we only support the mediator role so we don't have to explicitly declare this - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - getIndySdkModules() + { + ...getIndySdkModules(), + mediationRecipient: new MediationRecipientModule({ + // FIXME: discover features returns that we support this protocol, but we don't support all roles + // we should return that we only support the mediator role so we don't have to explicitly declare this + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const mediatorAgentOptions = getAgentOptions( 'OOB mediation - Mediator Agent', { endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, }, - getIndySdkModules() + { ...getIndySdkModules(), mediator: new MediatorModule({ autoAcceptMediationRequests: true }) } ) describe('out of band with mediation', () => { diff --git a/packages/tenants/tests/tenant-sessions.e2e.test.ts b/packages/tenants/tests/tenant-sessions.e2e.test.ts index 19a99eda19..24a66caad9 100644 --- a/packages/tenants/tests/tenant-sessions.e2e.test.ts +++ b/packages/tenants/tests/tenant-sessions.e2e.test.ts @@ -1,6 +1,6 @@ import type { InitConfig } from '@aries-framework/core' -import { Agent } from '@aries-framework/core' +import { ConnectionsModule, Agent } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { testLogger, indySdk } from '../../core/tests' @@ -16,7 +16,6 @@ const agentConfig: InitConfig = { }, logger: testLogger, endpoints: ['rxjs:tenant-agent1'], - autoAcceptConnections: true, } // Create multi-tenant agent @@ -26,6 +25,9 @@ const agent = new Agent({ modules: { tenants: new TenantsModule({ sessionAcquireTimeout: 10000 }), indySdk: new IndySdkModule({ indySdk }), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), }, }) diff --git a/packages/tenants/tests/tenants.e2e.test.ts b/packages/tenants/tests/tenants.e2e.test.ts index 7f955b395e..f37e962449 100644 --- a/packages/tenants/tests/tenants.e2e.test.ts +++ b/packages/tenants/tests/tenants.e2e.test.ts @@ -1,6 +1,6 @@ import type { InitConfig } from '@aries-framework/core' -import { OutOfBandRecord, Agent } from '@aries-framework/core' +import { ConnectionsModule, OutOfBandRecord, Agent } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' @@ -18,7 +18,6 @@ const agent1Config: InitConfig = { }, logger: testLogger, endpoints: ['rxjs:tenant-agent1'], - autoAcceptConnections: true, } const agent2Config: InitConfig = { @@ -29,7 +28,6 @@ const agent2Config: InitConfig = { }, logger: testLogger, endpoints: ['rxjs:tenant-agent2'], - autoAcceptConnections: true, } // Create multi-tenant agents @@ -38,6 +36,9 @@ const agent1 = new Agent({ modules: { tenants: new TenantsModule(), indySdk: new IndySdkModule({ indySdk }), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), }, dependencies: agentDependencies, }) @@ -47,6 +48,9 @@ const agent2 = new Agent({ modules: { tenants: new TenantsModule(), indySdk: new IndySdkModule({ indySdk }), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), }, dependencies: agentDependencies, }) diff --git a/samples/extension-module/requester.ts b/samples/extension-module/requester.ts index ac83b076ce..c148a35eb0 100644 --- a/samples/extension-module/requester.ts +++ b/samples/extension-module/requester.ts @@ -7,6 +7,7 @@ import { ConsoleLogger, LogLevel, WsOutboundTransport, + ConnectionsModule, } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { filter, first, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' @@ -28,10 +29,12 @@ const run = async () => { key: 'requester', }, logger: new ConsoleLogger(LogLevel.info), - autoAcceptConnections: true, }, modules: { dummy: new DummyModule(), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), }, dependencies: agentDependencies, }) diff --git a/samples/extension-module/responder.ts b/samples/extension-module/responder.ts index 91b106ecf4..c18894ce5f 100644 --- a/samples/extension-module/responder.ts +++ b/samples/extension-module/responder.ts @@ -1,7 +1,7 @@ import type { DummyStateChangedEvent } from './dummy' import type { Socket } from 'net' -import { Agent, ConsoleLogger, LogLevel } from '@aries-framework/core' +import { Agent, ConnectionsModule, ConsoleLogger, LogLevel } from '@aries-framework/core' import { agentDependencies, HttpInboundTransport, WsInboundTransport } from '@aries-framework/node' import express from 'express' import { Server } from 'ws' @@ -28,10 +28,12 @@ const run = async () => { key: 'responder', }, logger: new ConsoleLogger(LogLevel.debug), - autoAcceptConnections: true, }, modules: { dummy: new DummyModule({ autoAcceptRequests }), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), }, dependencies: agentDependencies, }) diff --git a/samples/mediator.ts b/samples/mediator.ts index f62d1d00b8..b76727fe4d 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -21,6 +21,8 @@ import { Server } from 'ws' import { TestLogger } from '../packages/core/tests/logger' import { + ConnectionsModule, + MediatorModule, HttpOutboundTransport, Agent, ConnectionInvitationMessage, @@ -47,13 +49,23 @@ const agentConfig: InitConfig = { id: process.env.WALLET_NAME || 'AriesFrameworkJavaScript', key: process.env.WALLET_KEY || 'AriesFrameworkJavaScript', }, - autoAcceptConnections: true, - autoAcceptMediationRequests: true, + logger, } // Set up agent -const agent = new Agent({ config: agentConfig, dependencies: agentDependencies }) +const agent = new Agent({ + config: agentConfig, + dependencies: agentDependencies, + modules: { + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + }), + connections: new ConnectionsModule({ + autoAcceptConnections: true, + }), + }, +}) const config = agent.config // Create all transports diff --git a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts index f7ce1e929f..36aab1fda7 100644 --- a/tests/e2e-askar-indy-sdk-wallet-subject.test.ts +++ b/tests/e2e-askar-indy-sdk-wallet-subject.test.ts @@ -14,37 +14,52 @@ import { describeRunInNodeVersion } from './runInVersion' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' -import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { + Agent, + AutoAcceptCredential, + MediatorModule, + MediatorPickupStrategy, + MediationRecipientModule, +} from '@aries-framework/core' const recipientAgentOptions = getAgentOptions( 'E2E Askar Subject Recipient', + {}, { - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getAskarAnonCredsIndyModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + ...getAskarAnonCredsIndyModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const mediatorAgentOptions = getAgentOptions( 'E2E Askar Subject Mediator', { endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, }, - getAskarAnonCredsIndyModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getAskarAnonCredsIndyModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediator: new MediatorModule({ autoAcceptMediationRequests: true }), + } ) const senderAgentOptions = getAgentOptions( 'E2E Indy SDK Subject Sender', { endpoints: ['rxjs:sender'], - mediatorPollingInterval: 1000, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) // Performance issues outside of Node 18 @@ -54,9 +69,9 @@ describeRunInNodeVersion([18], 'E2E Askar-AnonCredsRS-IndyVDR Subject tests', () let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientAgentOptions) - mediatorAgent = new Agent(mediatorAgentOptions) - senderAgent = new Agent(senderAgentOptions) + recipientAgent = new Agent(recipientAgentOptions) as AnonCredsTestsAgent + mediatorAgent = new Agent(mediatorAgentOptions) as AnonCredsTestsAgent + senderAgent = new Agent(senderAgentOptions) as AnonCredsTestsAgent }) afterEach(async () => { diff --git a/tests/e2e-http.test.ts b/tests/e2e-http.test.ts index 04bc30ea17..befa8fe000 100644 --- a/tests/e2e-http.test.ts +++ b/tests/e2e-http.test.ts @@ -5,17 +5,27 @@ import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { HttpOutboundTransport, Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { + HttpOutboundTransport, + Agent, + AutoAcceptCredential, + MediatorPickupStrategy, + MediationRecipientModule, + MediatorModule, +} from '@aries-framework/core' import { HttpInboundTransport } from '@aries-framework/node' const recipientAgentOptions = getAgentOptions( 'E2E HTTP Recipient', + {}, { - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const mediatorPort = 3000 @@ -23,11 +33,15 @@ const mediatorAgentOptions = getAgentOptions( 'E2E HTTP Mediator', { endpoints: [`http://localhost:${mediatorPort}`], - autoAcceptMediationRequests: true, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediator: new MediatorModule({ + autoAcceptMediationRequests: true, + }), + } ) const senderPort = 3001 @@ -35,12 +49,16 @@ const senderAgentOptions = getAgentOptions( 'E2E HTTP Sender', { endpoints: [`http://localhost:${senderPort}`], - mediatorPollingInterval: 1000, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) describe('E2E HTTP tests', () => { @@ -49,9 +67,9 @@ describe('E2E HTTP tests', () => { let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientAgentOptions) - mediatorAgent = new Agent(mediatorAgentOptions) - senderAgent = new Agent(senderAgentOptions) + recipientAgent = new Agent(recipientAgentOptions) as AnonCredsTestsAgent + mediatorAgent = new Agent(mediatorAgentOptions) as AnonCredsTestsAgent + senderAgent = new Agent(senderAgentOptions) as AnonCredsTestsAgent }) afterEach(async () => { diff --git a/tests/e2e-subject.test.ts b/tests/e2e-subject.test.ts index 148b13f9c4..a724d4bedd 100644 --- a/tests/e2e-subject.test.ts +++ b/tests/e2e-subject.test.ts @@ -10,37 +10,52 @@ import { e2eTest } from './e2e-test' import { SubjectInboundTransport } from './transport/SubjectInboundTransport' import { SubjectOutboundTransport } from './transport/SubjectOutboundTransport' -import { Agent, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { + Agent, + AutoAcceptCredential, + MediatorModule, + MediatorPickupStrategy, + MediationRecipientModule, +} from '@aries-framework/core' const recipientAgentOptions = getAgentOptions( 'E2E Subject Recipient', + {}, { - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const mediatorAgentOptions = getAgentOptions( 'E2E Subject Mediator', { endpoints: ['rxjs:mediator'], - autoAcceptMediationRequests: true, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediator: new MediatorModule({ autoAcceptMediationRequests: true }), + } ) const senderAgentOptions = getAgentOptions( 'E2E Subject Sender', { endpoints: ['rxjs:sender'], - mediatorPollingInterval: 1000, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) describe('E2E Subject tests', () => { @@ -49,9 +64,9 @@ describe('E2E Subject tests', () => { let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientAgentOptions) - mediatorAgent = new Agent(mediatorAgentOptions) - senderAgent = new Agent(senderAgentOptions) + recipientAgent = new Agent(recipientAgentOptions) as AnonCredsTestsAgent + mediatorAgent = new Agent(mediatorAgentOptions) as AnonCredsTestsAgent + senderAgent = new Agent(senderAgentOptions) as AnonCredsTestsAgent }) afterEach(async () => { diff --git a/tests/e2e-ws-pickup-v2.test.ts b/tests/e2e-ws-pickup-v2.test.ts index 50fe8e0598..3ee7ffb404 100644 --- a/tests/e2e-ws-pickup-v2.test.ts +++ b/tests/e2e-ws-pickup-v2.test.ts @@ -5,17 +5,27 @@ import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { + Agent, + WsOutboundTransport, + AutoAcceptCredential, + MediatorPickupStrategy, + MediationRecipientModule, + MediatorModule, +} from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' const recipientOptions = getAgentOptions( 'E2E WS Pickup V2 Recipient ', + {}, { - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, - }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, + }), + } ) // FIXME: port numbers should not depend on availability from other test suites that use web sockets @@ -24,11 +34,13 @@ const mediatorOptions = getAgentOptions( 'E2E WS Pickup V2 Mediator', { endpoints: [`ws://localhost:${mediatorPort}`], - autoAcceptMediationRequests: true, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediator: new MediatorModule({ autoAcceptMediationRequests: true }), + } ) const senderPort = 4101 @@ -36,13 +48,16 @@ const senderOptions = getAgentOptions( 'E2E WS Pickup V2 Sender', { endpoints: [`ws://localhost:${senderPort}`], - mediatorPollingInterval: 1000, - - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV2, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) describe('E2E WS Pickup V2 tests', () => { @@ -51,9 +66,9 @@ describe('E2E WS Pickup V2 tests', () => { let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientOptions) - mediatorAgent = new Agent(mediatorOptions) - senderAgent = new Agent(senderOptions) + recipientAgent = new Agent(recipientOptions) as AnonCredsTestsAgent + mediatorAgent = new Agent(mediatorOptions) as AnonCredsTestsAgent + senderAgent = new Agent(senderOptions) as AnonCredsTestsAgent }) afterEach(async () => { diff --git a/tests/e2e-ws.test.ts b/tests/e2e-ws.test.ts index 942ea92971..b3969a8748 100644 --- a/tests/e2e-ws.test.ts +++ b/tests/e2e-ws.test.ts @@ -5,17 +5,27 @@ import { getAgentOptions } from '../packages/core/tests/helpers' import { e2eTest } from './e2e-test' -import { Agent, WsOutboundTransport, AutoAcceptCredential, MediatorPickupStrategy } from '@aries-framework/core' +import { + Agent, + WsOutboundTransport, + AutoAcceptCredential, + MediatorPickupStrategy, + MediationRecipientModule, + MediatorModule, +} from '@aries-framework/core' import { WsInboundTransport } from '@aries-framework/node' const recipientAgentOptions = getAgentOptions( 'E2E WS Recipient ', + {}, { - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) const mediatorPort = 4000 @@ -23,11 +33,13 @@ const mediatorAgentOptions = getAgentOptions( 'E2E WS Mediator', { endpoints: [`ws://localhost:${mediatorPort}`], - autoAcceptMediationRequests: true, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediator: new MediatorModule({ autoAcceptMediationRequests: true }), + } ) const senderPort = 4001 @@ -35,12 +47,16 @@ const senderAgentOptions = getAgentOptions( 'E2E WS Sender', { endpoints: [`ws://localhost:${senderPort}`], - mediatorPollingInterval: 1000, - mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, }, - getLegacyAnonCredsModules({ - autoAcceptCredentials: AutoAcceptCredential.ContentApproved, - }) + { + ...getLegacyAnonCredsModules({ + autoAcceptCredentials: AutoAcceptCredential.ContentApproved, + }), + mediationRecipient: new MediationRecipientModule({ + mediatorPollingInterval: 1000, + mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, + }), + } ) describe('E2E WS tests', () => { @@ -49,9 +65,9 @@ describe('E2E WS tests', () => { let senderAgent: AnonCredsTestsAgent beforeEach(async () => { - recipientAgent = new Agent(recipientAgentOptions) - mediatorAgent = new Agent(mediatorAgentOptions) - senderAgent = new Agent(senderAgentOptions) + recipientAgent = new Agent(recipientAgentOptions) as AnonCredsTestsAgent + mediatorAgent = new Agent(mediatorAgentOptions) as AnonCredsTestsAgent + senderAgent = new Agent(senderAgentOptions) as AnonCredsTestsAgent }) afterEach(async () => { From fa5b1a83d5573af4be48dacf9e9de531b7a36bb9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 31 Mar 2023 18:59:56 +0200 Subject: [PATCH 110/139] chore(askar-migration): move indy-sdk to dev deps (#1414) Signed-off-by: Timo Glastra --- packages/indy-sdk-to-askar-migration/package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index a84e6ed9cd..2d1faff8e0 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -24,15 +24,15 @@ "test": "jest" }, "dependencies": { - "@aries-framework/anoncreds": "^0.3.3", - "@aries-framework/askar": "^0.3.3", - "@aries-framework/core": "^0.3.3", - "@aries-framework/indy-sdk": "^0.3.3", - "@aries-framework/node": "^0.3.3", + "@aries-framework/anoncreds": "0.3.3", + "@aries-framework/askar": "0.3.3", + "@aries-framework/core": "0.3.3", + "@aries-framework/node": "0.3.3", "@hyperledger/aries-askar-shared": "^0.1.0-dev.6" }, "devDependencies": { "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", + "@aries-framework/indy-sdk": "0.3.3", "indy-sdk": "^1.16.0-dev-1655", "rimraf": "^4.4.0", "typescript": "~4.9.5" From a8439db90fd11e014b457db476e8327b6ced6358 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 1 Apr 2023 10:41:04 -0300 Subject: [PATCH 111/139] feat: add message pickup module (#1413) Signed-off-by: Ariel Gentile --- packages/core/src/agent/AgentModules.ts | 2 + packages/core/src/agent/BaseAgent.ts | 8 + .../core/src/agent/__tests__/Agent.test.ts | 3 + .../src/agent/__tests__/AgentModules.test.ts | 4 + .../MessagePickupApi.ts" | 96 +++++ .../MessagePickupApiOptions.ts" | 23 + .../MessagePickupModule.ts" | 60 +++ .../MessagePickupModuleConfig.ts" | 57 +++ .../__tests__/MessagePickupModule.test.ts" | 42 ++ .../__tests__/pickup.test.ts" | 11 +- .../modules/message-p\303\254ckup/index.ts" | 5 + .../protocol/BaseMessagePickupProtocol.ts" | 21 + .../protocol/MessagePickupProtocol.ts" | 16 + .../protocol/MessagePickupProtocolOptions.ts" | 12 + .../message-p\303\254ckup/protocol/index.ts" | 0 .../protocol/v1/V1MessagePickupProtocol.ts" | 85 ++++ .../protocol/v1/handlers/V1BatchHandler.ts" | 28 ++ .../v1/handlers/V1BatchPickupHandler.ts" | 19 + .../protocol/v1/handlers/index.ts" | 2 + .../protocol/v1/index.ts" | 2 + .../protocol/v1/messages/V1BatchMessage.ts" | 16 +- .../v1/messages/V1BatchPickupMessage.ts" | 10 +- .../protocol/v1/messages/index.ts" | 2 + .../protocol/v2/V2MessagePickupProtocol.ts" | 253 +++++++++++ .../V2MessagePickupProtocol.test.ts" | 407 ++++++++++++++++++ .../v2/handlers/V2DeliveryRequestHandler.ts" | 19 + .../v2/handlers/V2MessageDeliveryHandler.ts" | 27 ++ .../v2/handlers/V2MessagesReceivedHandler.ts" | 19 + .../protocol/v2/handlers/V2StatusHandler.ts" | 27 ++ .../v2/handlers/V2StatusRequestHandler.ts" | 19 + .../protocol/v2/handlers/index.ts" | 5 + .../protocol/v2/index.ts" | 2 + .../v2/messages/V2DeliveryRequestMessage.ts" | 16 +- .../v2/messages/V2MessageDeliveryMessage.ts" | 18 +- .../v2/messages/V2MessagesReceivedMessage.ts" | 16 +- .../protocol/v2/messages/V2StatusMessage.ts" | 18 +- .../v2/messages/V2StatusRequestMessage.ts" | 14 +- .../protocol/v2/messages/index.ts" | 5 + .../modules/routing/MediationRecipientApi.ts | 68 ++- .../core/src/modules/routing/MediatorApi.ts | 20 +- .../src/modules/routing/MediatorModule.ts | 11 - .../routing/__tests__/MediatorModule.test.ts | 5 +- packages/core/src/modules/routing/index.ts | 1 - .../src/modules/routing/protocol/index.ts | 1 - .../pickup/v1/MessagePickupService.ts | 62 --- .../pickup/v1/handlers/BatchHandler.ts | 32 -- .../pickup/v1/handlers/BatchPickupHandler.ts | 19 - .../protocol/pickup/v1/handlers/index.ts | 2 - .../routing/protocol/pickup/v1/index.ts | 2 - .../protocol/pickup/v1/messages/index.ts | 2 - .../pickup/v2/V2MessagePickupService.ts | 124 ------ .../v2/handlers/DeliveryRequestHandler.ts | 19 - .../v2/handlers/MessageDeliveryHandler.ts | 27 -- .../v2/handlers/MessagesReceivedHandler.ts | 19 - .../pickup/v2/handlers/StatusHandler.ts | 27 -- .../v2/handlers/StatusRequestHandler.ts | 19 - .../protocol/pickup/v2/handlers/index.ts | 5 - .../routing/protocol/pickup/v2/index.ts | 2 - .../protocol/pickup/v2/messages/index.ts | 5 - .../services/MediationRecipientService.ts | 106 +---- .../MediationRecipientService.test.ts | 151 +------ .../__tests__/V2MessagePickupService.test.ts | 251 ----------- 62 files changed, 1375 insertions(+), 994 deletions(-) create mode 100644 "packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/MessagePickupModule.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/MessagePickupModuleConfig.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/__tests__/MessagePickupModule.test.ts" rename packages/core/src/modules/routing/__tests__/pickup.test.ts => "packages/core/src/modules/message-p\303\254ckup/__tests__/pickup.test.ts" (95%) create mode 100644 "packages/core/src/modules/message-p\303\254ckup/index.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" rename packages/core/src/modules/routing/protocol/pickup/index.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/index.ts" (100%) create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchPickupHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/index.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v1/index.ts" rename packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" (74%) rename packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchPickupMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" (77%) create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/index.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2DeliveryRequestHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessageDeliveryHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessagesReceivedHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusRequestHandler.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/index.ts" create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/index.ts" rename packages/core/src/modules/routing/protocol/pickup/v2/messages/DeliveryRequestMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" (59%) rename packages/core/src/modules/routing/protocol/pickup/v2/messages/MessageDeliveryMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" (57%) rename packages/core/src/modules/routing/protocol/pickup/v2/messages/MessagesReceivedMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" (55%) rename packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" (79%) rename packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusRequestMessage.ts => "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" (59%) create mode 100644 "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/index.ts" delete mode 100644 packages/core/src/modules/routing/protocol/index.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/handlers/index.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/index.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v1/messages/index.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/handlers/index.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/index.ts delete mode 100644 packages/core/src/modules/routing/protocol/pickup/v2/messages/index.ts delete mode 100644 packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 7259eeb092..c4e8dec0d8 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -9,6 +9,7 @@ import { CredentialsModule } from '../modules/credentials' import { DidsModule } from '../modules/dids' import { DiscoverFeaturesModule } from '../modules/discover-features' import { GenericRecordsModule } from '../modules/generic-records' +import { MessagePickupModule } from '../modules/message-pìckup' import { OutOfBandModule } from '../modules/oob' import { ProofsModule } from '../modules/proofs' import { MediatorModule, MediationRecipientModule } from '../modules/routing' @@ -121,6 +122,7 @@ function getDefaultAgentModules() { proofs: () => new ProofsModule(), mediator: () => new MediatorModule(), mediationRecipient: () => new MediationRecipientModule(), + messagePickup: () => new MessagePickupModule(), basicMessages: () => new BasicMessagesModule(), genericRecords: () => new GenericRecordsModule(), discovery: () => new DiscoverFeaturesModule(), diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 444eba3f9d..447c22d677 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -3,6 +3,7 @@ import type { AgentApi, CustomOrDefaultApi, EmptyModuleMap, ModulesMap, WithoutD import type { TransportSession } from './TransportService' import type { Logger } from '../logger' import type { CredentialsModule } from '../modules/credentials' +import type { MessagePickupModule } from '../modules/message-pìckup' import type { ProofsModule } from '../modules/proofs' import type { DependencyManager } from '../plugins' @@ -13,6 +14,7 @@ import { CredentialsApi } from '../modules/credentials' import { DidsApi } from '../modules/dids' import { DiscoverFeaturesApi } from '../modules/discover-features' import { GenericRecordsApi } from '../modules/generic-records' +import { MessagePickupApi } from '../modules/message-pìckup/MessagePickupApi' import { OutOfBandApi } from '../modules/oob' import { ProofsApi } from '../modules/proofs' import { MediatorApi, MediationRecipientApi } from '../modules/routing' @@ -47,6 +49,7 @@ export abstract class BaseAgent public readonly mediator: MediatorApi public readonly mediationRecipient: MediationRecipientApi + public readonly messagePickup: CustomOrDefaultApi public readonly basicMessages: BasicMessagesApi public readonly genericRecords: GenericRecordsApi public readonly discovery: DiscoverFeaturesApi @@ -90,6 +93,10 @@ export abstract class BaseAgent this.mediator = this.dependencyManager.resolve(MediatorApi) this.mediationRecipient = this.dependencyManager.resolve(MediationRecipientApi) + this.messagePickup = this.dependencyManager.resolve(MessagePickupApi) as CustomOrDefaultApi< + AgentModules['messagePickup'], + MessagePickupModule + > this.basicMessages = this.dependencyManager.resolve(BasicMessagesApi) this.genericRecords = this.dependencyManager.resolve(GenericRecordsApi) this.discovery = this.dependencyManager.resolve(DiscoverFeaturesApi) @@ -103,6 +110,7 @@ export abstract class BaseAgent { expect(container.resolve(MediatorApi)).toBeInstanceOf(MediatorApi) expect(container.resolve(MediationRecipientApi)).toBeInstanceOf(MediationRecipientApi) + expect(container.resolve(MessagePickupApi)).toBeInstanceOf(MessagePickupApi) expect(container.resolve(MediationRepository)).toBeInstanceOf(MediationRepository) expect(container.resolve(MediatorService)).toBeInstanceOf(MediatorService) expect(container.resolve(MediationRecipientService)).toBeInstanceOf(MediationRecipientService) @@ -208,6 +210,7 @@ describe('Agent', () => { expect(container.resolve(MediatorApi)).toBe(container.resolve(MediatorApi)) expect(container.resolve(MediationRecipientApi)).toBe(container.resolve(MediationRecipientApi)) + expect(container.resolve(MessagePickupApi)).toBe(container.resolve(MessagePickupApi)) expect(container.resolve(MediationRepository)).toBe(container.resolve(MediationRepository)) expect(container.resolve(MediatorService)).toBe(container.resolve(MediatorService)) expect(container.resolve(MediationRecipientService)).toBe(container.resolve(MediationRecipientService)) diff --git a/packages/core/src/agent/__tests__/AgentModules.test.ts b/packages/core/src/agent/__tests__/AgentModules.test.ts index 25466501d7..6a7833fbd0 100644 --- a/packages/core/src/agent/__tests__/AgentModules.test.ts +++ b/packages/core/src/agent/__tests__/AgentModules.test.ts @@ -7,6 +7,7 @@ import { CredentialsModule } from '../../modules/credentials' import { DidsModule } from '../../modules/dids' import { DiscoverFeaturesModule } from '../../modules/discover-features' import { GenericRecordsModule } from '../../modules/generic-records' +import { MessagePickupModule } from '../../modules/message-pìckup' import { OutOfBandModule } from '../../modules/oob' import { ProofsModule } from '../../modules/proofs' import { MediatorModule, MediationRecipientModule } from '../../modules/routing' @@ -59,6 +60,7 @@ describe('AgentModules', () => { proofs: expect.any(ProofsModule), mediator: expect.any(MediatorModule), mediationRecipient: expect.any(MediationRecipientModule), + messagePickup: expect.any(MessagePickupModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), @@ -82,6 +84,7 @@ describe('AgentModules', () => { proofs: expect.any(ProofsModule), mediator: expect.any(MediatorModule), mediationRecipient: expect.any(MediationRecipientModule), + messagePickup: expect.any(MessagePickupModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), @@ -108,6 +111,7 @@ describe('AgentModules', () => { proofs: expect.any(ProofsModule), mediator: expect.any(MediatorModule), mediationRecipient: expect.any(MediationRecipientModule), + messagePickup: expect.any(MessagePickupModule), basicMessages: expect.any(BasicMessagesModule), genericRecords: expect.any(GenericRecordsModule), discovery: expect.any(DiscoverFeaturesModule), diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" new file mode 100644 index 0000000000..653fafd7d7 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupApi.ts" @@ -0,0 +1,96 @@ +import type { + PickupMessagesOptions, + PickupMessagesReturnType, + QueueMessageOptions, + QueueMessageReturnType, +} from './MessagePickupApiOptions' +import type { V1MessagePickupProtocol, V2MessagePickupProtocol } from './protocol' +import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' +import type { MessageRepository } from '../../storage/MessageRepository' + +import { AgentContext } from '../../agent' +import { MessageSender } from '../../agent/MessageSender' +import { OutboundMessageContext } from '../../agent/models' +import { InjectionSymbols } from '../../constants' +import { AriesFrameworkError } from '../../error' +import { injectable } from '../../plugins' +import { ConnectionService } from '../connections/services' + +import { MessagePickupModuleConfig } from './MessagePickupModuleConfig' + +export interface MessagePickupApi { + queueMessage(options: QueueMessageOptions): Promise + pickupMessages(options: PickupMessagesOptions): Promise +} + +@injectable() +export class MessagePickupApi + implements MessagePickupApi +{ + public config: MessagePickupModuleConfig + + private messageSender: MessageSender + private agentContext: AgentContext + private connectionService: ConnectionService + + public constructor( + messageSender: MessageSender, + agentContext: AgentContext, + connectionService: ConnectionService, + config: MessagePickupModuleConfig + ) { + this.messageSender = messageSender + this.connectionService = connectionService + this.agentContext = agentContext + this.config = config + } + + private getProtocol(protocolVersion: MPP): MessagePickupProtocol { + const protocol = this.config.protocols.find((protocol) => protocol.version === protocolVersion) + + if (!protocol) { + throw new AriesFrameworkError(`No message pickup protocol registered for protocol version ${protocolVersion}`) + } + + return protocol + } + + /** + * Add an encrypted message to the message pickup queue + * + * @param options: connectionId associated to the message and the encrypted message itself + */ + public async queueMessage(options: QueueMessageOptions): Promise { + const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) + + const messageRepository = this.agentContext.dependencyManager.resolve( + InjectionSymbols.MessageRepository + ) + + await messageRepository.add(connectionRecord.id, options.message) + } + + /** + * Pickup queued messages from a message holder. It attempts to retrieve all current messages from the + * queue, receiving up to `batchSize` messages per batch retrieval. + * + * @param options connectionId, protocol version to use and batch size + */ + public async pickupMessages(options: PickupMessagesOptions): Promise { + const connectionRecord = await this.connectionService.getById(this.agentContext, options.connectionId) + + const protocol = this.getProtocol(options.protocolVersion) + const { message } = await protocol.pickupMessages(this.agentContext, { + connectionRecord, + batchSize: options.batchSize, + recipientKey: options.recipientKey, + }) + + await this.messageSender.sendMessage( + new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection: connectionRecord, + }) + ) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" new file mode 100644 index 0000000000..1f8d54e264 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" @@ -0,0 +1,23 @@ +import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' +import type { EncryptedMessage } from '../../types' + +/** + * Get the supported protocol versions based on the provided discover features services. + */ +export type MessagePickupProtocolVersionType = MPPs[number]['version'] + +export interface QueueMessageOptions { + connectionId: string + message: EncryptedMessage +} + +export interface PickupMessagesOptions { + connectionId: string + protocolVersion: MessagePickupProtocolVersionType + recipientKey?: string + batchSize?: number +} + +export type QueueMessageReturnType = void + +export type PickupMessagesReturnType = void diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupModule.ts" "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupModule.ts" new file mode 100644 index 0000000000..5cf4540625 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupModule.ts" @@ -0,0 +1,60 @@ +import type { MessagePickupModuleConfigOptions } from './MessagePickupModuleConfig' +import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' +import type { FeatureRegistry } from '../../agent/FeatureRegistry' +import type { ApiModule, DependencyManager } from '../../plugins' +import type { Optional } from '../../utils' +import type { Constructor } from '../../utils/mixins' + +import { InjectionSymbols } from '../../constants' + +import { MessagePickupApi } from './MessagePickupApi' +import { MessagePickupModuleConfig } from './MessagePickupModuleConfig' +import { V1MessagePickupProtocol, V2MessagePickupProtocol } from './protocol' + +/** + * Default protocols that will be registered if the `protocols` property is not configured. + */ +export type DefaultMessagePickupProtocols = [V1MessagePickupProtocol, V2MessagePickupProtocol] + +// MessagePickupModuleOptions makes the protocols property optional from the config, as it will set it when not provided. +export type MessagePickupModuleOptions = Optional< + MessagePickupModuleConfigOptions, + 'protocols' +> + +export class MessagePickupModule + implements ApiModule +{ + public readonly config: MessagePickupModuleConfig + + // Infer Api type from the config + public readonly api: Constructor> = MessagePickupApi + + public constructor(config?: MessagePickupModuleOptions) { + this.config = new MessagePickupModuleConfig({ + ...config, + protocols: config?.protocols ?? [new V1MessagePickupProtocol(), new V2MessagePickupProtocol()], + }) as MessagePickupModuleConfig + } + + /** + * Registers the dependencies of the question answer module on the dependency manager. + */ + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) { + // Api + dependencyManager.registerContextScoped(MessagePickupApi) + + // Config + dependencyManager.registerInstance(MessagePickupModuleConfig, this.config) + + // Message repository + if (this.config.messageRepository) { + dependencyManager.registerInstance(InjectionSymbols.MessageRepository, this.config.messageRepository) + } + + // Protocol needs to register feature registry items and handlers + for (const protocol of this.config.protocols) { + protocol.register(dependencyManager, featureRegistry) + } + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupModuleConfig.ts" "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupModuleConfig.ts" new file mode 100644 index 0000000000..d755c082e3 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupModuleConfig.ts" @@ -0,0 +1,57 @@ +import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' +import type { MessageRepository } from '../../storage/MessageRepository' + +/** + * MessagePickupModuleConfigOptions defines the interface for the options of the MessagePickupModuleConfig class. + * This can contain optional parameters that have default values in the config class itself. + */ +export interface MessagePickupModuleConfigOptions { + /** + * Maximum number of messages to retrieve in a single batch message pickup + * + * @default 10 + */ + maximumBatchSize?: number + + /** + * Message pickup protocols to make available to the message pickup module. Only one protocol should be registered for each + * protocol version. + * + * When not provided, V1MessagePickupProtocol and V2MessagePickupProtocol` are registered by default. + * + * @default + * ``` + * [V1MessagePickupProtocol, V2MessagePickupProtocol] + * ``` + */ + protocols: MessagePickupProtocols + + /** + * Allows to specify a custom pickup message queue. It defaults to an in-memory repository + * + */ + messageRepository?: MessageRepository +} + +export class MessagePickupModuleConfig { + private options: MessagePickupModuleConfigOptions + + public constructor(options: MessagePickupModuleConfigOptions) { + this.options = options + } + + /** See {@link MessagePickupModuleConfig.maximumBatchSize} */ + public get maximumBatchSize() { + return this.options.maximumBatchSize ?? 10 + } + + /** See {@link MessagePickupModuleConfig.protocols} */ + public get protocols() { + return this.options.protocols + } + + /** See {@link MessagePickupModuleConfig.protocols} */ + public get messageRepository() { + return this.options.messageRepository + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/__tests__/MessagePickupModule.test.ts" "b/packages/core/src/modules/message-p\303\254ckup/__tests__/MessagePickupModule.test.ts" new file mode 100644 index 0000000000..141d8f81af --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/__tests__/MessagePickupModule.test.ts" @@ -0,0 +1,42 @@ +import { FeatureRegistry } from '../../../agent/FeatureRegistry' +import { Protocol } from '../../../agent/models' +import { DependencyManager } from '../../../plugins/DependencyManager' +import { MessagePickupApi } from '../MessagePickupApi' +import { MessagePickupModule } from '../MessagePickupModule' +import { MessagePickupModuleConfig } from '../MessagePickupModuleConfig' + +jest.mock('../../../plugins/DependencyManager') +const DependencyManagerMock = DependencyManager as jest.Mock + +jest.mock('../../../agent/FeatureRegistry') +const FeatureRegistryMock = FeatureRegistry as jest.Mock + +const dependencyManager = new DependencyManagerMock() +const featureRegistry = new FeatureRegistryMock() + +describe('MessagePickupModule', () => { + test('registers dependencies on the dependency manager', () => { + const module = new MessagePickupModule() + module.register(dependencyManager, featureRegistry) + + expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(MessagePickupApi) + + expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(MessagePickupModuleConfig, module.config) + + expect(featureRegistry.register).toHaveBeenCalledTimes(2) + expect(featureRegistry.register).toHaveBeenCalledWith( + new Protocol({ + id: 'https://didcomm.org/messagepickup/1.0', + roles: ['message_holder', 'recipient', 'batch_sender', 'batch_recipient'], + }) + ) + expect(featureRegistry.register).toHaveBeenCalledWith( + new Protocol({ + id: 'https://didcomm.org/messagepickup/2.0', + roles: ['mediator', 'recipient'], + }) + ) + }) +}) diff --git a/packages/core/src/modules/routing/__tests__/pickup.test.ts "b/packages/core/src/modules/message-p\303\254ckup/__tests__/pickup.test.ts" similarity index 95% rename from packages/core/src/modules/routing/__tests__/pickup.test.ts rename to "packages/core/src/modules/message-p\303\254ckup/__tests__/pickup.test.ts" index 2879dacca8..6285f2828d 100644 --- a/packages/core/src/modules/routing/__tests__/pickup.test.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/__tests__/pickup.test.ts" @@ -9,7 +9,6 @@ import { getIndySdkModules } from '../../../../../indy-sdk/tests/setupIndySdkMod import { getAgentOptions, waitForBasicMessage, waitForTrustPingReceivedEvent } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' import { HandshakeProtocol } from '../../connections' -import { MediatorPickupStrategy } from '../MediatorPickupStrategy' const recipientOptions = getAgentOptions('Mediation: Recipient Pickup', {}, getIndySdkModules()) const mediatorOptions = getAgentOptions( @@ -81,7 +80,10 @@ describe('E2E Pick Up protocol', () => { const message = 'hello pickup V1' await mediatorAgent.basicMessages.sendMessage(mediatorRecipientConnection.id, message) - await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection, MediatorPickupStrategy.PickUpV1) + await recipientAgent.messagePickup.pickupMessages({ + connectionId: recipientMediatorConnection.id, + protocolVersion: 'v1', + }) const basicMessage = await waitForBasicMessage(recipientAgent, { content: message, @@ -142,7 +144,10 @@ describe('E2E Pick Up protocol', () => { content: message, }) const trustPingPromise = waitForTrustPingReceivedEvent(mediatorAgent, {}) - await recipientAgent.mediationRecipient.pickupMessages(recipientMediatorConnection, MediatorPickupStrategy.PickUpV2) + await recipientAgent.messagePickup.pickupMessages({ + connectionId: recipientMediatorConnection.id, + protocolVersion: 'v2', + }) const basicMessage = await basicMessagePromise expect(basicMessage.content).toBe(message) diff --git "a/packages/core/src/modules/message-p\303\254ckup/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/index.ts" new file mode 100644 index 0000000000..b4745b6037 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/index.ts" @@ -0,0 +1,5 @@ +export * from './MessagePickupApi' +export * from './MessagePickupApiOptions' +export * from './MessagePickupModule' +export * from './MessagePickupModuleConfig' +export * from './protocol' diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" new file mode 100644 index 0000000000..ebbd6fde39 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" @@ -0,0 +1,21 @@ +import type { MessagePickupProtocol } from './MessagePickupProtocol' +import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from './MessagePickupProtocolOptions' +import type { AgentContext } from '../../../agent' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../agent/FeatureRegistry' +import type { DependencyManager } from '../../../plugins' + +/** + * Base implementation of the MessagePickupProtocol that can be used as a foundation for implementing + * the MessagePickupProtocol interface. + */ +export abstract class BaseMessagePickupProtocol implements MessagePickupProtocol { + public abstract readonly version: string + + public abstract pickupMessages( + agentContext: AgentContext, + options: PickupMessagesProtocolOptions + ): Promise> + + public abstract register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" new file mode 100644 index 0000000000..9acdcf5e4d --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" @@ -0,0 +1,16 @@ +import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from './MessagePickupProtocolOptions' +import type { AgentContext } from '../../../agent' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../agent/FeatureRegistry' +import type { DependencyManager } from '../../../plugins' + +export interface MessagePickupProtocol { + readonly version: string + + pickupMessages( + agentContext: AgentContext, + options: PickupMessagesProtocolOptions + ): Promise> + + register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" new file mode 100644 index 0000000000..9f3f252c6a --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" @@ -0,0 +1,12 @@ +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { ConnectionRecord } from '../../connections' + +export interface PickupMessagesProtocolOptions { + connectionRecord: ConnectionRecord + recipientKey?: string + batchSize?: number +} + +export type PickupMessagesProtocolReturnType = { + message: MessageType +} diff --git a/packages/core/src/modules/routing/protocol/pickup/index.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/index.ts" similarity index 100% rename from packages/core/src/modules/routing/protocol/pickup/index.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/index.ts" diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" new file mode 100644 index 0000000000..581d0d31a7 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" @@ -0,0 +1,85 @@ +import type { AgentContext } from '../../../../agent' +import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../../plugins' +import type { MessageRepository } from '../../../../storage/MessageRepository' +import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from '../MessagePickupProtocolOptions' + +import { OutboundMessageContext, Protocol } from '../../../../agent/models' +import { InjectionSymbols } from '../../../../constants' +import { injectable } from '../../../../plugins' +import { MessagePickupModuleConfig } from '../../MessagePickupModuleConfig' +import { BaseMessagePickupProtocol } from '../BaseMessagePickupProtocol' + +import { V1BatchHandler, V1BatchPickupHandler } from './handlers' +import { V1BatchMessage, BatchMessageMessage, V1BatchPickupMessage } from './messages' + +@injectable() +export class V1MessagePickupProtocol extends BaseMessagePickupProtocol { + public constructor() { + super() + } + + /** + * The version of the message pickup protocol this class supports + */ + public readonly version = 'v1' as const + + /** + * Registers the protocol implementation (handlers, feature registry) on the agent. + */ + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void { + dependencyManager.registerMessageHandlers([new V1BatchPickupHandler(this), new V1BatchHandler()]) + + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/messagepickup/1.0', + roles: ['message_holder', 'recipient', 'batch_sender', 'batch_recipient'], + }) + ) + } + + public async pickupMessages( + agentContext: AgentContext, + options: PickupMessagesProtocolOptions + ): Promise> { + const { connectionRecord, batchSize } = options + connectionRecord.assertReady() + + const config = agentContext.dependencyManager.resolve(MessagePickupModuleConfig) + const message = new V1BatchPickupMessage({ + batchSize: batchSize ?? config.maximumBatchSize, + }) + + return { message } + } + + public async processBatchPickup(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + const { message } = messageContext + + const messageRepository = messageContext.agentContext.dependencyManager.resolve( + InjectionSymbols.MessageRepository + ) + + const messages = await messageRepository.takeFromQueue(connection.id, message.batchSize) + + // TODO: each message should be stored with an id. to be able to conform to the id property + // of batch message + const batchMessages = messages.map( + (msg) => + new BatchMessageMessage({ + message: msg, + }) + ) + + const batchMessage = new V1BatchMessage({ + messages: batchMessages, + }) + + return new OutboundMessageContext(batchMessage, { agentContext: messageContext.agentContext, connection }) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchHandler.ts" new file mode 100644 index 0000000000..071711f9e3 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchHandler.ts" @@ -0,0 +1,28 @@ +import type { AgentMessageReceivedEvent } from '../../../../../agent/Events' +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' + +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { AgentEventTypes } from '../../../../../agent/Events' +import { V1BatchMessage } from '../messages' + +export class V1BatchHandler implements MessageHandler { + public supportedMessages = [V1BatchMessage] + + public async handle(messageContext: MessageHandlerInboundMessage) { + const { message } = messageContext + const eventEmitter = messageContext.agentContext.dependencyManager.resolve(EventEmitter) + + messageContext.assertReadyConnection() + + const forwardedMessages = message.messages + forwardedMessages.forEach((message) => { + eventEmitter.emit(messageContext.agentContext, { + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: message.message, + contextCorrelationId: messageContext.agentContext.contextCorrelationId, + }, + }) + }) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchPickupHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchPickupHandler.ts" new file mode 100644 index 0000000000..d9eee7c4d9 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/V1BatchPickupHandler.ts" @@ -0,0 +1,19 @@ +import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../agent/MessageHandler' +import type { V1MessagePickupProtocol } from '../V1MessagePickupProtocol' + +import { V1BatchPickupMessage } from '../messages' + +export class V1BatchPickupHandler implements MessageHandler { + private messagePickupService: V1MessagePickupProtocol + public supportedMessages = [V1BatchPickupMessage] + + public constructor(messagePickupService: V1MessagePickupProtocol) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: MessageHandlerInboundMessage) { + messageContext.assertReadyConnection() + + return this.messagePickupService.processBatchPickup(messageContext) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/index.ts" new file mode 100644 index 0000000000..b8aef88046 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/handlers/index.ts" @@ -0,0 +1,2 @@ +export * from './V1BatchHandler' +export * from './V1BatchPickupHandler' diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/index.ts" new file mode 100644 index 0000000000..abf43d6b2a --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/index.ts" @@ -0,0 +1,2 @@ +export * from './V1MessagePickupProtocol' +export * from './messages' diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" similarity index 74% rename from packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" index fae8f4d3d6..91e0b5debc 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" @@ -1,11 +1,11 @@ import { Type, Expose } from 'class-transformer' import { Matches, IsArray, ValidateNested, IsObject, IsInstance } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { MessageIdRegExp } from '../../../../../../agent/BaseMessage' -import { EncryptedMessage } from '../../../../../../types' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' -import { uuid } from '../../../../../../utils/uuid' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { MessageIdRegExp } from '../../../../../agent/BaseMessage' +import { EncryptedMessage } from '../../../../../types' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { uuid } from '../../../../../utils/uuid' export class BatchMessageMessage { public constructor(options: { id?: string; message: EncryptedMessage }) { @@ -32,7 +32,7 @@ export interface BatchMessageOptions { * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0212-pickup/README.md#batch */ -export class BatchMessage extends AgentMessage { +export class V1BatchMessage extends AgentMessage { public constructor(options: BatchMessageOptions) { super() @@ -42,8 +42,8 @@ export class BatchMessage extends AgentMessage { } } - @IsValidMessageType(BatchMessage.type) - public readonly type = BatchMessage.type.messageTypeUri + @IsValidMessageType(V1BatchMessage.type) + public readonly type = V1BatchMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/1.0/batch') @Type(() => BatchMessageMessage) diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchPickupMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" similarity index 77% rename from packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchPickupMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" index 4756bc4416..aa5e7ff646 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v1/messages/BatchPickupMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" @@ -1,8 +1,8 @@ import { Expose } from 'class-transformer' import { IsInt } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export interface BatchPickupMessageOptions { id?: string @@ -14,7 +14,7 @@ export interface BatchPickupMessageOptions { * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0212-pickup/README.md#batch-pickup */ -export class BatchPickupMessage extends AgentMessage { +export class V1BatchPickupMessage extends AgentMessage { /** * Create new BatchPickupMessage instance. * @@ -29,8 +29,8 @@ export class BatchPickupMessage extends AgentMessage { } } - @IsValidMessageType(BatchPickupMessage.type) - public readonly type = BatchPickupMessage.type.messageTypeUri + @IsValidMessageType(V1BatchPickupMessage.type) + public readonly type = V1BatchPickupMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/1.0/batch-pickup') @IsInt() diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/index.ts" new file mode 100644 index 0000000000..19c16cf1d8 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/index.ts" @@ -0,0 +1,2 @@ +export * from './V1BatchMessage' +export * from './V1BatchPickupMessage' diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" new file mode 100644 index 0000000000..b9dc22ae7e --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" @@ -0,0 +1,253 @@ +import type { AgentContext } from '../../../../agent' +import type { AgentMessage } from '../../../../agent/AgentMessage' +import type { AgentMessageReceivedEvent } from '../../../../agent/Events' +import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { DependencyManager } from '../../../../plugins' +import type { MessageRepository } from '../../../../storage/MessageRepository' +import type { EncryptedMessage } from '../../../../types' +import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from '../MessagePickupProtocolOptions' + +import { EventEmitter } from '../../../../agent/EventEmitter' +import { AgentEventTypes } from '../../../../agent/Events' +import { MessageSender } from '../../../../agent/MessageSender' +import { OutboundMessageContext, Protocol } from '../../../../agent/models' +import { InjectionSymbols } from '../../../../constants' +import { Attachment } from '../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../error' +import { injectable } from '../../../../plugins' +import { ConnectionService } from '../../../connections' +import { ProblemReportError } from '../../../problem-reports' +import { RoutingProblemReportReason } from '../../../routing/error' +import { MessagePickupModuleConfig } from '../../MessagePickupModuleConfig' +import { BaseMessagePickupProtocol } from '../BaseMessagePickupProtocol' + +import { + V2DeliveryRequestHandler, + V2MessageDeliveryHandler, + V2MessagesReceivedHandler, + V2StatusHandler, + V2StatusRequestHandler, +} from './handlers' +import { + V2MessageDeliveryMessage, + V2StatusMessage, + V2DeliveryRequestMessage, + V2MessagesReceivedMessage, + V2StatusRequestMessage, +} from './messages' + +@injectable() +export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { + public constructor() { + super() + } + + /** + * The version of the message pickup protocol this class supports + */ + public readonly version = 'v2' as const + + /** + * Registers the protocol implementation (handlers, feature registry) on the agent. + */ + public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void { + dependencyManager.registerMessageHandlers([ + new V2StatusRequestHandler(this), + new V2DeliveryRequestHandler(this), + new V2MessagesReceivedHandler(this), + new V2StatusHandler(this), + new V2MessageDeliveryHandler(this), + ]) + + featureRegistry.register( + new Protocol({ + id: 'https://didcomm.org/messagepickup/2.0', + roles: ['mediator', 'recipient'], + }) + ) + } + + public async pickupMessages( + agentContext: AgentContext, + options: PickupMessagesProtocolOptions + ): Promise> { + const { connectionRecord, recipientKey } = options + connectionRecord.assertReady() + + const message = new V2StatusRequestMessage({ + recipientKey, + }) + + return { message } + } + + public async processStatusRequest(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + const messageRepository = messageContext.agentContext.dependencyManager.resolve( + InjectionSymbols.MessageRepository + ) + + if (messageContext.message.recipientKey) { + throw new AriesFrameworkError('recipient_key parameter not supported') + } + + const statusMessage = new V2StatusMessage({ + threadId: messageContext.message.threadId, + messageCount: await messageRepository.getAvailableMessageCount(connection.id), + }) + + return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) + } + + public async processDeliveryRequest(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + if (messageContext.message.recipientKey) { + throw new AriesFrameworkError('recipient_key parameter not supported') + } + + const { message } = messageContext + + const messageRepository = messageContext.agentContext.dependencyManager.resolve( + InjectionSymbols.MessageRepository + ) + + // Get available messages from queue, but don't delete them + const messages = await messageRepository.takeFromQueue(connection.id, message.limit, true) + + // TODO: each message should be stored with an id. to be able to conform to the id property + // of delivery message + const attachments = messages.map( + (msg) => + new Attachment({ + data: { + json: msg, + }, + }) + ) + + const outboundMessageContext = + messages.length > 0 + ? new V2MessageDeliveryMessage({ + threadId: messageContext.message.threadId, + attachments, + }) + : new V2StatusMessage({ + threadId: messageContext.message.threadId, + messageCount: 0, + }) + + return new OutboundMessageContext(outboundMessageContext, { agentContext: messageContext.agentContext, connection }) + } + + public async processMessagesReceived(messageContext: InboundMessageContext) { + // Assert ready connection + const connection = messageContext.assertReadyConnection() + + const { message } = messageContext + + const messageRepository = messageContext.agentContext.dependencyManager.resolve( + InjectionSymbols.MessageRepository + ) + + // TODO: Add Queued Message ID + await messageRepository.takeFromQueue( + connection.id, + message.messageIdList ? message.messageIdList.length : undefined + ) + + const statusMessage = new V2StatusMessage({ + threadId: messageContext.message.threadId, + messageCount: await messageRepository.getAvailableMessageCount(connection.id), + }) + + return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) + } + + public async processStatus(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + const { message: statusMessage } = messageContext + const { messageCount, recipientKey } = statusMessage + + const connectionService = messageContext.agentContext.dependencyManager.resolve(ConnectionService) + const messageSender = messageContext.agentContext.dependencyManager.resolve(MessageSender) + const messagePickupModuleConfig = messageContext.agentContext.dependencyManager.resolve(MessagePickupModuleConfig) + + //No messages to be sent + if (messageCount === 0) { + const { message, connectionRecord } = await connectionService.createTrustPing( + messageContext.agentContext, + connection, + { + responseRequested: false, + } + ) + + // FIXME: check where this flow fits, as it seems very particular for the AFJ-ACA-Py combination + const websocketSchemes = ['ws', 'wss'] + + await messageSender.sendMessage( + new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: connectionRecord, + }), + { + transportPriority: { + schemes: websocketSchemes, + restrictive: true, + // TODO: add keepAlive: true to enforce through the public api + // we need to keep the socket alive. It already works this way, but would + // be good to make more explicit from the public facing API. + // This would also make it easier to change the internal API later on. + // keepAlive: true, + }, + } + ) + + return null + } + const { maximumBatchSize: maximumMessagePickup } = messagePickupModuleConfig + const limit = messageCount < maximumMessagePickup ? messageCount : maximumMessagePickup + + const deliveryRequestMessage = new V2DeliveryRequestMessage({ + limit, + recipientKey, + }) + + return deliveryRequestMessage + } + + public async processDelivery(messageContext: InboundMessageContext) { + messageContext.assertReadyConnection() + + const { appendedAttachments } = messageContext.message + + const eventEmitter = messageContext.agentContext.dependencyManager.resolve(EventEmitter) + + if (!appendedAttachments) + throw new ProblemReportError('Error processing attachments', { + problemCode: RoutingProblemReportReason.ErrorProcessingAttachments, + }) + + const ids: string[] = [] + for (const attachment of appendedAttachments) { + ids.push(attachment.id) + + eventEmitter.emit(messageContext.agentContext, { + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: attachment.getDataAsJson(), + contextCorrelationId: messageContext.agentContext.contextCorrelationId, + }, + }) + } + + return new V2MessagesReceivedMessage({ + messageIdList: ids, + }) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" new file mode 100644 index 0000000000..50476217f9 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" @@ -0,0 +1,407 @@ +import type { EncryptedMessage } from '../../../../../types' + +import { getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { AgentEventTypes } from '../../../../../agent/Events' +import { MessageSender } from '../../../../../agent/MessageSender' +import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import { InjectionSymbols } from '../../../../../constants' +import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { AriesFrameworkError } from '../../../../../error' +import { InMemoryMessageRepository } from '../../../../../storage/InMemoryMessageRepository' +import { uuid } from '../../../../../utils/uuid' +import { DidExchangeState, TrustPingMessage } from '../../../../connections' +import { ConnectionService } from '../../../../connections/services/ConnectionService' +import { MessagePickupModuleConfig } from '../../../MessagePickupModuleConfig' +import { V1MessagePickupProtocol } from '../../v1' +import { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' +import { + V2DeliveryRequestMessage, + V2MessageDeliveryMessage, + V2MessagesReceivedMessage, + V2StatusMessage, + V2StatusRequestMessage, +} from '../messages' + +const mockConnection = getMockConnection({ + state: DidExchangeState.Completed, +}) + +// Mock classes +jest.mock('../../../../../storage/InMemoryMessageRepository') +jest.mock('../../../../../agent/EventEmitter') +jest.mock('../../../../../agent/MessageSender') +jest.mock('../../../../connections/services/ConnectionService') + +// Mock typed object +const InMessageRepositoryMock = InMemoryMessageRepository as jest.Mock +const EventEmitterMock = EventEmitter as jest.Mock +const MessageSenderMock = MessageSender as jest.Mock +const ConnectionServiceMock = ConnectionService as jest.Mock + +const messagePickupModuleConfig = new MessagePickupModuleConfig({ + maximumBatchSize: 10, + protocols: [new V1MessagePickupProtocol(), new V2MessagePickupProtocol()], +}) +const messageSender = new MessageSenderMock() +const eventEmitter = new EventEmitterMock() +const connectionService = new ConnectionServiceMock() +const messageRepository = new InMessageRepositoryMock() + +const agentContext = getAgentContext({ + registerInstances: [ + [InjectionSymbols.MessageRepository, messageRepository], + [EventEmitter, eventEmitter], + [MessageSender, messageSender], + [ConnectionService, connectionService], + [MessagePickupModuleConfig, messagePickupModuleConfig], + ], +}) + +const encryptedMessage: EncryptedMessage = { + protected: 'base64url', + iv: 'base64url', + ciphertext: 'base64url', + tag: 'base64url', +} +const queuedMessages = [encryptedMessage, encryptedMessage, encryptedMessage] + +describe('V2MessagePickupService', () => { + let pickupProtocol: V2MessagePickupProtocol + + beforeEach(async () => { + pickupProtocol = new V2MessagePickupProtocol() + }) + + describe('processStatusRequest', () => { + test('no available messages in queue', async () => { + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(0) + + const statusRequest = new V2StatusRequestMessage({}) + + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processStatusRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toEqual( + new V2StatusMessage({ + id: message.id, + threadId: statusRequest.threadId, + messageCount: 0, + }) + ) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + }) + + test('multiple messages in queue', async () => { + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(5) + const statusRequest = new V2StatusRequestMessage({}) + + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processStatusRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toEqual( + new V2StatusMessage({ + id: message.id, + threadId: statusRequest.threadId, + messageCount: 5, + }) + ) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + }) + + test('status request specifying recipient key', async () => { + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(10) + + const statusRequest = new V2StatusRequestMessage({ + recipientKey: 'recipientKey', + }) + + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) + + await expect(pickupProtocol.processStatusRequest(messageContext)).rejects.toThrowError( + 'recipient_key parameter not supported' + ) + }) + }) + + describe('processDeliveryRequest', () => { + test('no available messages in queue', async () => { + mockFunction(messageRepository.takeFromQueue).mockReturnValue([]) + + const deliveryRequest = new V2DeliveryRequestMessage({ limit: 10 }) + + const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processDeliveryRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toEqual( + new V2StatusMessage({ + id: message.id, + threadId: deliveryRequest.threadId, + messageCount: 0, + }) + ) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) + }) + + test('less messages in queue than limit', async () => { + mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages) + + const deliveryRequest = new V2DeliveryRequestMessage({ limit: 10 }) + + const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processDeliveryRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toBeInstanceOf(V2MessageDeliveryMessage) + expect(message.threadId).toEqual(deliveryRequest.threadId) + expect(message.appendedAttachments?.length).toEqual(3) + expect(message.appendedAttachments).toEqual( + expect.arrayContaining( + queuedMessages.map((msg) => + expect.objectContaining({ + data: { + json: msg, + }, + }) + ) + ) + ) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) + }) + + test('more messages in queue than limit', async () => { + mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages.slice(0, 2)) + + const deliveryRequest = new V2DeliveryRequestMessage({ limit: 2 }) + + const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processDeliveryRequest(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toBeInstanceOf(V2MessageDeliveryMessage) + expect(message.threadId).toEqual(deliveryRequest.threadId) + expect(message.appendedAttachments?.length).toEqual(2) + expect(message.appendedAttachments).toEqual( + expect.arrayContaining( + queuedMessages.slice(0, 2).map((msg) => + expect.objectContaining({ + data: { + json: msg, + }, + }) + ) + ) + ) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2, true) + }) + + test('delivery request specifying recipient key', async () => { + mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages) + + const statusRequest = new V2DeliveryRequestMessage({ + limit: 10, + recipientKey: 'recipientKey', + }) + + const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) + + await expect(pickupProtocol.processStatusRequest(messageContext)).rejects.toThrowError( + 'recipient_key parameter not supported' + ) + }) + }) + + describe('processMessagesReceived', () => { + test('messages received partially', async () => { + mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages) + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(4) + + const messagesReceived = new V2MessagesReceivedMessage({ + messageIdList: ['1', '2'], + }) + + const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processMessagesReceived(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toEqual( + new V2StatusMessage({ + id: message.id, + threadId: messagesReceived.threadId, + messageCount: 4, + }) + ) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) + }) + + test('all messages have been received', async () => { + mockFunction(messageRepository.takeFromQueue).mockReturnValue(queuedMessages) + mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(0) + + const messagesReceived = new V2MessagesReceivedMessage({ + messageIdList: ['1', '2'], + }) + + const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) + + const { connection, message } = await pickupProtocol.processMessagesReceived(messageContext) + + expect(connection).toEqual(mockConnection) + expect(message).toEqual( + new V2StatusMessage({ + id: message.id, + threadId: messagesReceived.threadId, + messageCount: 0, + }) + ) + + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) + }) + }) + + describe('pickupMessages', () => { + it('creates a status request message', async () => { + const { message: statusRequestMessage } = await pickupProtocol.pickupMessages(agentContext, { + connectionRecord: mockConnection, + recipientKey: 'a-key', + }) + + expect(statusRequestMessage).toMatchObject({ + id: expect.any(String), + recipientKey: 'a-key', + }) + }) + }) + + describe('processStatus', () => { + it('if status request has a message count of zero returns nothing', async () => { + const status = new V2StatusMessage({ + threadId: uuid(), + messageCount: 0, + }) + + mockFunction(connectionService.createTrustPing).mockResolvedValueOnce({ + message: new TrustPingMessage({}), + connectionRecord: mockConnection, + }) + + const messageContext = new InboundMessageContext(status, { connection: mockConnection, agentContext }) + const deliveryRequestMessage = await pickupProtocol.processStatus(messageContext) + expect(deliveryRequestMessage).toBeNull() + }) + + it('if it has a message count greater than zero return a valid delivery request', async () => { + const status = new V2StatusMessage({ + threadId: uuid(), + messageCount: 1, + }) + const messageContext = new InboundMessageContext(status, { connection: mockConnection, agentContext }) + + const deliveryRequestMessage = await pickupProtocol.processStatus(messageContext) + expect(deliveryRequestMessage) + expect(deliveryRequestMessage).toEqual(new V2DeliveryRequestMessage({ id: deliveryRequestMessage?.id, limit: 1 })) + }) + }) + + describe('processDelivery', () => { + it('if the delivery has no attachments expect an error', async () => { + const messageContext = new InboundMessageContext({} as V2MessageDeliveryMessage, { + connection: mockConnection, + agentContext, + }) + + await expect(pickupProtocol.processDelivery(messageContext)).rejects.toThrowError( + new AriesFrameworkError('Error processing attachments') + ) + }) + + it('should return a message received with an message id list in it', async () => { + const messageDeliveryMessage = new V2MessageDeliveryMessage({ + threadId: uuid(), + attachments: [ + new Attachment({ + id: '1', + data: { + json: { + a: 'value', + }, + }, + }), + ], + }) + const messageContext = new InboundMessageContext(messageDeliveryMessage, { + connection: mockConnection, + agentContext, + }) + + const messagesReceivedMessage = await pickupProtocol.processDelivery(messageContext) + + expect(messagesReceivedMessage).toEqual( + new V2MessagesReceivedMessage({ + id: messagesReceivedMessage.id, + messageIdList: ['1'], + }) + ) + }) + + it('calls the event emitter for each message', async () => { + // This is to not take into account events previously emitted + jest.clearAllMocks() + + const messageDeliveryMessage = new V2MessageDeliveryMessage({ + threadId: uuid(), + attachments: [ + new Attachment({ + id: '1', + data: { + json: { + first: 'value', + }, + }, + }), + new Attachment({ + id: '2', + data: { + json: { + second: 'value', + }, + }, + }), + ], + }) + const messageContext = new InboundMessageContext(messageDeliveryMessage, { + connection: mockConnection, + agentContext, + }) + + await pickupProtocol.processDelivery(messageContext) + + expect(eventEmitter.emit).toHaveBeenCalledTimes(2) + expect(eventEmitter.emit).toHaveBeenNthCalledWith(1, agentContext, { + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: { first: 'value' }, + contextCorrelationId: agentContext.contextCorrelationId, + }, + }) + expect(eventEmitter.emit).toHaveBeenNthCalledWith(2, agentContext, { + type: AgentEventTypes.AgentMessageReceived, + payload: { + message: { second: 'value' }, + contextCorrelationId: agentContext.contextCorrelationId, + }, + }) + }) + }) +}) diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2DeliveryRequestHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2DeliveryRequestHandler.ts" new file mode 100644 index 0000000000..b935dcd512 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2DeliveryRequestHandler.ts" @@ -0,0 +1,19 @@ +import type { MessageHandler } from '../../../../../agent/MessageHandler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' + +import { V2DeliveryRequestMessage } from '../messages' + +export class V2DeliveryRequestHandler implements MessageHandler { + public supportedMessages = [V2DeliveryRequestMessage] + private messagePickupService: V2MessagePickupProtocol + + public constructor(messagePickupService: V2MessagePickupProtocol) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + messageContext.assertReadyConnection() + return this.messagePickupService.processDeliveryRequest(messageContext) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessageDeliveryHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessageDeliveryHandler.ts" new file mode 100644 index 0000000000..918b3f37b8 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessageDeliveryHandler.ts" @@ -0,0 +1,27 @@ +import type { MessageHandler } from '../../../../../agent/MessageHandler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' + +import { OutboundMessageContext } from '../../../../../agent/models' +import { V2MessageDeliveryMessage } from '../messages/V2MessageDeliveryMessage' + +export class V2MessageDeliveryHandler implements MessageHandler { + public supportedMessages = [V2MessageDeliveryMessage] + private messagePickupService: V2MessagePickupProtocol + + public constructor(messagePickupService: V2MessagePickupProtocol) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + const deliveryReceivedMessage = await this.messagePickupService.processDelivery(messageContext) + + if (deliveryReceivedMessage) { + return new OutboundMessageContext(deliveryReceivedMessage, { + agentContext: messageContext.agentContext, + connection, + }) + } + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessagesReceivedHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessagesReceivedHandler.ts" new file mode 100644 index 0000000000..5820c4878c --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2MessagesReceivedHandler.ts" @@ -0,0 +1,19 @@ +import type { MessageHandler } from '../../../../../agent/MessageHandler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' + +import { V2MessagesReceivedMessage } from '../messages' + +export class V2MessagesReceivedHandler implements MessageHandler { + public supportedMessages = [V2MessagesReceivedMessage] + private messagePickupService: V2MessagePickupProtocol + + public constructor(messagePickupService: V2MessagePickupProtocol) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + messageContext.assertReadyConnection() + return this.messagePickupService.processMessagesReceived(messageContext) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusHandler.ts" new file mode 100644 index 0000000000..0e4d1467f2 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusHandler.ts" @@ -0,0 +1,27 @@ +import type { MessageHandler } from '../../../../../agent/MessageHandler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' + +import { OutboundMessageContext } from '../../../../../agent/models' +import { V2StatusMessage } from '../messages' + +export class V2StatusHandler implements MessageHandler { + public supportedMessages = [V2StatusMessage] + private messagePickupService: V2MessagePickupProtocol + + public constructor(messagePickupService: V2MessagePickupProtocol) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + const connection = messageContext.assertReadyConnection() + const deliveryRequestMessage = await this.messagePickupService.processStatus(messageContext) + + if (deliveryRequestMessage) { + return new OutboundMessageContext(deliveryRequestMessage, { + agentContext: messageContext.agentContext, + connection, + }) + } + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusRequestHandler.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusRequestHandler.ts" new file mode 100644 index 0000000000..b9e365b8a4 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/V2StatusRequestHandler.ts" @@ -0,0 +1,19 @@ +import type { MessageHandler } from '../../../../../agent/MessageHandler' +import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { V2MessagePickupProtocol } from '../V2MessagePickupProtocol' + +import { V2StatusRequestMessage } from '../messages' + +export class V2StatusRequestHandler implements MessageHandler { + public supportedMessages = [V2StatusRequestMessage] + private messagePickupService: V2MessagePickupProtocol + + public constructor(messagePickupService: V2MessagePickupProtocol) { + this.messagePickupService = messagePickupService + } + + public async handle(messageContext: InboundMessageContext) { + messageContext.assertReadyConnection() + return this.messagePickupService.processStatusRequest(messageContext) + } +} diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/index.ts" new file mode 100644 index 0000000000..5f54b56ac7 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/handlers/index.ts" @@ -0,0 +1,5 @@ +export * from './V2DeliveryRequestHandler' +export * from './V2MessageDeliveryHandler' +export * from './V2MessagesReceivedHandler' +export * from './V2StatusHandler' +export * from './V2StatusRequestHandler' diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/index.ts" new file mode 100644 index 0000000000..90567cdaf4 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/index.ts" @@ -0,0 +1,2 @@ +export * from './V2MessagePickupProtocol' +export * from './messages' diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/messages/DeliveryRequestMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" similarity index 59% rename from packages/core/src/modules/routing/protocol/pickup/v2/messages/DeliveryRequestMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" index 21e044309a..b7c37bf426 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/messages/DeliveryRequestMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" @@ -1,18 +1,18 @@ import { Expose } from 'class-transformer' import { IsInt, IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { ReturnRouteTypes } from '../../../../../../decorators/transport/TransportDecorator' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -export interface DeliveryRequestMessageOptions { +export interface V2DeliveryRequestMessageOptions { id?: string recipientKey?: string limit: number } -export class DeliveryRequestMessage extends AgentMessage { - public constructor(options: DeliveryRequestMessageOptions) { +export class V2DeliveryRequestMessage extends AgentMessage { + public constructor(options: V2DeliveryRequestMessageOptions) { super() if (options) { @@ -23,8 +23,8 @@ export class DeliveryRequestMessage extends AgentMessage { this.setReturnRouting(ReturnRouteTypes.all) } - @IsValidMessageType(DeliveryRequestMessage.type) - public readonly type = DeliveryRequestMessage.type.messageTypeUri + @IsValidMessageType(V2DeliveryRequestMessage.type) + public readonly type = V2DeliveryRequestMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/delivery-request') @IsString() diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/messages/MessageDeliveryMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" similarity index 57% rename from packages/core/src/modules/routing/protocol/pickup/v2/messages/MessageDeliveryMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" index fc3e215720..48783f634b 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/messages/MessageDeliveryMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" @@ -1,21 +1,21 @@ -import type { Attachment } from '../../../../../../decorators/attachment/Attachment' +import type { Attachment } from '../../../../../decorators/attachment/Attachment' import { Expose } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { ReturnRouteTypes } from '../../../../../../decorators/transport/TransportDecorator' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -export interface MessageDeliveryMessageOptions { +export interface V2MessageDeliveryMessageOptions { id?: string recipientKey?: string threadId: string attachments: Attachment[] } -export class MessageDeliveryMessage extends AgentMessage { - public constructor(options: MessageDeliveryMessageOptions) { +export class V2MessageDeliveryMessage extends AgentMessage { + public constructor(options: V2MessageDeliveryMessageOptions) { super() if (options) { @@ -29,8 +29,8 @@ export class MessageDeliveryMessage extends AgentMessage { this.setReturnRouting(ReturnRouteTypes.all) } - @IsValidMessageType(MessageDeliveryMessage.type) - public readonly type = MessageDeliveryMessage.type.messageTypeUri + @IsValidMessageType(V2MessageDeliveryMessage.type) + public readonly type = V2MessageDeliveryMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/delivery') @IsString() diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/messages/MessagesReceivedMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" similarity index 55% rename from packages/core/src/modules/routing/protocol/pickup/v2/messages/MessagesReceivedMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" index be59ba7639..23da433de6 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/messages/MessagesReceivedMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" @@ -1,17 +1,17 @@ import { Expose } from 'class-transformer' import { IsArray, IsOptional } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { ReturnRouteTypes } from '../../../../../../decorators/transport/TransportDecorator' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -export interface MessagesReceivedMessageOptions { +export interface V2MessagesReceivedMessageOptions { id?: string messageIdList: string[] } -export class MessagesReceivedMessage extends AgentMessage { - public constructor(options: MessagesReceivedMessageOptions) { +export class V2MessagesReceivedMessage extends AgentMessage { + public constructor(options: V2MessagesReceivedMessageOptions) { super() if (options) { @@ -21,8 +21,8 @@ export class MessagesReceivedMessage extends AgentMessage { this.setReturnRouting(ReturnRouteTypes.all) } - @IsValidMessageType(MessagesReceivedMessage.type) - public readonly type = MessagesReceivedMessage.type.messageTypeUri + @IsValidMessageType(V2MessagesReceivedMessage.type) + public readonly type = V2MessagesReceivedMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/messages-received') @IsArray() diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" similarity index 79% rename from packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" index 8e2851ba6c..a28296742e 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" @@ -1,12 +1,12 @@ import { Expose, Transform } from 'class-transformer' import { IsBoolean, IsDate, IsInt, IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { ReturnRouteTypes } from '../../../../../../decorators/transport/TransportDecorator' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' -import { DateParser } from '../../../../../../utils/transformers' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { DateParser } from '../../../../../utils/transformers' -export interface StatusMessageOptions { +export interface V2StatusMessageOptions { id?: string recipientKey?: string threadId: string @@ -18,8 +18,8 @@ export interface StatusMessageOptions { liveDelivery?: boolean } -export class StatusMessage extends AgentMessage { - public constructor(options: StatusMessageOptions) { +export class V2StatusMessage extends AgentMessage { + public constructor(options: V2StatusMessageOptions) { super() if (options) { this.id = options.id || this.generateId() @@ -37,8 +37,8 @@ export class StatusMessage extends AgentMessage { this.setReturnRouting(ReturnRouteTypes.all) } - @IsValidMessageType(StatusMessage.type) - public readonly type = StatusMessage.type.messageTypeUri + @IsValidMessageType(V2StatusMessage.type) + public readonly type = V2StatusMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/status') @IsString() diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusRequestMessage.ts "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" similarity index 59% rename from packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusRequestMessage.ts rename to "packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" index c25c1d8c4a..eb6908bae2 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/messages/StatusRequestMessage.ts +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" @@ -1,16 +1,16 @@ import { Expose } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../../../../agent/AgentMessage' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { AgentMessage } from '../../../../../agent/AgentMessage' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -export interface StatusRequestMessageOptions { +export interface V2StatusRequestMessageOptions { id?: string recipientKey?: string } -export class StatusRequestMessage extends AgentMessage { - public constructor(options: StatusRequestMessageOptions) { +export class V2StatusRequestMessage extends AgentMessage { + public constructor(options: V2StatusRequestMessageOptions) { super() if (options) { @@ -19,8 +19,8 @@ export class StatusRequestMessage extends AgentMessage { } } - @IsValidMessageType(StatusRequestMessage.type) - public readonly type = StatusRequestMessage.type.messageTypeUri + @IsValidMessageType(V2StatusRequestMessage.type) + public readonly type = V2StatusRequestMessage.type.messageTypeUri public static readonly type = parseMessageType('https://didcomm.org/messagepickup/2.0/status-request') @IsString() diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/index.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/index.ts" new file mode 100644 index 0000000000..4746216ec0 --- /dev/null +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/index.ts" @@ -0,0 +1,5 @@ +export * from './V2DeliveryRequestMessage' +export * from './V2MessageDeliveryMessage' +export * from './V2MessagesReceivedMessage' +export * from './V2StatusMessage' +export * from './V2StatusRequestMessage' diff --git a/packages/core/src/modules/routing/MediationRecipientApi.ts b/packages/core/src/modules/routing/MediationRecipientApi.ts index 7643542ee5..dc8a1023c1 100644 --- a/packages/core/src/modules/routing/MediationRecipientApi.ts +++ b/packages/core/src/modules/routing/MediationRecipientApi.ts @@ -23,6 +23,9 @@ import { ConnectionService } from '../connections/services' import { DidsApi } from '../dids' import { verkeyToDidKey } from '../dids/helpers' import { DiscoverFeaturesApi } from '../discover-features' +import { MessagePickupApi } from '../message-pìckup/MessagePickupApi' +import { V1BatchPickupMessage } from '../message-pìckup/protocol/v1' +import { V2StatusMessage } from '../message-pìckup/protocol/v2' import { MediationRecipientModuleConfig } from './MediationRecipientModuleConfig' import { MediatorPickupStrategy } from './MediatorPickupStrategy' @@ -32,8 +35,6 @@ import { MediationDenyHandler } from './handlers/MediationDenyHandler' import { MediationGrantHandler } from './handlers/MediationGrantHandler' import { KeylistUpdate, KeylistUpdateAction, KeylistUpdateMessage } from './messages' import { MediationState } from './models/MediationState' -import { StatusRequestMessage, BatchPickupMessage, StatusMessage } from './protocol' -import { StatusHandler, MessageDeliveryHandler } from './protocol/pickup/v2/handlers' import { MediationRepository } from './repository' import { MediationRecipientService } from './services/MediationRecipientService' import { RoutingService } from './services/RoutingService' @@ -49,6 +50,7 @@ export class MediationRecipientApi { private eventEmitter: EventEmitter private logger: Logger private discoverFeaturesApi: DiscoverFeaturesApi + private messagePickupApi: MessagePickupApi private mediationRepository: MediationRepository private routingService: RoutingService private agentContext: AgentContext @@ -65,6 +67,7 @@ export class MediationRecipientApi { messageSender: MessageSender, eventEmitter: EventEmitter, discoverFeaturesApi: DiscoverFeaturesApi, + messagePickupApi: MessagePickupApi, mediationRepository: MediationRepository, routingService: RoutingService, @inject(InjectionSymbols.Logger) logger: Logger, @@ -79,6 +82,7 @@ export class MediationRecipientApi { this.eventEmitter = eventEmitter this.logger = logger this.discoverFeaturesApi = discoverFeaturesApi + this.messagePickupApi = messagePickupApi this.mediationRepository = mediationRepository this.routingService = routingService this.agentContext = agentContext @@ -195,7 +199,11 @@ export class MediationRecipientApi { try { if (pickupStrategy === MediatorPickupStrategy.PickUpV2) { // Start Pickup v2 protocol to receive messages received while websocket offline - await this.sendStatusRequest({ mediatorId: mediator.id }) + await this.messagePickupApi.pickupMessages({ + connectionId: mediator.connectionId, + batchSize: this.config.maximumMessagePickup, + protocolVersion: 'v2', + }) } else { await this.openMediationWebSocket(mediator) } @@ -237,7 +245,11 @@ export class MediationRecipientApi { case MediatorPickupStrategy.PickUpV2: this.logger.info(`Starting pickup of messages from mediator '${mediatorRecord.id}'`) await this.openWebSocketAndPickUp(mediatorRecord, mediatorPickupStrategy) - await this.sendStatusRequest({ mediatorId: mediatorRecord.id }) + await this.messagePickupApi.pickupMessages({ + connectionId: mediatorConnection.id, + batchSize: this.config.maximumMessagePickup, + protocolVersion: 'v2', + }) break case MediatorPickupStrategy.PickUpV1: { const stopConditions$ = merge(this.stop$, this.stopMessagePickup$).pipe() @@ -247,7 +259,11 @@ export class MediationRecipientApi { .pipe(takeUntil(stopConditions$)) .subscribe({ next: async () => { - await this.pickupMessages(mediatorConnection) + await this.messagePickupApi.pickupMessages({ + connectionId: mediatorConnection.id, + batchSize: this.config.maximumMessagePickup, + protocolVersion: 'v1', + }) }, complete: () => this.logger.info(`Stopping pickup of messages from mediator '${mediatorRecord.id}'`), }) @@ -271,22 +287,6 @@ export class MediationRecipientApi { this.stopMessagePickup$.next(true) } - private async sendStatusRequest(config: { mediatorId: string; recipientKey?: string }) { - const mediationRecord = await this.mediationRecipientService.getById(this.agentContext, config.mediatorId) - - const statusRequestMessage = await this.mediationRecipientService.createStatusRequest(mediationRecord, { - recipientKey: config.recipientKey, - }) - - const mediatorConnection = await this.connectionService.getById(this.agentContext, mediationRecord.connectionId) - return this.messageSender.sendMessage( - new OutboundMessageContext(statusRequestMessage, { - agentContext: this.agentContext, - connection: mediatorConnection, - }) - ) - } - private async getPickupStrategyForMediator(mediator: MediationRecord) { let mediatorPickupStrategy = mediator.pickupStrategy ?? this.config.mediatorPickupStrategy @@ -296,22 +296,22 @@ export class MediationRecipientApi { const discloseForPickupV2 = await this.discoverFeaturesApi.queryFeatures({ connectionId: mediator.connectionId, protocolVersion: 'v1', - queries: [{ featureType: 'protocol', match: StatusMessage.type.protocolUri }], + queries: [{ featureType: 'protocol', match: V2StatusMessage.type.protocolUri }], awaitDisclosures: true, }) - if (discloseForPickupV2.features?.find((item) => item.id === StatusMessage.type.protocolUri)) { + if (discloseForPickupV2.features?.find((item) => item.id === V2StatusMessage.type.protocolUri)) { mediatorPickupStrategy = MediatorPickupStrategy.PickUpV2 } else { const discloseForPickupV1 = await this.discoverFeaturesApi.queryFeatures({ connectionId: mediator.connectionId, protocolVersion: 'v1', - queries: [{ featureType: 'protocol', match: BatchPickupMessage.type.protocolUri }], + queries: [{ featureType: 'protocol', match: V1BatchPickupMessage.type.protocolUri }], awaitDisclosures: true, }) // Use explicit pickup strategy mediatorPickupStrategy = discloseForPickupV1.features?.find( - (item) => item.id === BatchPickupMessage.type.protocolUri + (item) => item.id === V1BatchPickupMessage.type.protocolUri ) ? MediatorPickupStrategy.PickUpV1 : MediatorPickupStrategy.Implicit @@ -329,18 +329,18 @@ export class MediationRecipientApi { return this.mediationRecipientService.discoverMediation(this.agentContext) } + /** + * @deprecated Use `MessagePickupApi.pickupMessages` instead. + * */ public async pickupMessages(mediatorConnection: ConnectionRecord, pickupStrategy?: MediatorPickupStrategy) { mediatorConnection.assertReady() - const pickupMessage = - pickupStrategy === MediatorPickupStrategy.PickUpV2 - ? new StatusRequestMessage({}) - : new BatchPickupMessage({ batchSize: 10 }) - const outboundMessageContext = new OutboundMessageContext(pickupMessage, { - agentContext: this.agentContext, - connection: mediatorConnection, + const messagePickupApi = this.agentContext.dependencyManager.resolve(MessagePickupApi) + + await messagePickupApi.pickupMessages({ + connectionId: mediatorConnection.id, + protocolVersion: pickupStrategy === MediatorPickupStrategy.PickUpV2 ? 'v2' : 'v1', }) - await this.sendMessage(outboundMessageContext, pickupStrategy) } public async setDefaultMediator(mediatorRecord: MediationRecord) { @@ -476,8 +476,6 @@ export class MediationRecipientApi { messageHandlerRegistry.registerMessageHandler(new KeylistUpdateResponseHandler(this.mediationRecipientService)) messageHandlerRegistry.registerMessageHandler(new MediationGrantHandler(this.mediationRecipientService)) messageHandlerRegistry.registerMessageHandler(new MediationDenyHandler(this.mediationRecipientService)) - messageHandlerRegistry.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) - messageHandlerRegistry.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) //messageHandlerRegistry.registerMessageHandler(new KeylistListHandler(this.mediationRecipientService)) // TODO: write this } } diff --git a/packages/core/src/modules/routing/MediatorApi.ts b/packages/core/src/modules/routing/MediatorApi.ts index af477e6052..af1bf29e79 100644 --- a/packages/core/src/modules/routing/MediatorApi.ts +++ b/packages/core/src/modules/routing/MediatorApi.ts @@ -2,18 +2,16 @@ import type { MediationRecord } from './repository' import type { EncryptedMessage } from '../../types' import { AgentContext } from '../../agent' -import { EventEmitter } from '../../agent/EventEmitter' import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' import { MessageSender } from '../../agent/MessageSender' import { OutboundMessageContext } from '../../agent/models' import { injectable } from '../../plugins' import { ConnectionService } from '../connections/services' +import { MessagePickupApi } from '../message-pìckup' import { MediatorModuleConfig } from './MediatorModuleConfig' import { ForwardHandler, KeylistUpdateHandler } from './handlers' import { MediationRequestHandler } from './handlers/MediationRequestHandler' -import { MessagePickupService, V2MessagePickupService } from './protocol' -import { BatchHandler, BatchPickupHandler } from './protocol/pickup/v1/handlers' import { MediatorService } from './services/MediatorService' @injectable() @@ -21,28 +19,20 @@ export class MediatorApi { public config: MediatorModuleConfig private mediatorService: MediatorService - private messagePickupService: MessagePickupService private messageSender: MessageSender - private eventEmitter: EventEmitter private agentContext: AgentContext private connectionService: ConnectionService public constructor( messageHandlerRegistry: MessageHandlerRegistry, mediationService: MediatorService, - messagePickupService: MessagePickupService, - // Only imported so it is injected and handlers are registered - v2MessagePickupService: V2MessagePickupService, messageSender: MessageSender, - eventEmitter: EventEmitter, agentContext: AgentContext, connectionService: ConnectionService, config: MediatorModuleConfig ) { this.mediatorService = mediationService - this.messagePickupService = messagePickupService this.messageSender = messageSender - this.eventEmitter = eventEmitter this.connectionService = connectionService this.agentContext = agentContext this.config = config @@ -81,8 +71,12 @@ export class MediatorApi { return mediationRecord } + /** + * @deprecated Use `MessagePickupApi.queueMessage` instead. + * */ public queueMessage(connectionId: string, message: EncryptedMessage) { - return this.messagePickupService.queueMessage(connectionId, message) + const messagePickupApi = this.agentContext.dependencyManager.resolve(MessagePickupApi) + return messagePickupApi.queueMessage({ connectionId, message }) } private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { @@ -90,8 +84,6 @@ export class MediatorApi { messageHandlerRegistry.registerMessageHandler( new ForwardHandler(this.mediatorService, this.connectionService, this.messageSender) ) - messageHandlerRegistry.registerMessageHandler(new BatchPickupHandler(this.messagePickupService)) - messageHandlerRegistry.registerMessageHandler(new BatchHandler(this.eventEmitter)) messageHandlerRegistry.registerMessageHandler(new MediationRequestHandler(this.mediatorService, this.config)) } } diff --git a/packages/core/src/modules/routing/MediatorModule.ts b/packages/core/src/modules/routing/MediatorModule.ts index fa4ef31f13..0ddc220263 100644 --- a/packages/core/src/modules/routing/MediatorModule.ts +++ b/packages/core/src/modules/routing/MediatorModule.ts @@ -7,7 +7,6 @@ import { Protocol } from '../../agent/models' import { MediatorApi } from './MediatorApi' import { MediatorModuleConfig } from './MediatorModuleConfig' import { MediationRole } from './models' -import { MessagePickupService, V2MessagePickupService } from './protocol' import { MediationRepository, MediatorRoutingRepository } from './repository' import { MediatorService } from './services' @@ -31,8 +30,6 @@ export class MediatorModule implements Module { // Services dependencyManager.registerSingleton(MediatorService) - dependencyManager.registerSingleton(MessagePickupService) - dependencyManager.registerSingleton(V2MessagePickupService) // Repositories dependencyManager.registerSingleton(MediationRepository) @@ -43,14 +40,6 @@ export class MediatorModule implements Module { new Protocol({ id: 'https://didcomm.org/coordinate-mediation/1.0', roles: [MediationRole.Mediator], - }), - new Protocol({ - id: 'https://didcomm.org/messagepickup/1.0', - roles: ['message_holder', 'recipient', 'batch_sender', 'batch_recipient'], - }), - new Protocol({ - id: 'https://didcomm.org/messagepickup/2.0', - roles: ['mediator', 'recipient'], }) ) } diff --git a/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts b/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts index 5835103180..81ba044281 100644 --- a/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts +++ b/packages/core/src/modules/routing/__tests__/MediatorModule.test.ts @@ -2,7 +2,6 @@ import { FeatureRegistry } from '../../../agent/FeatureRegistry' import { DependencyManager } from '../../../plugins/DependencyManager' import { MediatorApi } from '../MediatorApi' import { MediatorModule } from '../MediatorModule' -import { MessagePickupService, V2MessagePickupService } from '../protocol' import { MediationRepository, MediatorRoutingRepository } from '../repository' import { MediatorService } from '../services' @@ -22,10 +21,8 @@ describe('MediatorModule', () => { expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(MediatorApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(5) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediatorService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MessagePickupService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(V2MessagePickupService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediationRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(MediatorRoutingRepository) }) diff --git a/packages/core/src/modules/routing/index.ts b/packages/core/src/modules/routing/index.ts index a644af607a..981dbe5207 100644 --- a/packages/core/src/modules/routing/index.ts +++ b/packages/core/src/modules/routing/index.ts @@ -1,6 +1,5 @@ export * from './messages' export * from './services' -export * from './protocol' export * from './repository' export * from './models' export * from './RoutingEvents' diff --git a/packages/core/src/modules/routing/protocol/index.ts b/packages/core/src/modules/routing/protocol/index.ts deleted file mode 100644 index c18db7326d..0000000000 --- a/packages/core/src/modules/routing/protocol/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './pickup' diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts deleted file mode 100644 index aa97fc51a4..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v1/MessagePickupService.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { BatchPickupMessage } from './messages' -import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import type { EncryptedMessage } from '../../../../../types' - -import { EventEmitter } from '../../../../../agent/EventEmitter' -import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' -import { OutboundMessageContext } from '../../../../../agent/models' -import { InjectionSymbols } from '../../../../../constants' -import { inject, injectable } from '../../../../../plugins' -import { MessageRepository } from '../../../../../storage/MessageRepository' - -import { BatchHandler, BatchPickupHandler } from './handlers' -import { BatchMessage, BatchMessageMessage } from './messages' - -@injectable() -export class MessagePickupService { - private messageRepository: MessageRepository - private eventEmitter: EventEmitter - - public constructor( - @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, - messageHandlerRegistry: MessageHandlerRegistry, - eventEmitter: EventEmitter - ) { - this.messageRepository = messageRepository - this.eventEmitter = eventEmitter - - this.registerMessageHandlers(messageHandlerRegistry) - } - - public async batch(messageContext: InboundMessageContext) { - // Assert ready connection - const connection = messageContext.assertReadyConnection() - - const { message } = messageContext - const messages = await this.messageRepository.takeFromQueue(connection.id, message.batchSize) - - // TODO: each message should be stored with an id. to be able to conform to the id property - // of batch message - const batchMessages = messages.map( - (msg) => - new BatchMessageMessage({ - message: msg, - }) - ) - - const batchMessage = new BatchMessage({ - messages: batchMessages, - }) - - return new OutboundMessageContext(batchMessage, { agentContext: messageContext.agentContext, connection }) - } - - public async queueMessage(connectionId: string, message: EncryptedMessage) { - await this.messageRepository.add(connectionId, message) - } - - protected registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { - messageHandlerRegistry.registerMessageHandler(new BatchPickupHandler(this)) - messageHandlerRegistry.registerMessageHandler(new BatchHandler(this.eventEmitter)) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts deleted file mode 100644 index 30d8e5263f..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchHandler.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { EventEmitter } from '../../../../../../agent/EventEmitter' -import type { AgentMessageReceivedEvent } from '../../../../../../agent/Events' -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../../agent/MessageHandler' - -import { AgentEventTypes } from '../../../../../../agent/Events' -import { BatchMessage } from '../messages' - -export class BatchHandler implements MessageHandler { - private eventEmitter: EventEmitter - public supportedMessages = [BatchMessage] - - public constructor(eventEmitter: EventEmitter) { - this.eventEmitter = eventEmitter - } - - public async handle(messageContext: MessageHandlerInboundMessage) { - const { message } = messageContext - - messageContext.assertReadyConnection() - - const forwardedMessages = message.messages - forwardedMessages.forEach((message) => { - this.eventEmitter.emit(messageContext.agentContext, { - type: AgentEventTypes.AgentMessageReceived, - payload: { - message: message.message, - contextCorrelationId: messageContext.agentContext.contextCorrelationId, - }, - }) - }) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts deleted file mode 100644 index e47fb6c2f5..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/BatchPickupHandler.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../../agent/MessageHandler' -import type { MessagePickupService } from '../MessagePickupService' - -import { BatchPickupMessage } from '../messages' - -export class BatchPickupHandler implements MessageHandler { - private messagePickupService: MessagePickupService - public supportedMessages = [BatchPickupMessage] - - public constructor(messagePickupService: MessagePickupService) { - this.messagePickupService = messagePickupService - } - - public async handle(messageContext: MessageHandlerInboundMessage) { - messageContext.assertReadyConnection() - - return this.messagePickupService.batch(messageContext) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/index.ts b/packages/core/src/modules/routing/protocol/pickup/v1/handlers/index.ts deleted file mode 100644 index d7a709a49d..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v1/handlers/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './BatchHandler' -export * from './BatchPickupHandler' diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/index.ts b/packages/core/src/modules/routing/protocol/pickup/v1/index.ts deleted file mode 100644 index 9174e24a93..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v1/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './MessagePickupService' -export * from './messages' diff --git a/packages/core/src/modules/routing/protocol/pickup/v1/messages/index.ts b/packages/core/src/modules/routing/protocol/pickup/v1/messages/index.ts deleted file mode 100644 index 8e32f97f68..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v1/messages/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './BatchMessage' -export * from './BatchPickupMessage' diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts b/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts deleted file mode 100644 index dc99c47856..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/V2MessagePickupService.ts +++ /dev/null @@ -1,124 +0,0 @@ -import type { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from './messages' -import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import type { EncryptedMessage } from '../../../../../types' - -import { MessageHandlerRegistry } from '../../../../../agent/MessageHandlerRegistry' -import { OutboundMessageContext } from '../../../../../agent/models' -import { InjectionSymbols } from '../../../../../constants' -import { Attachment } from '../../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../../error' -import { inject, injectable } from '../../../../../plugins' -import { MessageRepository } from '../../../../../storage/MessageRepository' -import { MediationRecipientService } from '../../../services' - -import { - DeliveryRequestHandler, - MessageDeliveryHandler, - MessagesReceivedHandler, - StatusHandler, - StatusRequestHandler, -} from './handlers' -import { MessageDeliveryMessage, StatusMessage } from './messages' - -@injectable() -export class V2MessagePickupService { - private messageRepository: MessageRepository - private mediationRecipientService: MediationRecipientService - - public constructor( - @inject(InjectionSymbols.MessageRepository) messageRepository: MessageRepository, - messageHandlerRegistry: MessageHandlerRegistry, - mediationRecipientService: MediationRecipientService - ) { - this.messageRepository = messageRepository - this.mediationRecipientService = mediationRecipientService - - this.registerMessageHandlers(messageHandlerRegistry) - } - - public async processStatusRequest(messageContext: InboundMessageContext) { - // Assert ready connection - const connection = messageContext.assertReadyConnection() - - if (messageContext.message.recipientKey) { - throw new AriesFrameworkError('recipient_key parameter not supported') - } - - const statusMessage = new StatusMessage({ - threadId: messageContext.message.threadId, - messageCount: await this.messageRepository.getAvailableMessageCount(connection.id), - }) - - return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) - } - - public async queueMessage(connectionId: string, message: EncryptedMessage) { - await this.messageRepository.add(connectionId, message) - } - - public async processDeliveryRequest(messageContext: InboundMessageContext) { - // Assert ready connection - const connection = messageContext.assertReadyConnection() - - if (messageContext.message.recipientKey) { - throw new AriesFrameworkError('recipient_key parameter not supported') - } - - const { message } = messageContext - - // Get available messages from queue, but don't delete them - const messages = await this.messageRepository.takeFromQueue(connection.id, message.limit, true) - - // TODO: each message should be stored with an id. to be able to conform to the id property - // of delivery message - const attachments = messages.map( - (msg) => - new Attachment({ - data: { - json: msg, - }, - }) - ) - - const outboundMessageContext = - messages.length > 0 - ? new MessageDeliveryMessage({ - threadId: messageContext.message.threadId, - attachments, - }) - : new StatusMessage({ - threadId: messageContext.message.threadId, - messageCount: 0, - }) - - return new OutboundMessageContext(outboundMessageContext, { agentContext: messageContext.agentContext, connection }) - } - - public async processMessagesReceived(messageContext: InboundMessageContext) { - // Assert ready connection - const connection = messageContext.assertReadyConnection() - - const { message } = messageContext - - // TODO: Add Queued Message ID - await this.messageRepository.takeFromQueue( - connection.id, - message.messageIdList ? message.messageIdList.length : undefined - ) - - const statusMessage = new StatusMessage({ - threadId: messageContext.message.threadId, - messageCount: await this.messageRepository.getAvailableMessageCount(connection.id), - }) - - return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) - } - - protected registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) { - messageHandlerRegistry.registerMessageHandler(new StatusRequestHandler(this)) - messageHandlerRegistry.registerMessageHandler(new DeliveryRequestHandler(this)) - messageHandlerRegistry.registerMessageHandler(new MessagesReceivedHandler(this)) - messageHandlerRegistry.registerMessageHandler(new StatusHandler(this.mediationRecipientService)) - messageHandlerRegistry.registerMessageHandler(new MessageDeliveryHandler(this.mediationRecipientService)) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts deleted file mode 100644 index 78334b5448..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/DeliveryRequestHandler.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { MessageHandler } from '../../../../../../agent/MessageHandler' -import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' -import type { V2MessagePickupService } from '../V2MessagePickupService' - -import { DeliveryRequestMessage } from '../messages' - -export class DeliveryRequestHandler implements MessageHandler { - public supportedMessages = [DeliveryRequestMessage] - private messagePickupService: V2MessagePickupService - - public constructor(messagePickupService: V2MessagePickupService) { - this.messagePickupService = messagePickupService - } - - public async handle(messageContext: InboundMessageContext) { - messageContext.assertReadyConnection() - return this.messagePickupService.processDeliveryRequest(messageContext) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts deleted file mode 100644 index 606647edb9..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { MessageHandler } from '../../../../../../agent/MessageHandler' -import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' -import type { MediationRecipientService } from '../../../../services' - -import { OutboundMessageContext } from '../../../../../../agent/models' -import { MessageDeliveryMessage } from '../messages/MessageDeliveryMessage' - -export class MessageDeliveryHandler implements MessageHandler { - public supportedMessages = [MessageDeliveryMessage] - private mediationRecipientService: MediationRecipientService - - public constructor(mediationRecipientService: MediationRecipientService) { - this.mediationRecipientService = mediationRecipientService - } - - public async handle(messageContext: InboundMessageContext) { - const connection = messageContext.assertReadyConnection() - const deliveryReceivedMessage = await this.mediationRecipientService.processDelivery(messageContext) - - if (deliveryReceivedMessage) { - return new OutboundMessageContext(deliveryReceivedMessage, { - agentContext: messageContext.agentContext, - connection, - }) - } - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts deleted file mode 100644 index 7ddf4b6d75..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessagesReceivedHandler.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { MessageHandler } from '../../../../../../agent/MessageHandler' -import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' -import type { V2MessagePickupService } from '../V2MessagePickupService' - -import { MessagesReceivedMessage } from '../messages' - -export class MessagesReceivedHandler implements MessageHandler { - public supportedMessages = [MessagesReceivedMessage] - private messagePickupService: V2MessagePickupService - - public constructor(messagePickupService: V2MessagePickupService) { - this.messagePickupService = messagePickupService - } - - public async handle(messageContext: InboundMessageContext) { - messageContext.assertReadyConnection() - return this.messagePickupService.processMessagesReceived(messageContext) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts deleted file mode 100644 index 7fedcc381f..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { MessageHandler } from '../../../../../../agent/MessageHandler' -import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' -import type { MediationRecipientService } from '../../../../services' - -import { OutboundMessageContext } from '../../../../../../agent/models' -import { StatusMessage } from '../messages' - -export class StatusHandler implements MessageHandler { - public supportedMessages = [StatusMessage] - private mediatorRecipientService: MediationRecipientService - - public constructor(mediatorRecipientService: MediationRecipientService) { - this.mediatorRecipientService = mediatorRecipientService - } - - public async handle(messageContext: InboundMessageContext) { - const connection = messageContext.assertReadyConnection() - const deliveryRequestMessage = await this.mediatorRecipientService.processStatus(messageContext) - - if (deliveryRequestMessage) { - return new OutboundMessageContext(deliveryRequestMessage, { - agentContext: messageContext.agentContext, - connection, - }) - } - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts deleted file mode 100644 index 1e7c67ae1d..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusRequestHandler.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { MessageHandler } from '../../../../../../agent/MessageHandler' -import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' -import type { V2MessagePickupService } from '../V2MessagePickupService' - -import { StatusRequestMessage } from '../messages' - -export class StatusRequestHandler implements MessageHandler { - public supportedMessages = [StatusRequestMessage] - private messagePickupService: V2MessagePickupService - - public constructor(messagePickupService: V2MessagePickupService) { - this.messagePickupService = messagePickupService - } - - public async handle(messageContext: InboundMessageContext) { - messageContext.assertReadyConnection() - return this.messagePickupService.processStatusRequest(messageContext) - } -} diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/index.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/index.ts deleted file mode 100644 index c8f4456634..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './DeliveryRequestHandler' -export * from './MessageDeliveryHandler' -export * from './MessagesReceivedHandler' -export * from './StatusHandler' -export * from './StatusRequestHandler' diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/index.ts b/packages/core/src/modules/routing/protocol/pickup/v2/index.ts deleted file mode 100644 index b6a5eb72c5..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './V2MessagePickupService' -export * from './messages' diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/messages/index.ts b/packages/core/src/modules/routing/protocol/pickup/v2/messages/index.ts deleted file mode 100644 index fa807e7249..0000000000 --- a/packages/core/src/modules/routing/protocol/pickup/v2/messages/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './DeliveryRequestMessage' -export * from './MessageDeliveryMessage' -export * from './MessagesReceivedMessage' -export * from './StatusMessage' -export * from './StatusRequestMessage' diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index f5149a3828..a2b861a602 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -1,21 +1,18 @@ import type { GetRoutingOptions, RemoveRoutingOptions } from './RoutingService' import type { AgentContext } from '../../../agent' import type { AgentMessage } from '../../../agent/AgentMessage' -import type { AgentMessageReceivedEvent } from '../../../agent/Events' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { Query } from '../../../storage/StorageService' -import type { EncryptedMessage } from '../../../types' import type { ConnectionRecord } from '../../connections' import type { Routing } from '../../connections/services/ConnectionService' import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' import type { MediationDenyMessage } from '../messages' -import type { StatusMessage, MessageDeliveryMessage } from '../protocol' import { firstValueFrom, ReplaySubject } from 'rxjs' import { filter, first, timeout } from 'rxjs/operators' import { EventEmitter } from '../../../agent/EventEmitter' -import { filterContextCorrelationId, AgentEventTypes } from '../../../agent/Events' +import { filterContextCorrelationId } from '../../../agent/Events' import { MessageSender } from '../../../agent/MessageSender' import { OutboundMessageContext } from '../../../agent/models' import { Key, KeyType } from '../../../crypto' @@ -27,10 +24,7 @@ import { ConnectionMetadataKeys } from '../../connections/repository/ConnectionM import { ConnectionService } from '../../connections/services/ConnectionService' import { DidKey } from '../../dids' import { didKeyToVerkey, isDidKey } from '../../dids/helpers' -import { ProblemReportError } from '../../problem-reports' -import { MediationRecipientModuleConfig } from '../MediationRecipientModuleConfig' import { RoutingEventTypes } from '../RoutingEvents' -import { RoutingProblemReportReason } from '../error' import { KeylistUpdateAction, KeylistUpdateResponseMessage, @@ -39,7 +33,6 @@ import { } from '../messages' import { KeylistUpdate, KeylistUpdateMessage } from '../messages/KeylistUpdateMessage' import { MediationRole, MediationState } from '../models' -import { DeliveryRequestMessage, MessagesReceivedMessage, StatusRequestMessage } from '../protocol/pickup/v2/messages' import { MediationRecord } from '../repository/MediationRecord' import { MediationRepository } from '../repository/MediationRepository' @@ -49,37 +42,17 @@ export class MediationRecipientService { private eventEmitter: EventEmitter private connectionService: ConnectionService private messageSender: MessageSender - private mediationRecipientModuleConfig: MediationRecipientModuleConfig public constructor( connectionService: ConnectionService, messageSender: MessageSender, mediatorRepository: MediationRepository, - eventEmitter: EventEmitter, - mediationRecipientModuleConfig: MediationRecipientModuleConfig + eventEmitter: EventEmitter ) { this.mediationRepository = mediatorRepository this.eventEmitter = eventEmitter this.connectionService = connectionService this.messageSender = messageSender - this.mediationRecipientModuleConfig = mediationRecipientModuleConfig - } - - public async createStatusRequest( - mediationRecord: MediationRecord, - config: { - recipientKey?: string - } = {} - ) { - mediationRecord.assertRole(MediationRole.Recipient) - mediationRecord.assertReady() - - const { recipientKey } = config - const statusRequest = new StatusRequestMessage({ - recipientKey, - }) - - return statusRequest } public async createRequest( @@ -308,81 +281,6 @@ export class MediationRecipientService { return mediationRecord } - public async processStatus(messageContext: InboundMessageContext) { - const connection = messageContext.assertReadyConnection() - const { message: statusMessage } = messageContext - const { messageCount, recipientKey } = statusMessage - - //No messages to be sent - if (messageCount === 0) { - const { message, connectionRecord } = await this.connectionService.createTrustPing( - messageContext.agentContext, - connection, - { - responseRequested: false, - } - ) - const websocketSchemes = ['ws', 'wss'] - - await this.messageSender.sendMessage( - new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - connection: connectionRecord, - }), - { - transportPriority: { - schemes: websocketSchemes, - restrictive: true, - // TODO: add keepAlive: true to enforce through the public api - // we need to keep the socket alive. It already works this way, but would - // be good to make more explicit from the public facing API. - // This would also make it easier to change the internal API later on. - // keepAlive: true, - }, - } - ) - - return null - } - const { maximumMessagePickup } = this.mediationRecipientModuleConfig - const limit = messageCount < maximumMessagePickup ? messageCount : maximumMessagePickup - - const deliveryRequestMessage = new DeliveryRequestMessage({ - limit, - recipientKey, - }) - - return deliveryRequestMessage - } - - public async processDelivery(messageContext: InboundMessageContext) { - messageContext.assertReadyConnection() - - const { appendedAttachments } = messageContext.message - - if (!appendedAttachments) - throw new ProblemReportError('Error processing attachments', { - problemCode: RoutingProblemReportReason.ErrorProcessingAttachments, - }) - - const ids: string[] = [] - for (const attachment of appendedAttachments) { - ids.push(attachment.id) - - this.eventEmitter.emit(messageContext.agentContext, { - type: AgentEventTypes.AgentMessageReceived, - payload: { - message: attachment.getDataAsJson(), - contextCorrelationId: messageContext.agentContext.contextCorrelationId, - }, - }) - } - - return new MessagesReceivedMessage({ - messageIdList: ids, - }) - } - /** * Update the record to a new state and emit an state changed event. Also updates the record * in storage. diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index e958a3d587..f8c2077310 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -3,12 +3,9 @@ import type { Routing } from '../../../connections/services/ConnectionService' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../tests/helpers' import { EventEmitter } from '../../../../agent/EventEmitter' -import { AgentEventTypes } from '../../../../agent/Events' import { MessageSender } from '../../../../agent/MessageSender' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import { Key } from '../../../../crypto' -import { Attachment } from '../../../../decorators/attachment/Attachment' -import { AriesFrameworkError } from '../../../../error' import { uuid } from '../../../../utils/uuid' import { DidExchangeState } from '../../../connections' import { ConnectionMetadataKeys } from '../../../connections/repository/ConnectionMetadataTypes' @@ -25,7 +22,6 @@ import { MediationGrantMessage, } from '../../messages' import { MediationRole, MediationState } from '../../models' -import { DeliveryRequestMessage, MessageDeliveryMessage, MessagesReceivedMessage, StatusMessage } from '../../protocol' import { MediationRecord } from '../../repository/MediationRecord' import { MediationRepository } from '../../repository/MediationRepository' import { MediationRecipientService } from '../MediationRecipientService' @@ -50,10 +46,6 @@ const DidRegistrarServiceMock = DidRegistrarService as jest.Mock { const config = getAgentConfig('MediationRecipientServiceTest', { endpoints: ['http://agent.com:8080'], @@ -105,8 +97,7 @@ describe('MediationRecipientService', () => { connectionService, messageSender, mediationRepository, - eventEmitter, - new MediationRecipientModuleConfig() + eventEmitter ) }) @@ -156,33 +147,6 @@ describe('MediationRecipientService', () => { }) }) - describe('createStatusRequest', () => { - it('creates a status request message', async () => { - const statusRequestMessage = await mediationRecipientService.createStatusRequest(mediationRecord, { - recipientKey: 'a-key', - }) - - expect(statusRequestMessage).toMatchObject({ - id: expect.any(String), - recipientKey: 'a-key', - }) - }) - - it('it throws an error when the mediation record has incorrect role or state', async () => { - mediationRecord.role = MediationRole.Mediator - await expect(mediationRecipientService.createStatusRequest(mediationRecord)).rejects.toThrowError( - 'Mediation record has invalid role MEDIATOR. Expected role RECIPIENT.' - ) - - mediationRecord.role = MediationRole.Recipient - mediationRecord.state = MediationState.Requested - - await expect(mediationRecipientService.createStatusRequest(mediationRecord)).rejects.toThrowError( - 'Mediation record is not ready to be used. Expected granted, found invalid state requested' - ) - }) - }) - describe('processKeylistUpdateResults', () => { it('it stores did:key-encoded keys in base58 format', async () => { const spyAddRecipientKey = jest.spyOn(mediationRecord, 'addRecipientKey') @@ -226,119 +190,6 @@ describe('MediationRecipientService', () => { }) }) - describe('processStatus', () => { - it('if status request has a message count of zero returns nothing', async () => { - const status = new StatusMessage({ - threadId: uuid(), - messageCount: 0, - }) - - const messageContext = new InboundMessageContext(status, { connection: mockConnection, agentContext }) - const deliveryRequestMessage = await mediationRecipientService.processStatus(messageContext) - expect(deliveryRequestMessage).toBeNull() - }) - - it('if it has a message count greater than zero return a valid delivery request', async () => { - const status = new StatusMessage({ - threadId: uuid(), - messageCount: 1, - }) - const messageContext = new InboundMessageContext(status, { connection: mockConnection, agentContext }) - - const deliveryRequestMessage = await mediationRecipientService.processStatus(messageContext) - expect(deliveryRequestMessage) - expect(deliveryRequestMessage).toEqual(new DeliveryRequestMessage({ id: deliveryRequestMessage?.id, limit: 1 })) - }) - }) - - describe('processDelivery', () => { - it('if the delivery has no attachments expect an error', async () => { - const messageContext = new InboundMessageContext({} as MessageDeliveryMessage, { - connection: mockConnection, - agentContext, - }) - - await expect(mediationRecipientService.processDelivery(messageContext)).rejects.toThrowError( - new AriesFrameworkError('Error processing attachments') - ) - }) - - it('should return a message received with an message id list in it', async () => { - const messageDeliveryMessage = new MessageDeliveryMessage({ - threadId: uuid(), - attachments: [ - new Attachment({ - id: '1', - data: { - json: { - a: 'value', - }, - }, - }), - ], - }) - const messageContext = new InboundMessageContext(messageDeliveryMessage, { - connection: mockConnection, - agentContext, - }) - - const messagesReceivedMessage = await mediationRecipientService.processDelivery(messageContext) - - expect(messagesReceivedMessage).toEqual( - new MessagesReceivedMessage({ - id: messagesReceivedMessage.id, - messageIdList: ['1'], - }) - ) - }) - - it('calls the event emitter for each message', async () => { - const messageDeliveryMessage = new MessageDeliveryMessage({ - threadId: uuid(), - attachments: [ - new Attachment({ - id: '1', - data: { - json: { - first: 'value', - }, - }, - }), - new Attachment({ - id: '2', - data: { - json: { - second: 'value', - }, - }, - }), - ], - }) - const messageContext = new InboundMessageContext(messageDeliveryMessage, { - connection: mockConnection, - agentContext, - }) - - await mediationRecipientService.processDelivery(messageContext) - - expect(eventEmitter.emit).toHaveBeenCalledTimes(2) - expect(eventEmitter.emit).toHaveBeenNthCalledWith(1, agentContext, { - type: AgentEventTypes.AgentMessageReceived, - payload: { - message: { first: 'value' }, - contextCorrelationId: agentContext.contextCorrelationId, - }, - }) - expect(eventEmitter.emit).toHaveBeenNthCalledWith(2, agentContext, { - type: AgentEventTypes.AgentMessageReceived, - payload: { - message: { second: 'value' }, - contextCorrelationId: agentContext.contextCorrelationId, - }, - }) - }) - }) - describe('addMediationRouting', () => { const routingKey = Key.fromFingerprint('z6Mkk7yqnGF3YwTrLpqrW6PGsKci7dNqh1CjnvMbzrMerSeL') const recipientKey = Key.fromFingerprint('z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th') diff --git a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts deleted file mode 100644 index 95055f4945..0000000000 --- a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts +++ /dev/null @@ -1,251 +0,0 @@ -import type { MessageRepository } from '../../../../storage/MessageRepository' -import type { EncryptedMessage } from '../../../../types' - -import { getAgentContext, getMockConnection, mockFunction } from '../../../../../tests/helpers' -import { MessageHandlerRegistry } from '../../../../agent/MessageHandlerRegistry' -import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import { InMemoryMessageRepository } from '../../../../storage/InMemoryMessageRepository' -import { DidExchangeState } from '../../../connections' -import { - DeliveryRequestMessage, - MessageDeliveryMessage, - MessagesReceivedMessage, - StatusMessage, - StatusRequestMessage, - V2MessagePickupService, -} from '../../protocol' -import { MediationRecipientService } from '../MediationRecipientService' - -const mockConnection = getMockConnection({ - state: DidExchangeState.Completed, -}) - -// Mock classes -jest.mock('../MediationRecipientService') -jest.mock('../../../../storage/InMemoryMessageRepository') -jest.mock('../../../../agent/MessageHandlerRegistry') - -// Mock typed object -const MediationRecipientServiceMock = MediationRecipientService as jest.Mock -const MessageHandlerRegistryMock = MessageHandlerRegistry as jest.Mock -const InMessageRepositoryMock = InMemoryMessageRepository as jest.Mock - -const agentContext = getAgentContext() - -const encryptedMessage: EncryptedMessage = { - protected: 'base64url', - iv: 'base64url', - ciphertext: 'base64url', - tag: 'base64url', -} -const queuedMessages = [encryptedMessage, encryptedMessage, encryptedMessage] - -describe('V2MessagePickupService', () => { - let pickupService: V2MessagePickupService - let messageRepository: MessageRepository - - beforeEach(async () => { - const messageHandlerRegistry = new MessageHandlerRegistryMock() - const mediationRecipientService = new MediationRecipientServiceMock() - - messageRepository = new InMessageRepositoryMock() - pickupService = new V2MessagePickupService(messageRepository, messageHandlerRegistry, mediationRecipientService) - }) - - describe('processStatusRequest', () => { - test('no available messages in queue', async () => { - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(0) - - const statusRequest = new StatusRequestMessage({}) - - const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processStatusRequest(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toEqual( - new StatusMessage({ - id: message.id, - threadId: statusRequest.threadId, - messageCount: 0, - }) - ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) - }) - - test('multiple messages in queue', async () => { - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(5) - const statusRequest = new StatusRequestMessage({}) - - const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processStatusRequest(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toEqual( - new StatusMessage({ - id: message.id, - threadId: statusRequest.threadId, - messageCount: 5, - }) - ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) - }) - - test('status request specifying recipient key', async () => { - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(10) - - const statusRequest = new StatusRequestMessage({ - recipientKey: 'recipientKey', - }) - - const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) - - await expect(pickupService.processStatusRequest(messageContext)).rejects.toThrowError( - 'recipient_key parameter not supported' - ) - }) - }) - - describe('processDeliveryRequest', () => { - test('no available messages in queue', async () => { - mockFunction(messageRepository.takeFromQueue).mockResolvedValue([]) - - const deliveryRequest = new DeliveryRequestMessage({ limit: 10 }) - - const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processDeliveryRequest(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toEqual( - new StatusMessage({ - id: message.id, - threadId: deliveryRequest.threadId, - messageCount: 0, - }) - ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) - }) - - test('less messages in queue than limit', async () => { - mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages) - - const deliveryRequest = new DeliveryRequestMessage({ limit: 10 }) - - const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processDeliveryRequest(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toBeInstanceOf(MessageDeliveryMessage) - expect(message.threadId).toEqual(deliveryRequest.threadId) - expect(message.appendedAttachments?.length).toEqual(3) - expect(message.appendedAttachments).toEqual( - expect.arrayContaining( - queuedMessages.map((msg) => - expect.objectContaining({ - data: { - json: msg, - }, - }) - ) - ) - ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) - }) - - test('more messages in queue than limit', async () => { - mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages.slice(0, 2)) - - const deliveryRequest = new DeliveryRequestMessage({ limit: 2 }) - - const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processDeliveryRequest(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toBeInstanceOf(MessageDeliveryMessage) - expect(message.threadId).toEqual(deliveryRequest.threadId) - expect(message.appendedAttachments?.length).toEqual(2) - expect(message.appendedAttachments).toEqual( - expect.arrayContaining( - queuedMessages.slice(0, 2).map((msg) => - expect.objectContaining({ - data: { - json: msg, - }, - }) - ) - ) - ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2, true) - }) - - test('delivery request specifying recipient key', async () => { - mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages) - - const statusRequest = new DeliveryRequestMessage({ - limit: 10, - recipientKey: 'recipientKey', - }) - - const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) - - await expect(pickupService.processStatusRequest(messageContext)).rejects.toThrowError( - 'recipient_key parameter not supported' - ) - }) - }) - - describe('processMessagesReceived', () => { - test('messages received partially', async () => { - mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages) - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(4) - - const messagesReceived = new MessagesReceivedMessage({ - messageIdList: ['1', '2'], - }) - - const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processMessagesReceived(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toEqual( - new StatusMessage({ - id: message.id, - threadId: messagesReceived.threadId, - messageCount: 4, - }) - ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) - }) - - test('all messages have been received', async () => { - mockFunction(messageRepository.takeFromQueue).mockResolvedValue(queuedMessages) - mockFunction(messageRepository.getAvailableMessageCount).mockResolvedValue(0) - - const messagesReceived = new MessagesReceivedMessage({ - messageIdList: ['1', '2'], - }) - - const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) - - const { connection, message } = await pickupService.processMessagesReceived(messageContext) - - expect(connection).toEqual(mockConnection) - expect(message).toEqual( - new StatusMessage({ - id: message.id, - threadId: messagesReceived.threadId, - messageCount: 0, - }) - ) - - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) - }) - }) -}) From 8bc8dbcb53776cde8e495905adb7518b01c3c446 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sat, 1 Apr 2023 11:16:59 -0300 Subject: [PATCH 112/139] refactor(anoncreds): master secret to link secret (#1415) Signed-off-by: Ariel Gentile --- demo/package.json | 2 +- packages/anoncreds-rs/package.json | 4 +-- .../src/services/AnonCredsRsHolderService.ts | 36 +++++++++---------- .../src/services/__tests__/helpers.ts | 11 +++--- .../anoncreds-rs/tests/anoncreds-flow.test.ts | 4 +-- packages/anoncreds-rs/tests/indy-flow.test.ts | 4 +-- packages/anoncreds/package.json | 2 +- .../legacy-indy-format-services.test.ts | 4 +-- packages/anoncreds/src/models/internal.ts | 12 ++++--- .../credentialExchangeRecord.test.ts | 8 ++--- .../0.3.1-0.4/credentialExchangeRecord.ts | 7 ++-- .../__tests__/__snapshots__/0.3.test.ts.snap | 4 +-- .../services/IndySdkHolderService.ts | 9 +++-- .../indy-sdk/src/anoncreds/utils/transform.ts | 24 ++++++++++++- yarn.lock | 18 +++++----- 15 files changed, 85 insertions(+), 64 deletions(-) diff --git a/demo/package.json b/demo/package.json index 45b43f4a7f..81f6d992d5 100644 --- a/demo/package.json +++ b/demo/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.13", "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", "inquirer": "^8.2.5" }, diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index 9e74c5ae66..d69a68df0d 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.11", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.13", "class-transformer": "^0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.11", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.13", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index e543fd32b7..e5f9f1ef39 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -40,7 +40,7 @@ import { Credential, CredentialRequest, CredentialRevocationState, - MasterSecret, + LinkSecret, Presentation, RevocationRegistryDefinition, RevocationStatusList, @@ -55,19 +55,9 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { agentContext: AgentContext, options?: CreateLinkSecretOptions ): Promise { - let masterSecret: MasterSecret | undefined - try { - masterSecret = MasterSecret.create() - - // FIXME: This is a very specific format of anoncreds-rs. I think it should be simply a string - const linkSecretJson = masterSecret.toJson() as { value: { ms: string } } - - return { - linkSecretId: options?.linkSecretId ?? utils.uuid(), - linkSecretValue: linkSecretJson.value.ms, - } - } finally { - masterSecret?.handle.clear() + return { + linkSecretId: options?.linkSecretId ?? utils.uuid(), + linkSecretValue: LinkSecret.create(), } } @@ -184,7 +174,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { credentials: credentials.map((entry) => entry.credentialEntry), credentialsProve, selfAttest: selectedCredentials.selfAttestedAttributes, - masterSecret: { value: { ms: linkSecretRecord.value } }, + linkSecret: linkSecretRecord.value, }) return presentation.toJson() as unknown as AnonCredsProof @@ -216,6 +206,10 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { ) } + if (!linkSecretRecord.value) { + throw new AnonCredsRsError('Link Secret value not stored') + } + const isLegacyIdentifier = credentialOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex) if (!isLegacyIdentifier && useLegacyProverDid) { throw new AriesFrameworkError('Cannot use legacy prover_did with non-legacy identifiers') @@ -227,8 +221,8 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { : undefined, credentialDefinition: credentialDefinition as unknown as JsonObject, credentialOffer: credentialOffer as unknown as JsonObject, - masterSecret: { value: { ms: linkSecretRecord.value } }, - masterSecretId: linkSecretRecord.linkSecretId, + linkSecret: linkSecretRecord.value, + linkSecretId: linkSecretRecord.linkSecretId, }) return { @@ -247,7 +241,11 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { const linkSecretRecord = await agentContext.dependencyManager .resolve(AnonCredsLinkSecretRepository) - .getByLinkSecretId(agentContext, credentialRequestMetadata.master_secret_name) + .getByLinkSecretId(agentContext, credentialRequestMetadata.link_secret_name) + + if (!linkSecretRecord.value) { + throw new AnonCredsRsError('Link Secret value not stored') + } const revocationRegistryDefinition = revocationRegistry?.definition as unknown as JsonObject @@ -260,7 +258,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { processedCredential = credentialObj.process({ credentialDefinition: credentialDefinition as unknown as JsonObject, credentialRequestMetadata: credentialRequestMetadata as unknown as JsonObject, - masterSecret: { value: { ms: linkSecretRecord.value } }, + linkSecret: linkSecretRecord.value, revocationRegistryDefinition, }) diff --git a/packages/anoncreds-rs/src/services/__tests__/helpers.ts b/packages/anoncreds-rs/src/services/__tests__/helpers.ts index a6d97632f1..fefc63d9c1 100644 --- a/packages/anoncreds-rs/src/services/__tests__/helpers.ts +++ b/packages/anoncreds-rs/src/services/__tests__/helpers.ts @@ -13,7 +13,7 @@ import { CredentialOffer, CredentialRequest, CredentialRevocationConfig, - MasterSecret, + LinkSecret, RevocationRegistryDefinition, RevocationRegistryDefinitionPrivate, RevocationStatusList, @@ -77,10 +77,7 @@ export function createCredentialOffer(keyCorrectnessProof: Record { credentialDefinitionId: legacyCredentialDefinitionId, }, '_anoncreds/credentialRequest': { - master_secret_blinding_data: expect.any(Object), - master_secret_name: expect.any(String), + link_secret_blinding_data: expect.any(Object), + link_secret_name: expect.any(String), nonce: expect.any(String), }, }) diff --git a/packages/anoncreds/src/models/internal.ts b/packages/anoncreds/src/models/internal.ts index 4693e02859..8aacc72a52 100644 --- a/packages/anoncreds/src/models/internal.ts +++ b/packages/anoncreds/src/models/internal.ts @@ -31,11 +31,13 @@ export interface AnonCredsSelectedCredentials { selfAttestedAttributes: Record } +export interface AnonCredsLinkSecretBlindingData { + v_prime: string + vr_prime: string | null +} + export interface AnonCredsCredentialRequestMetadata { - master_secret_blinding_data: { - v_prime: string - vr_prime: string | null - } - master_secret_name: string + link_secret_blinding_data: AnonCredsLinkSecretBlindingData + link_secret_name: string nonce: string } diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts index 74af775b10..80e5a596c9 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/__tests__/credentialExchangeRecord.test.ts @@ -45,7 +45,7 @@ describe('0.3.1-0.4.0 | AnonCreds Migration | Credential Exchange Record', () => getCredentialRecord({ metadata: { '_internal/indyCredential': { some: 'value' }, - '_internal/indyRequest': { another: 'value' }, + '_internal/indyRequest': { nonce: 'nonce', master_secret_name: 'ms', master_secret_blinding_data: 'msbd' }, }, credentials: [ { @@ -71,7 +71,7 @@ describe('0.3.1-0.4.0 | AnonCreds Migration | Credential Exchange Record', () => expect(credentialRecord.toJSON()).toMatchObject({ metadata: { '_anoncreds/credential': { some: 'value' }, - '_anoncreds/credentialRequest': { another: 'value' }, + '_anoncreds/credentialRequest': { nonce: 'nonce', link_secret_name: 'ms', link_secret_blinding_data: 'msbd' }, }, credentials: [ { @@ -92,7 +92,7 @@ describe('0.3.1-0.4.0 | AnonCreds Migration | Credential Exchange Record', () => const record = getCredentialRecord({ metadata: { '_internal/indyCredential': { some: 'value' }, - '_internal/indyRequest': { another: 'value' }, + '_internal/indyRequest': { nonce: 'nonce', master_secret_name: 'ms', master_secret_blinding_data: 'msbd' }, }, }) @@ -101,7 +101,7 @@ describe('0.3.1-0.4.0 | AnonCreds Migration | Credential Exchange Record', () => expect(record.toJSON()).toMatchObject({ metadata: { '_anoncreds/credential': { some: 'value' }, - '_anoncreds/credentialRequest': { another: 'value' }, + '_anoncreds/credentialRequest': { nonce: 'nonce', link_secret_name: 'ms', link_secret_blinding_data: 'msbd' }, }, }) }) diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts b/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts index 526270d81f..b181f42a61 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/credentialExchangeRecord.ts @@ -131,8 +131,11 @@ export function migrateIndyCredentialMetadataToAnonCredsMetadata in the indy-sdk, but the anoncreds package contains the correct type - options.credentialRequestMetadata as unknown as CredReqMetadata, + indySdkCredentialRequestMetadataFromAnonCreds(options.credentialRequestMetadata), options.credential, indySdkCredentialDefinitionFromAnonCreds(options.credentialDefinitionId, options.credentialDefinition), indyRevocationRegistryDefinition @@ -277,7 +276,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { return { credentialRequest: result[0], // The type is typed as a Record in the indy-sdk, but the anoncreds package contains the correct type - credentialRequestMetadata: result[1] as unknown as AnonCredsCredentialRequestMetadata, + credentialRequestMetadata: anonCredsCredentialRequestMetadataFromIndySdk(result[1]), } } catch (error) { agentContext.config.logger.error(`Error creating Indy Credential Request`, { diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index a993475349..06b4b16698 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -3,8 +3,10 @@ import type { AnonCredsRevocationStatusList, AnonCredsRevocationRegistryDefinition, AnonCredsSchema, + AnonCredsCredentialRequestMetadata, + AnonCredsLinkSecretBlindingData, } from '@aries-framework/anoncreds' -import type { CredDef, RevocReg, RevocRegDef, RevocRegDelta, Schema } from 'indy-sdk' +import type { CredDef, CredReqMetadata, RevocReg, RevocRegDef, RevocRegDelta, Schema } from 'indy-sdk' import { parseCredentialDefinitionId, parseSchemaId } from './identifiers' @@ -136,3 +138,23 @@ export function indySdkRevocationDeltaFromAnonCreds( ver: '1.0', } } + +export function anonCredsCredentialRequestMetadataFromIndySdk( + credentialRequestMetadata: CredReqMetadata +): AnonCredsCredentialRequestMetadata { + return { + link_secret_blinding_data: credentialRequestMetadata.master_secret_blinding_data as AnonCredsLinkSecretBlindingData, + link_secret_name: credentialRequestMetadata.master_secret_name as string, + nonce: credentialRequestMetadata.nonce as string, + } +} + +export function indySdkCredentialRequestMetadataFromAnonCreds( + credentialRequestMetadata: AnonCredsCredentialRequestMetadata +): CredReqMetadata { + return { + master_secret_blinding_data: credentialRequestMetadata.link_secret_blinding_data, + master_secret_name: credentialRequestMetadata.link_secret_name, + nonce: credentialRequestMetadata.nonce, + } +} diff --git a/yarn.lock b/yarn.lock index 10600e83b5..93024b56e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -881,12 +881,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.11": - version "0.1.0-dev.11" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.11.tgz#301b9bc5a4bb0235212ac48da2bf41118b407cdd" - integrity sha512-4BSHOGOdXjF4pyJuEjwk0iaSHeqt5UdXRXNv+u9VJ7yYhqM/aJZNhtUAgHXu8KGZwimFcFsp2e0FoLqwO0vLHQ== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.13": + version "0.1.0-dev.13" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.13.tgz#65b60be1c5ff077457ccd2f8298e71f198a8984f" + integrity sha512-W6Hoxp4lzcdv6yIQruK0CJDH52n79yQ8XCekFNmkUsXxpycB8Ts2M1o3KKRPa68AYM3CzmcN2Nw8pE7XqqEMyQ== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.11" + "@hyperledger/anoncreds-shared" "0.1.0-dev.13" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -894,10 +894,10 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.11", "@hyperledger/anoncreds-shared@^0.1.0-dev.11": - version "0.1.0-dev.11" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.11.tgz#206328cabcd855ef20c863ab5c2615a3a4c2502c" - integrity sha512-nK05y/qNtI3P+hnkVZW/d5oduMa7slZfEh2gQ+ZmAEmwHEcSU8iJ+QTkKS3nRE+6igXUvVAztlGS7JZHf21KKw== +"@hyperledger/anoncreds-shared@0.1.0-dev.13", "@hyperledger/anoncreds-shared@^0.1.0-dev.13": + version "0.1.0-dev.13" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.13.tgz#e78768366e6d7dd6e65839b769b857fbd828bce7" + integrity sha512-UtR2zCrugTa/Mu6LqAiEX1NJw1bdaf65wqcdS/k9efcq0iY1slQb+qg/KWEf+pZZFVa6NmkjAwmdyrzVbu9WTQ== "@hyperledger/aries-askar-nodejs@^0.1.0-dev.6": version "0.1.0-dev.6" From d9cfc7df6679d2008d66070a6c8a818440d066ab Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 1 Apr 2023 23:38:27 +0200 Subject: [PATCH 113/139] fix: various anoncreds revocation fixes (#1416) Signed-off-by: Timo Glastra --- .../src/services/AnonCredsRsHolderService.ts | 19 +- .../formats/AnonCredsProofFormatService.ts | 187 +--------------- .../formats/LegacyIndyProofFormatService.ts | 188 +--------------- packages/anoncreds/src/index.ts | 1 + .../__tests__/revocationInterval.test.ts | 12 +- .../src/utils/getRevocationRegistries.ts | 201 ++++++++++++++++++ packages/anoncreds/src/utils/index.ts | 3 +- .../anoncreds/src/utils/revocationInterval.ts | 18 +- .../services/IndySdkRevocationService.ts | 49 ++--- .../indy-sdk/src/anoncreds/utils/transform.ts | 5 +- 10 files changed, 272 insertions(+), 411 deletions(-) create mode 100644 packages/anoncreds/src/utils/getRevocationRegistries.ts diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index e5f9f1ef39..ea0c64d337 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -105,20 +105,23 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { throw new AnonCredsRsError(`Revocation Registry ${revocationRegistryDefinitionId} not found`) } - const { definition, tailsFilePath } = options.revocationRegistries[revocationRegistryDefinitionId] + const { definition, revocationStatusLists, tailsFilePath } = + options.revocationRegistries[revocationRegistryDefinitionId] + + // Extract revocation status list for the given timestamp + const revocationStatusList = revocationStatusLists[timestamp] + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Revocation status list for revocation registry ${revocationRegistryDefinitionId} and timestamp ${timestamp} not found in revocation status lists. All revocation status lists must be present.` + ) + } revocationRegistryDefinition = RevocationRegistryDefinition.fromJson(definition as unknown as JsonObject) revocationState = CredentialRevocationState.create({ revocationRegistryIndex: Number(revocationRegistryIndex), revocationRegistryDefinition, tailsPath: tailsFilePath, - revocationStatusList: RevocationStatusList.create({ - issuerId: definition.issuerId, - issuanceByDefault: true, - revocationRegistryDefinition, - revocationRegistryDefinitionId, - timestamp, - }), + revocationStatusList: RevocationStatusList.fromJson(revocationStatusList as unknown as JsonObject), }) } return { diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts index b8cf7afb64..001aebb340 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts @@ -15,13 +15,7 @@ import type { AnonCredsSelectedCredentials, AnonCredsProofRequest, } from '../models' -import type { - AnonCredsHolderService, - AnonCredsVerifierService, - CreateProofOptions, - GetCredentialsForProofRequestReturn, - VerifyProofOptions, -} from '../services' +import type { AnonCredsHolderService, AnonCredsVerifierService, GetCredentialsForProofRequestReturn } from '../services' import type { ProofFormatService, AgentContext, @@ -57,11 +51,12 @@ import { sortRequestedCredentialsMatches, createRequestFromPreview, areAnonCredsProofRequestsEqual, - assertRevocationInterval, - downloadTailsFile, + assertBestPracticeRevocationInterval, checkValidCredentialValueEncoding, encodeCredentialValue, assertNoDuplicateGroupsNamesInProofRequest, + getRevocationRegistriesForRequest, + getRevocationRegistriesForProof, } from '../utils' const ANONCREDS_PRESENTATION_PROPOSAL = 'anoncreds/proof-request@v1.0' @@ -240,7 +235,7 @@ export class AnonCredsProofFormatService implements ProofFormatService i.cred_def_id)) ) - const revocationRegistries = await this.getRevocationRegistriesForProof(agentContext, proofJson) + const revocationRegistries = await getRevocationRegistriesForProof(agentContext, proofJson) return await verifierService.verifyProof(agentContext, { proofRequest: proofRequestJson, @@ -538,7 +533,7 @@ export class AnonCredsProofFormatService implements ProofFormatService c.credentialDefinitionId)) ) - const revocationRegistries = await this.getRevocationRegistriesForRequest( + // selectedCredentials are overridden with specified timestamps of the revocation status list that + // should be used for the selected credentials. + const { revocationRegistries, updatedSelectedCredentials } = await getRevocationRegistriesForRequest( agentContext, proofRequest, selectedCredentials @@ -604,177 +601,13 @@ export class AnonCredsProofFormatService implements ProofFormatService i.cred_def_id)) ) - const revocationRegistries = await this.getRevocationRegistriesForProof(agentContext, proofJson) + const revocationRegistries = await getRevocationRegistriesForProof(agentContext, proofJson) return await verifierService.verifyProof(agentContext, { proofRequest: proofRequestJson, @@ -552,7 +547,7 @@ export class LegacyIndyProofFormatService implements ProofFormatService c.credentialDefinitionId)) ) - const revocationRegistries = await this.getRevocationRegistriesForRequest( + // selectedCredentials are overridden with specified timestamps of the revocation status list that + // should be used for the selected credentials. + const { revocationRegistries, updatedSelectedCredentials } = await getRevocationRegistriesForRequest( agentContext, proofRequest, selectedCredentials @@ -618,178 +615,13 @@ export class LegacyIndyProofFormatService implements ProofFormatService { +describe('assertBestPracticeRevocationInterval', () => { test("throws if no 'to' value is specified", () => { expect(() => - assertRevocationInterval({ + assertBestPracticeRevocationInterval({ from: 10, }) ).toThrow() @@ -11,7 +11,7 @@ describe('assertRevocationInterval', () => { test("throws if a 'from' value is specified and it is different from 'to'", () => { expect(() => - assertRevocationInterval({ + assertBestPracticeRevocationInterval({ to: 5, from: 10, }) @@ -20,7 +20,7 @@ describe('assertRevocationInterval', () => { test('does not throw if only to is provided', () => { expect(() => - assertRevocationInterval({ + assertBestPracticeRevocationInterval({ to: 5, }) ).not.toThrow() @@ -28,7 +28,7 @@ describe('assertRevocationInterval', () => { test('does not throw if from and to are equal', () => { expect(() => - assertRevocationInterval({ + assertBestPracticeRevocationInterval({ to: 10, from: 10, }) diff --git a/packages/anoncreds/src/utils/getRevocationRegistries.ts b/packages/anoncreds/src/utils/getRevocationRegistries.ts new file mode 100644 index 0000000000..ffc402d2a4 --- /dev/null +++ b/packages/anoncreds/src/utils/getRevocationRegistries.ts @@ -0,0 +1,201 @@ +import type { AnonCredsProof, AnonCredsProofRequest, AnonCredsSelectedCredentials } from '../models' +import type { CreateProofOptions, VerifyProofOptions } from '../services' +import type { AgentContext } from '@aries-framework/core' + +import { AriesFrameworkError } from '@aries-framework/core' + +import { AnonCredsRegistryService } from '../services' + +import { assertBestPracticeRevocationInterval } from './revocationInterval' +import { downloadTailsFile } from './tails' + +export async function getRevocationRegistriesForRequest( + agentContext: AgentContext, + proofRequest: AnonCredsProofRequest, + selectedCredentials: AnonCredsSelectedCredentials +) { + const revocationRegistries: CreateProofOptions['revocationRegistries'] = {} + + // NOTE: we don't want to mutate this object, when modifying we need to always deeply clone objects firsts. + let updatedSelectedCredentials = selectedCredentials + + try { + agentContext.config.logger.debug(`Retrieving revocation registries for proof request`, { + proofRequest, + selectedCredentials, + }) + + const referentCredentials = [] + + // Retrieve information for referents and push to single array + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.attributes)) { + referentCredentials.push({ + type: 'attributes' as const, + referent, + selectedCredential, + nonRevoked: proofRequest.requested_attributes[referent].non_revoked ?? proofRequest.non_revoked, + }) + } + for (const [referent, selectedCredential] of Object.entries(selectedCredentials.predicates)) { + referentCredentials.push({ + type: 'predicates' as const, + referent, + selectedCredential, + nonRevoked: proofRequest.requested_predicates[referent].non_revoked ?? proofRequest.non_revoked, + }) + } + + for (const { referent, selectedCredential, nonRevoked, type } of referentCredentials) { + if (!selectedCredential.credentialInfo) { + throw new AriesFrameworkError( + `Credential for referent '${referent} does not have credential info for revocation state creation` + ) + } + + // Prefer referent-specific revocation interval over global revocation interval + const credentialRevocationId = selectedCredential.credentialInfo.credentialRevocationId + const revocationRegistryId = selectedCredential.credentialInfo.revocationRegistryId + const timestamp = selectedCredential.timestamp + + // If revocation interval is present and the credential is revocable then create revocation state + if (nonRevoked && credentialRevocationId && revocationRegistryId) { + agentContext.config.logger.trace( + `Presentation is requesting proof of non revocation for referent '${referent}', creating revocation state for credential`, + { + nonRevoked, + credentialRevocationId, + revocationRegistryId, + timestamp, + } + ) + + // Make sure the revocation interval follows best practices from Aries RFC 0441 + assertBestPracticeRevocationInterval(nonRevoked) + + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, revocationRegistryId) + + // Fetch revocation registry definition if not in revocation registries list yet + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId + ) + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } + + const { tailsLocation, tailsHash } = revocationRegistryDefinition.value + const { tailsFilePath } = await downloadTailsFile(agentContext, tailsLocation, tailsHash) + + // const tails = await this.indyUtilitiesService.downloadTails(tailsHash, tailsLocation) + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + tailsFilePath, + revocationStatusLists: {}, + } + } + + // In most cases we will have a timestamp, but if it's not defined, we use the nonRevoked.to value + const timestampToFetch = timestamp ?? nonRevoked.to + + // Fetch revocation status list if we don't already have a revocation status list for the given timestamp + if (!revocationRegistries[revocationRegistryId].revocationStatusLists[timestampToFetch]) { + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestampToFetch) + + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId].revocationStatusLists[revocationStatusList.timestamp] = + revocationStatusList + + // If we don't have a timestamp on the selected credential, we set it to the timestamp of the revocation status list + // this way we know which revocation status list to use when creating the proof. + if (!timestamp) { + updatedSelectedCredentials = { + ...updatedSelectedCredentials, + [type]: { + ...updatedSelectedCredentials[type], + [referent]: { + ...updatedSelectedCredentials[type][referent], + timestamp: revocationStatusList.timestamp, + }, + }, + } + } + } + } + } + + agentContext.config.logger.debug(`Retrieved revocation registries for proof request`, { + revocationRegistries, + }) + + return { revocationRegistries, updatedSelectedCredentials } + } catch (error) { + agentContext.config.logger.error(`Error retrieving revocation registry for proof request`, { + error, + proofRequest, + selectedCredentials, + }) + + throw error + } +} + +export async function getRevocationRegistriesForProof(agentContext: AgentContext, proof: AnonCredsProof) { + const revocationRegistries: VerifyProofOptions['revocationRegistries'] = {} + + for (const identifier of proof.identifiers) { + const revocationRegistryId = identifier.rev_reg_id + const timestamp = identifier.timestamp + + // Skip if no revocation registry id is present + if (!revocationRegistryId || !timestamp) continue + + const registry = agentContext.dependencyManager + .resolve(AnonCredsRegistryService) + .getRegistryForIdentifier(agentContext, revocationRegistryId) + + // Fetch revocation registry definition if not already fetched + if (!revocationRegistries[revocationRegistryId]) { + const { revocationRegistryDefinition, resolutionMetadata } = await registry.getRevocationRegistryDefinition( + agentContext, + revocationRegistryId + ) + if (!revocationRegistryDefinition) { + throw new AriesFrameworkError( + `Could not retrieve revocation registry definition for revocation registry ${revocationRegistryId}: ${resolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId] = { + definition: revocationRegistryDefinition, + revocationStatusLists: {}, + } + } + + // Fetch revocation status list by timestamp if not already fetched + if (!revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp]) { + const { revocationStatusList, resolutionMetadata: statusListResolutionMetadata } = + await registry.getRevocationStatusList(agentContext, revocationRegistryId, timestamp) + + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Could not retrieve revocation status list for revocation registry ${revocationRegistryId}: ${statusListResolutionMetadata.message}` + ) + } + + revocationRegistries[revocationRegistryId].revocationStatusLists[timestamp] = revocationStatusList + } + } + + return revocationRegistries +} diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts index 2de326adf2..7fa0da87ed 100644 --- a/packages/anoncreds/src/utils/index.ts +++ b/packages/anoncreds/src/utils/index.ts @@ -3,7 +3,8 @@ export { sortRequestedCredentialsMatches } from './sortRequestedCredentialsMatch export { assertNoDuplicateGroupsNamesInProofRequest } from './hasDuplicateGroupNames' export { areAnonCredsProofRequestsEqual } from './areRequestsEqual' export { downloadTailsFile } from './tails' -export { assertRevocationInterval } from './revocationInterval' +export { assertBestPracticeRevocationInterval } from './revocationInterval' +export { getRevocationRegistriesForRequest, getRevocationRegistriesForProof } from './getRevocationRegistries' export { encodeCredentialValue, checkValidCredentialValueEncoding } from './credential' export { IsMap } from './isMap' export { composeCredentialAutoAccept, composeProofAutoAccept } from './composeAutoAccept' diff --git a/packages/anoncreds/src/utils/revocationInterval.ts b/packages/anoncreds/src/utils/revocationInterval.ts index caf40b93c1..59f490f569 100644 --- a/packages/anoncreds/src/utils/revocationInterval.ts +++ b/packages/anoncreds/src/utils/revocationInterval.ts @@ -2,16 +2,24 @@ import type { AnonCredsNonRevokedInterval } from '../models' import { AriesFrameworkError } from '@aries-framework/core' -// TODO: Add Test +// This sets the `to` value to be required. We do this check in the `assertBestPracticeRevocationInterval` method, +// and it makes it easier to work with the object in TS +interface BestPracticeNonRevokedInterval { + from?: number + to: number +} + // Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints -export function assertRevocationInterval(nonRevokedInterval: AnonCredsNonRevokedInterval) { - if (!nonRevokedInterval.to) { +export function assertBestPracticeRevocationInterval( + revocationInterval: AnonCredsNonRevokedInterval +): asserts revocationInterval is BestPracticeNonRevokedInterval { + if (!revocationInterval.to) { throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) } - if ((nonRevokedInterval.from || nonRevokedInterval.from === 0) && nonRevokedInterval.to !== nonRevokedInterval.from) { + if ((revocationInterval.from || revocationInterval.from === 0) && revocationInterval.to !== revocationInterval.from) { throw new AriesFrameworkError( - `Presentation requests proof of non-revocation with an interval from: '${nonRevokedInterval.from}' that does not match the interval to: '${nonRevokedInterval.to}', as specified in Aries RFC 0441` + `Presentation requests proof of non-revocation with an interval from: '${revocationInterval.from}' that does not match the interval to: '${revocationInterval.to}', as specified in Aries RFC 0441` ) } } diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts index ed2572dee7..6690fb6ab3 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkRevocationService.ts @@ -9,6 +9,7 @@ import type { import type { AgentContext } from '@aries-framework/core' import type { RevStates } from 'indy-sdk' +import { assertBestPracticeRevocationInterval } from '@aries-framework/anoncreds' import { AriesFrameworkError, inject, injectable } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' @@ -67,6 +68,7 @@ export class IndySdkRevocationService { referent: string credentialInfo: AnonCredsCredentialInfo referentRevocationInterval: AnonCredsNonRevokedInterval | undefined + timestamp: number | undefined }> = [] //Retrieve information for referents and push to single array @@ -76,6 +78,7 @@ export class IndySdkRevocationService { credentialInfo: selectedCredential.credentialInfo, type: RequestReferentType.Attribute, referentRevocationInterval: proofRequest.requested_attributes[referent].non_revoked, + timestamp: selectedCredential.timestamp, }) } for (const [referent, selectedCredential] of Object.entries(selectedCredentials.predicates ?? {})) { @@ -84,17 +87,18 @@ export class IndySdkRevocationService { credentialInfo: selectedCredential.credentialInfo, type: RequestReferentType.Predicate, referentRevocationInterval: proofRequest.requested_predicates[referent].non_revoked, + timestamp: selectedCredential.timestamp, }) } - for (const { referent, credentialInfo, type, referentRevocationInterval } of referentCredentials) { + for (const { referent, credentialInfo, type, referentRevocationInterval, timestamp } of referentCredentials) { // Prefer referent-specific revocation interval over global revocation interval const requestRevocationInterval = referentRevocationInterval ?? proofRequest.non_revoked const credentialRevocationId = credentialInfo.credentialRevocationId const revocationRegistryId = credentialInfo.revocationRegistryId // If revocation interval is present and the credential is revocable then create revocation state - if (requestRevocationInterval && credentialRevocationId && revocationRegistryId) { + if (requestRevocationInterval && timestamp && credentialRevocationId && revocationRegistryId) { agentContext.config.logger.trace( `Presentation is requesting proof of non revocation for ${type} referent '${referent}', creating revocation state for credential`, { @@ -104,12 +108,17 @@ export class IndySdkRevocationService { } ) - this.assertRevocationInterval(requestRevocationInterval) + assertBestPracticeRevocationInterval(requestRevocationInterval) const { definition, revocationStatusLists, tailsFilePath } = revocationRegistries[revocationRegistryId] - // NOTE: we assume that the revocationStatusLists have been added based on timestamps of the `to` query. On a higher level it means we'll find the - // most accurate revocation list for a given timestamp. It doesn't have to be that the revocationStatusList is from the `to` timestamp however. - const revocationStatusList = revocationStatusLists[requestRevocationInterval.to] + + // Extract revocation status list for the given timestamp + const revocationStatusList = revocationStatusLists[timestamp] + if (!revocationStatusList) { + throw new AriesFrameworkError( + `Revocation status list for revocation registry ${revocationRegistryId} and timestamp ${timestamp} not found in revocation status lists. All revocation status lists must be present.` + ) + } const tails = await createTailsReader(agentContext, tailsFilePath) @@ -120,7 +129,6 @@ export class IndySdkRevocationService { revocationStatusList.timestamp, credentialRevocationId ) - const timestamp = revocationState.timestamp if (!indyRevocationStates[revocationRegistryId]) { indyRevocationStates[revocationRegistryId] = {} @@ -144,31 +152,4 @@ export class IndySdkRevocationService { throw isIndyError(error) ? new IndySdkError(error) : error } } - - // TODO: Add Test - // TODO: we should do this verification on a higher level I think? - // Check revocation interval in accordance with https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0441-present-proof-best-practices/README.md#semantics-of-non-revocation-interval-endpoints - private assertRevocationInterval( - revocationInterval: AnonCredsNonRevokedInterval - ): asserts revocationInterval is BestPracticeNonRevokedInterval { - if (!revocationInterval.to) { - throw new AriesFrameworkError(`Presentation requests proof of non-revocation with no 'to' value specified`) - } - - if ( - (revocationInterval.from || revocationInterval.from === 0) && - revocationInterval.to !== revocationInterval.from - ) { - throw new AriesFrameworkError( - `Presentation requests proof of non-revocation with an interval from: '${revocationInterval.from}' that does not match the interval to: '${revocationInterval.to}', as specified in Aries RFC 0441` - ) - } - } -} - -// This sets the `to` value to be required. We do this check in the `assertRevocationInterval` method, -// and it makes it easier to work with the object in TS -interface BestPracticeNonRevokedInterval { - from?: number - to: number } diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index 06b4b16698..e7eac06ecc 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -132,8 +132,9 @@ export function indySdkRevocationDeltaFromAnonCreds( accum: revocationStatusList.currentAccumulator, issued: [], revoked: revokedIndices, - // NOTE: I don't think this is used? - prevAccum: '', + // NOTE: this must be a valid accumulator but it's not actually used. So we set it to the + // currentAccumulator as that should always be a valid accumulator. + prevAccum: revocationStatusList.currentAccumulator, }, ver: '1.0', } From c46a6b81b8a1e28e05013c27ffe2eeaee4724130 Mon Sep 17 00:00:00 2001 From: NB-MikeRichardson <93971245+NB-MikeRichardson@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:03:09 +0300 Subject: [PATCH 114/139] fix: jsonld credential format identifier version (#1412) --- .../tests/v2.ldproof.credentials.propose-offerBbs.test.ts | 2 +- .../formats/jsonld/JsonLdCredentialFormatService.ts | 2 +- .../jsonld/__tests__/JsonLdCredentialFormatService.test.ts | 2 +- .../v2.ldproof.credentials.propose-offerED25519.test.ts | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index 2205fde9b0..78d06d862a 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -250,7 +250,7 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { formats: [ { attach_id: expect.any(String), - format: 'aries/ld-proof-vc@1.0', + format: 'aries/ld-proof-vc@v1.0', }, ], 'credentials~attach': [ diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index 0b49134c0f..d598b68fe9 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -37,7 +37,7 @@ import { CredentialFormatSpec } from '../../models/CredentialFormatSpec' import { JsonLdCredentialDetail } from './JsonLdCredentialDetail' const JSONLD_VC_DETAIL = 'aries/ld-proof-vc-detail@v1.0' -const JSONLD_VC = 'aries/ld-proof-vc@1.0' +const JSONLD_VC = 'aries/ld-proof-vc@v1.0' export class JsonLdCredentialFormatService implements CredentialFormatService { public readonly formatKey = 'jsonld' as const diff --git a/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts b/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts index dfa001c528..0c3abd80e9 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/__tests__/JsonLdCredentialFormatService.test.ts @@ -341,7 +341,7 @@ describe('JsonLd CredentialFormatService', () => { }) expect(format).toMatchObject({ attachmentId: expect.any(String), - format: 'aries/ld-proof-vc@1.0', + format: 'aries/ld-proof-vc@v1.0', }) }) }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 6c514127ce..3cae9182d1 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -322,7 +322,7 @@ describe('V2 Credentials - JSON-LD - Ed25519', () => { formats: [ { attach_id: expect.any(String), - format: 'aries/ld-proof-vc@1.0', + format: 'aries/ld-proof-vc@v1.0', }, ], 'credentials~attach': [ @@ -593,7 +593,7 @@ describe('V2 Credentials - JSON-LD - Ed25519', () => { }, { attach_id: expect.any(String), - format: 'aries/ld-proof-vc@1.0', + format: 'aries/ld-proof-vc@v1.0', }, ], 'credentials~attach': [ From c888736cb6b51014e23f5520fbc4074cf0e49e15 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Tue, 4 Apr 2023 15:49:53 +0200 Subject: [PATCH 115/139] feat(core): add W3cCredentialsApi Add an API class to the `W3cCredentialsModule` that allows for CRUD operations for W3C credentials. --- .../tests/bbs-signatures.e2e.test.ts | 4 +- packages/core/src/agent/AgentModules.ts | 6 +- packages/core/src/agent/BaseAgent.ts | 4 + .../src/agent/__tests__/AgentModules.test.ts | 10 +- ...f.credentials.propose-offerED25519.test.ts | 4 +- .../src/modules/vc/W3cCredentialService.ts | 24 ++--- .../core/src/modules/vc/W3cCredentialsApi.ts | 42 +++++++++ ...W3cVcModule.ts => W3cCredentialsModule.ts} | 18 ++-- ...onfig.ts => W3cCredentialsModuleConfig.ts} | 2 +- ...le.test.ts => W3CredentialsModule.test.ts} | 12 ++- .../vc/__tests__/W3cCredentialService.test.ts | 60 ++++++------ .../vc/__tests__/W3cCredentialsApi.test.ts | 91 +++++++++++++++++++ ....ts => W3cCredentialsModuleConfig.test.ts} | 8 +- packages/core/src/modules/vc/index.ts | 2 +- packages/core/src/modules/vc/models/index.ts | 1 + packages/core/tests/jsonld.ts | 4 +- .../tests/openid4vc-client.e2e.test.ts | 5 +- 17 files changed, 222 insertions(+), 75 deletions(-) create mode 100644 packages/core/src/modules/vc/W3cCredentialsApi.ts rename packages/core/src/modules/vc/{W3cVcModule.ts => W3cCredentialsModule.ts} (69%) rename packages/core/src/modules/vc/{W3cVcModuleConfig.ts => W3cCredentialsModuleConfig.ts} (96%) rename packages/core/src/modules/vc/__tests__/{W3cVcModule.test.ts => W3CredentialsModule.test.ts} (77%) create mode 100644 packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts rename packages/core/src/modules/vc/__tests__/{W3cVcModuleConfig.test.ts => W3cCredentialsModuleConfig.test.ts} (59%) diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index 0ee5b85036..d9cbd95f91 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -22,7 +22,7 @@ import { } from '@aries-framework/core' import { SignatureSuiteRegistry } from '../../core/src/modules/vc/SignatureSuiteRegistry' -import { W3cVcModuleConfig } from '../../core/src/modules/vc/W3cVcModuleConfig' +import { W3cCredentialsModuleConfig } from '../../core/src/modules/vc/W3cCredentialsModuleConfig' import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/documentLoader' import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' import { IndySdkWallet } from '../../indy-sdk/src' @@ -77,7 +77,7 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { w3cCredentialService = new W3cCredentialService( {} as unknown as W3cCredentialRepository, signatureSuiteRegistry, - new W3cVcModuleConfig({ + new W3cCredentialsModuleConfig({ documentLoader: customDocumentLoader, }) ) diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index c4e8dec0d8..805a147918 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -12,8 +12,8 @@ import { GenericRecordsModule } from '../modules/generic-records' import { MessagePickupModule } from '../modules/message-pìckup' import { OutOfBandModule } from '../modules/oob' import { ProofsModule } from '../modules/proofs' -import { MediatorModule, MediationRecipientModule } from '../modules/routing' -import { W3cVcModule } from '../modules/vc' +import { MediationRecipientModule, MediatorModule } from '../modules/routing' +import { W3cCredentialsModule } from '../modules/vc' import { WalletModule } from '../wallet' /** @@ -129,7 +129,7 @@ function getDefaultAgentModules() { dids: () => new DidsModule(), wallet: () => new WalletModule(), oob: () => new OutOfBandModule(), - w3cVc: () => new W3cVcModule(), + w3cCredentials: () => new W3cCredentialsModule(), cache: () => new CacheModule(), } as const } diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index 447c22d677..ae9e6656ba 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -18,6 +18,7 @@ import { MessagePickupApi } from '../modules/message-pìckup/MessagePickupApi' import { OutOfBandApi } from '../modules/oob' import { ProofsApi } from '../modules/proofs' import { MediatorApi, MediationRecipientApi } from '../modules/routing' +import { W3cCredentialsApi } from '../modules/vc/W3cCredentialsApi' import { StorageUpdateService } from '../storage' import { UpdateAssistant } from '../storage/migration/UpdateAssistant' import { DEFAULT_UPDATE_CONFIG } from '../storage/migration/updates' @@ -56,6 +57,7 @@ export abstract class BaseAgent> @@ -103,6 +105,7 @@ export abstract class BaseAgent { dids: expect.any(DidsModule), wallet: expect.any(WalletModule), oob: expect.any(OutOfBandModule), - w3cVc: expect.any(W3cVcModule), + w3cCredentials: expect.any(W3cCredentialsModule), cache: expect.any(CacheModule), }) }) @@ -91,7 +91,7 @@ describe('AgentModules', () => { dids: expect.any(DidsModule), wallet: expect.any(WalletModule), oob: expect.any(OutOfBandModule), - w3cVc: expect.any(W3cVcModule), + w3cCredentials: expect.any(W3cCredentialsModule), cache: expect.any(CacheModule), myModule, }) @@ -118,7 +118,7 @@ describe('AgentModules', () => { dids: expect.any(DidsModule), wallet: expect.any(WalletModule), oob: expect.any(OutOfBandModule), - w3cVc: expect.any(W3cVcModule), + w3cCredentials: expect.any(W3cCredentialsModule), cache: expect.any(CacheModule), myModule, }) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 3cae9182d1..7f5abe619f 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -36,7 +36,7 @@ import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { CacheModule, InMemoryLruCache } from '../../../../cache' import { DidsModule, KeyDidRegistrar, KeyDidResolver } from '../../../../dids' import { ProofEventTypes, ProofsModule, V2ProofProtocol } from '../../../../proofs' -import { W3cVcModule } from '../../../../vc' +import { W3cCredentialsModule } from '../../../../vc' import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader' import { CredentialEventTypes } from '../../../CredentialEvents' import { CredentialsModule } from '../../../CredentialsModule' @@ -126,7 +126,7 @@ const getIndyJsonLdModules = () => cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 100 }), }), - w3cVc: new W3cVcModule({ + w3cVc: new W3cCredentialsModule({ documentLoader: customDocumentLoader, }), } as const) diff --git a/packages/core/src/modules/vc/W3cCredentialService.ts b/packages/core/src/modules/vc/W3cCredentialService.ts index fc9d454fac..214911d02c 100644 --- a/packages/core/src/modules/vc/W3cCredentialService.ts +++ b/packages/core/src/modules/vc/W3cCredentialService.ts @@ -21,7 +21,7 @@ import { VerificationMethod } from '../dids' import { getKeyFromVerificationMethod } from '../dids/domain/key-type' import { SignatureSuiteRegistry } from './SignatureSuiteRegistry' -import { W3cVcModuleConfig } from './W3cVcModuleConfig' +import { W3cCredentialsModuleConfig } from './W3cCredentialsModuleConfig' import { deriveProof } from './deriveProof' import { orArrayToArray, w3cDate } from './jsonldUtil' import jsonld from './libraries/jsonld' @@ -35,16 +35,16 @@ import { W3cCredentialRecord, W3cCredentialRepository } from './repository' export class W3cCredentialService { private w3cCredentialRepository: W3cCredentialRepository private signatureSuiteRegistry: SignatureSuiteRegistry - private w3cVcModuleConfig: W3cVcModuleConfig + private w3cCredentialsModuleConfig: W3cCredentialsModuleConfig public constructor( w3cCredentialRepository: W3cCredentialRepository, signatureSuiteRegistry: SignatureSuiteRegistry, - w3cVcModuleConfig: W3cVcModuleConfig + w3cCredentialsModuleConfig: W3cCredentialsModuleConfig ) { this.w3cCredentialRepository = w3cCredentialRepository this.signatureSuiteRegistry = signatureSuiteRegistry - this.w3cVcModuleConfig = w3cVcModuleConfig + this.w3cCredentialsModuleConfig = w3cCredentialsModuleConfig } /** @@ -89,7 +89,7 @@ export class W3cCredentialService { credential: JsonTransformer.toJSON(options.credential), suite: suite, purpose: options.proofPurpose, - documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), }) return JsonTransformer.fromJSON(result, W3cVerifiableCredential) @@ -112,7 +112,7 @@ export class W3cCredentialService { const verifyOptions: Record = { credential: JsonTransformer.toJSON(options.credential), suite: suites, - documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), checkStatus: () => { if (verifyRevocationState) { throw new AriesFrameworkError('Revocation for W3C credentials is currently not supported') @@ -182,7 +182,7 @@ export class W3cCredentialService { throw new AriesFrameworkError('The key type of the verification method does not match the suite') } - const documentLoader = this.w3cVcModuleConfig.documentLoader(agentContext) + const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext) const verificationMethodObject = (await documentLoader(options.verificationMethod)).document as Record< string, unknown @@ -209,7 +209,7 @@ export class W3cCredentialService { presentation: JsonTransformer.toJSON(options.presentation), suite: suite, challenge: options.challenge, - documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), }) return JsonTransformer.fromJSON(result, W3cVerifiablePresentation) @@ -262,7 +262,7 @@ export class W3cCredentialService { presentation: JsonTransformer.toJSON(options.presentation), suite: allSuites, challenge: options.challenge, - documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), } // this is a hack because vcjs throws if purpose is passed as undefined or null @@ -284,7 +284,7 @@ export class W3cCredentialService { const proof = await deriveProof(JsonTransformer.toJSON(options.credential), options.revealDocument, { suite: suite, - documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), }) return proof @@ -294,7 +294,7 @@ export class W3cCredentialService { agentContext: AgentContext, verificationMethod: string ): Promise { - const documentLoader = this.w3cVcModuleConfig.documentLoader(agentContext) + const documentLoader = this.w3cCredentialsModuleConfig.documentLoader(agentContext) const verificationMethodObject = await documentLoader(verificationMethod) const verificationMethodClass = JsonTransformer.fromJSON(verificationMethodObject.document, VerificationMethod) @@ -315,7 +315,7 @@ export class W3cCredentialService { // Get the expanded types const expandedTypes = ( await jsonld.expand(JsonTransformer.toJSON(options.credential), { - documentLoader: this.w3cVcModuleConfig.documentLoader(agentContext), + documentLoader: this.w3cCredentialsModuleConfig.documentLoader(agentContext), }) )[0]['@type'] diff --git a/packages/core/src/modules/vc/W3cCredentialsApi.ts b/packages/core/src/modules/vc/W3cCredentialsApi.ts new file mode 100644 index 0000000000..8f18f2f459 --- /dev/null +++ b/packages/core/src/modules/vc/W3cCredentialsApi.ts @@ -0,0 +1,42 @@ +import type { W3cVerifiableCredential, StoreCredentialOptions } from './models' +import type { W3cCredentialRecord } from './repository' +import type { Query } from '../../storage/StorageService' + +import { AgentContext } from '../../agent' +import { injectable } from '../../plugins' + +import { W3cCredentialService } from './W3cCredentialService' + +/** + * @public + */ +@injectable() +export class W3cCredentialsApi { + private agentContext: AgentContext + private w3cCredentialService: W3cCredentialService + + public constructor(agentContext: AgentContext, w3cCredentialService: W3cCredentialService) { + this.agentContext = agentContext + this.w3cCredentialService = w3cCredentialService + } + + public async storeCredential(options: StoreCredentialOptions): Promise { + return this.w3cCredentialService.storeCredential(this.agentContext, options) + } + + public async removeCredentialRecord(id: string) { + return this.w3cCredentialService.removeCredentialRecord(this.agentContext, id) + } + + public async getAllCredentialRecords(): Promise { + return this.w3cCredentialService.getAllCredentialRecords(this.agentContext) + } + + public async getCredentialRecordById(id: string): Promise { + return this.w3cCredentialService.getCredentialRecordById(this.agentContext, id) + } + + public async findCredentialRecordsByQuery(query: Query): Promise { + return this.w3cCredentialService.findCredentialsByQuery(this.agentContext, query) + } +} diff --git a/packages/core/src/modules/vc/W3cVcModule.ts b/packages/core/src/modules/vc/W3cCredentialsModule.ts similarity index 69% rename from packages/core/src/modules/vc/W3cVcModule.ts rename to packages/core/src/modules/vc/W3cCredentialsModule.ts index a793da49f4..70b93e81b6 100644 --- a/packages/core/src/modules/vc/W3cVcModule.ts +++ b/packages/core/src/modules/vc/W3cCredentialsModule.ts @@ -1,4 +1,4 @@ -import type { W3cVcModuleConfigOptions } from './W3cVcModuleConfig' +import type { W3cVcModuleConfigOptions } from './W3cCredentialsModuleConfig' import type { DependencyManager, Module } from '../../plugins' import { KeyType } from '../../crypto' @@ -9,25 +9,31 @@ import { import { SignatureSuiteRegistry, SignatureSuiteToken } from './SignatureSuiteRegistry' import { W3cCredentialService } from './W3cCredentialService' -import { W3cVcModuleConfig } from './W3cVcModuleConfig' +import { W3cCredentialsApi } from './W3cCredentialsApi' +import { W3cCredentialsModuleConfig } from './W3cCredentialsModuleConfig' import { W3cCredentialRepository } from './repository/W3cCredentialRepository' import { Ed25519Signature2018 } from './signature-suites' -export class W3cVcModule implements Module { - public readonly config: W3cVcModuleConfig +/** + * @public + */ +export class W3cCredentialsModule implements Module { + public readonly config: W3cCredentialsModuleConfig + public readonly api = W3cCredentialsApi public constructor(config?: W3cVcModuleConfigOptions) { - this.config = new W3cVcModuleConfig(config) + this.config = new W3cCredentialsModuleConfig(config) } public register(dependencyManager: DependencyManager) { + dependencyManager.registerContextScoped(W3cCredentialsApi) dependencyManager.registerSingleton(W3cCredentialService) dependencyManager.registerSingleton(W3cCredentialRepository) dependencyManager.registerSingleton(SignatureSuiteRegistry) // Register the config - dependencyManager.registerInstance(W3cVcModuleConfig, this.config) + dependencyManager.registerInstance(W3cCredentialsModuleConfig, this.config) // Always register ed25519 signature suite dependencyManager.registerInstance(SignatureSuiteToken, { diff --git a/packages/core/src/modules/vc/W3cVcModuleConfig.ts b/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts similarity index 96% rename from packages/core/src/modules/vc/W3cVcModuleConfig.ts rename to packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts index 2c05a446a9..c9b6fbdd2f 100644 --- a/packages/core/src/modules/vc/W3cVcModuleConfig.ts +++ b/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts @@ -32,7 +32,7 @@ export interface W3cVcModuleConfigOptions { documentLoader?: DocumentLoaderWithContext } -export class W3cVcModuleConfig { +export class W3cCredentialsModuleConfig { private options: W3cVcModuleConfigOptions public constructor(options?: W3cVcModuleConfigOptions) { diff --git a/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts b/packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts similarity index 77% rename from packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts rename to packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts index 873c8274e6..b0cdc58451 100644 --- a/packages/core/src/modules/vc/__tests__/W3cVcModule.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3CredentialsModule.test.ts @@ -2,8 +2,9 @@ import { KeyType } from '../../../crypto' import { DependencyManager } from '../../../plugins/DependencyManager' import { SignatureSuiteRegistry, SignatureSuiteToken } from '../SignatureSuiteRegistry' import { W3cCredentialService } from '../W3cCredentialService' -import { W3cVcModule } from '../W3cVcModule' -import { W3cVcModuleConfig } from '../W3cVcModuleConfig' +import { W3cCredentialsApi } from '../W3cCredentialsApi' +import { W3cCredentialsModule } from '../W3cCredentialsModule' +import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' import { W3cCredentialRepository } from '../repository' import { Ed25519Signature2018 } from '../signature-suites' @@ -12,19 +13,20 @@ const DependencyManagerMock = DependencyManager as jest.Mock const dependencyManager = new DependencyManagerMock() -describe('W3cVcModule', () => { +describe('W3cCredentialsModule', () => { test('registers dependencies on the dependency manager', () => { - const module = new W3cVcModule() + const module = new W3cCredentialsModule() module.register(dependencyManager) expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) + expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(W3cCredentialsApi) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialService) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(W3cCredentialRepository) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(SignatureSuiteRegistry) expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(2) - expect(dependencyManager.registerInstance).toHaveBeenCalledWith(W3cVcModuleConfig, module.config) + expect(dependencyManager.registerInstance).toHaveBeenCalledWith(W3cCredentialsModuleConfig, module.config) expect(dependencyManager.registerInstance).toHaveBeenCalledWith(SignatureSuiteToken, { suiteClass: Ed25519Signature2018, diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index f0f08fe07c..8322898fc9 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -16,7 +16,7 @@ import { } from '../../dids/domain/key-type/ed25519' import { SignatureSuiteRegistry } from '../SignatureSuiteRegistry' import { W3cCredentialService } from '../W3cCredentialService' -import { W3cVcModuleConfig } from '../W3cVcModuleConfig' +import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' import { orArrayToArray } from '../jsonldUtil' import jsonld from '../libraries/jsonld' import { W3cCredential, W3cVerifiableCredential } from '../models' @@ -46,7 +46,7 @@ const signatureSuiteRegistry = new SignatureSuiteRegistry([ const signingProviderRegistry = new SigningProviderRegistry([]) jest.mock('../repository/W3cCredentialRepository') -const W3cCredentialRepositoryMock = W3cCredentialRepository as jest.Mock +const W3cCredentialsRepositoryMock = W3cCredentialRepository as jest.Mock const agentConfig = getAgentConfig('W3cCredentialServiceTest') @@ -63,11 +63,11 @@ const credentialRecordFactory = async (credential: W3cVerifiableCredential) => { }) } -describe('W3cCredentialService', () => { +describe('W3cCredentialsService', () => { let wallet: Wallet let agentContext: AgentContext - let w3cCredentialService: W3cCredentialService - let w3cCredentialRepository: W3cCredentialRepository + let w3cCredentialsService: W3cCredentialService + let w3cCredentialsRepository: W3cCredentialRepository const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { @@ -77,11 +77,11 @@ describe('W3cCredentialService', () => { agentConfig, wallet, }) - w3cCredentialRepository = new W3cCredentialRepositoryMock() - w3cCredentialService = new W3cCredentialService( - w3cCredentialRepository, + w3cCredentialsRepository = new W3cCredentialsRepositoryMock() + w3cCredentialsService = new W3cCredentialService( + w3cCredentialsRepository, signatureSuiteRegistry, - new W3cVcModuleConfig({ + new W3cCredentialsModuleConfig({ documentLoader: customDocumentLoader, }) ) @@ -94,7 +94,7 @@ describe('W3cCredentialService', () => { describe('Utility methods', () => { describe('getKeyTypesByProofType', () => { it('should return the correct key types for Ed25519Signature2018 proof type', async () => { - const keyTypes = w3cCredentialService.getKeyTypesByProofType('Ed25519Signature2018') + const keyTypes = w3cCredentialsService.getKeyTypesByProofType('Ed25519Signature2018') expect(keyTypes).toEqual([KeyType.Ed25519]) }) }) @@ -102,7 +102,7 @@ describe('W3cCredentialService', () => { describe('getVerificationMethodTypesByProofType', () => { it('should return the correct key types for Ed25519Signature2018 proof type', async () => { const verificationMethodTypes = - w3cCredentialService.getVerificationMethodTypesByProofType('Ed25519Signature2018') + w3cCredentialsService.getVerificationMethodTypesByProofType('Ed25519Signature2018') expect(verificationMethodTypes).toEqual([ VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, @@ -129,7 +129,7 @@ describe('W3cCredentialService', () => { const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) - const vc = await w3cCredentialService.signCredential(agentContext, { + const vc = await w3cCredentialsService.signCredential(agentContext, { credential, proofType: 'Ed25519Signature2018', verificationMethod: verificationMethod, @@ -151,7 +151,7 @@ describe('W3cCredentialService', () => { const credential = JsonTransformer.fromJSON(credentialJson, W3cCredential) expect(async () => { - await w3cCredentialService.signCredential(agentContext, { + await w3cCredentialsService.signCredential(agentContext, { credential, proofType: 'Ed25519Signature2018', verificationMethod: @@ -166,7 +166,7 @@ describe('W3cCredentialService', () => { Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, W3cVerifiableCredential ) - const result = await w3cCredentialService.verifyCredential(agentContext, { credential: vc }) + const result = await w3cCredentialsService.verifyCredential(agentContext, { credential: vc }) expect(result.verified).toBe(true) expect(result.error).toBeUndefined() @@ -181,7 +181,7 @@ describe('W3cCredentialService', () => { Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_BAD_SIGNED, W3cVerifiableCredential ) - const result = await w3cCredentialService.verifyCredential(agentContext, { credential: vc }) + const result = await w3cCredentialsService.verifyCredential(agentContext, { credential: vc }) expect(result.verified).toBe(false) expect(result.error).toBeDefined() @@ -201,7 +201,7 @@ describe('W3cCredentialService', () => { } const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) - const result = await w3cCredentialService.verifyCredential(agentContext, { credential: vc }) + const result = await w3cCredentialsService.verifyCredential(agentContext, { credential: vc }) expect(result.verified).toBe(false) @@ -223,7 +223,7 @@ describe('W3cCredentialService', () => { } const vc = JsonTransformer.fromJSON(vcJson, W3cVerifiableCredential) - const result = await w3cCredentialService.verifyCredential(agentContext, { credential: vc }) + const result = await w3cCredentialsService.verifyCredential(agentContext, { credential: vc }) expect(result.verified).toBe(false) @@ -239,7 +239,7 @@ describe('W3cCredentialService', () => { Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, W3cVerifiableCredential ) - const result = await w3cCredentialService.createPresentation({ credentials: vc }) + const result = await w3cCredentialsService.createPresentation({ credentials: vc }) expect(result).toBeInstanceOf(W3cPresentation) @@ -259,7 +259,7 @@ describe('W3cCredentialService', () => { ) const vcs = [vc1, vc2] - const result = await w3cCredentialService.createPresentation({ credentials: vcs }) + const result = await w3cCredentialsService.createPresentation({ credentials: vcs }) expect(result).toBeInstanceOf(W3cPresentation) @@ -280,7 +280,7 @@ describe('W3cCredentialService', () => { date: new Date().toISOString(), }) - const verifiablePresentation = await w3cCredentialService.signPresentation(agentContext, { + const verifiablePresentation = await w3cCredentialsService.signPresentation(agentContext, { presentation: presentation, purpose: purpose, signatureType: 'Ed25519Signature2018', @@ -298,7 +298,7 @@ describe('W3cCredentialService', () => { W3cVerifiablePresentation ) - const result = await w3cCredentialService.verifyPresentation(agentContext, { + const result = await w3cCredentialsService.verifyPresentation(agentContext, { presentation: vp, challenge: '7bf32d0b-39d4-41f3-96b6-45de52988e4c', }) @@ -310,7 +310,7 @@ describe('W3cCredentialService', () => { describe('Credential Storage', () => { let w3cCredentialRecord: W3cCredentialRecord - let w3cCredentialRepositoryDeleteMock: jest.MockedFunction<(typeof w3cCredentialRepository)['delete']> + let w3cCredentialRepositoryDeleteMock: jest.MockedFunction<(typeof w3cCredentialsRepository)['delete']> beforeEach(async () => { const credential = JsonTransformer.fromJSON( @@ -320,9 +320,9 @@ describe('W3cCredentialService', () => { w3cCredentialRecord = await credentialRecordFactory(credential) - mockFunction(w3cCredentialRepository.getById).mockResolvedValue(w3cCredentialRecord) - mockFunction(w3cCredentialRepository.getAll).mockResolvedValue([w3cCredentialRecord]) - w3cCredentialRepositoryDeleteMock = mockFunction(w3cCredentialRepository.delete).mockResolvedValue() + mockFunction(w3cCredentialsRepository.getById).mockResolvedValue(w3cCredentialRecord) + mockFunction(w3cCredentialsRepository.getAll).mockResolvedValue([w3cCredentialRecord]) + w3cCredentialRepositoryDeleteMock = mockFunction(w3cCredentialsRepository.delete).mockResolvedValue() }) describe('storeCredential', () => { it('should store a credential and expand the tags correctly', async () => { @@ -331,7 +331,7 @@ describe('W3cCredentialService', () => { W3cVerifiableCredential ) - w3cCredentialRecord = await w3cCredentialService.storeCredential(agentContext, { credential: credential }) + w3cCredentialRecord = await w3cCredentialsService.storeCredential(agentContext, { credential: credential }) expect(w3cCredentialRecord).toMatchObject({ type: 'W3cCredentialRecord', @@ -357,7 +357,7 @@ describe('W3cCredentialService', () => { ) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await w3cCredentialService.removeCredentialRecord(agentContext, credential.id!) + await w3cCredentialsService.removeCredentialRecord(agentContext, credential.id!) expect(w3cCredentialRepositoryDeleteMock).toBeCalledWith(agentContext, w3cCredentialRecord) }) @@ -369,16 +369,16 @@ describe('W3cCredentialService', () => { Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, W3cVerifiableCredential ) - await w3cCredentialService.storeCredential(agentContext, { credential: credential }) + await w3cCredentialsService.storeCredential(agentContext, { credential: credential }) - const records = await w3cCredentialService.getAllCredentialRecords(agentContext) + const records = await w3cCredentialsService.getAllCredentialRecords(agentContext) expect(records.length).toEqual(1) }) }) describe('getCredentialRecordById', () => { it('should retrieve a W3cCredentialRecord by id', async () => { - const credential = await w3cCredentialService.getCredentialRecordById(agentContext, w3cCredentialRecord.id) + const credential = await w3cCredentialsService.getCredentialRecordById(agentContext, w3cCredentialRecord.id) expect(credential.id).toEqual(w3cCredentialRecord.id) }) diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts new file mode 100644 index 0000000000..f939a40ccd --- /dev/null +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts @@ -0,0 +1,91 @@ +import { IndySdkModule } from '../../../../../indy-sdk/src' +import { getAgentOptions, indySdk } from '../../../../tests' +import { Agent } from '../../../agent/Agent' +import { JsonTransformer } from '../../../utils' +import { W3cCredentialService } from '../W3cCredentialService' +import { W3cVerifiableCredential } from '../models' +import { W3cCredentialRepository } from '../repository' + +import { Ed25519Signature2018Fixtures } from './fixtures' + +const modules = { + indySdk: new IndySdkModule({ + indySdk, + }), +} + +const agentOptions = getAgentOptions('W3cCredentialsApi', {}, modules) + +const agent = new Agent(agentOptions) + +let w3cCredentialRepository: W3cCredentialRepository +let w3cCredentialService: W3cCredentialService + +const testCredential = JsonTransformer.fromJSON( + Ed25519Signature2018Fixtures.TEST_LD_DOCUMENT_SIGNED, + W3cVerifiableCredential +) + +describe('W3cCredentialsApi', () => { + beforeAll(() => { + w3cCredentialRepository = agent.dependencyManager.resolve(W3cCredentialRepository) + w3cCredentialService = agent.dependencyManager.resolve(W3cCredentialService) + }) + + beforeEach(async () => { + await agent.initialize() + }) + + afterEach(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('Should successfully store a credential', async () => { + const repoSpy = jest.spyOn(w3cCredentialRepository, 'save') + const serviceSpy = jest.spyOn(w3cCredentialService, 'storeCredential') + + await agent.w3cCredentials.storeCredential({ + credential: testCredential, + }) + + expect(repoSpy).toHaveBeenCalledTimes(1) + expect(serviceSpy).toHaveBeenCalledTimes(1) + }) + + it('Should successfully retrieve a credential by id', async () => { + const repoSpy = jest.spyOn(w3cCredentialRepository, 'getById') + const serviceSpy = jest.spyOn(w3cCredentialService, 'getCredentialRecordById') + + const storedCredential = await agent.w3cCredentials.storeCredential({ + credential: testCredential, + }) + + const retrievedCredential = await agent.w3cCredentials.getCredentialRecordById(storedCredential.id) + expect(storedCredential.id).toEqual(retrievedCredential.id) + + expect(repoSpy).toHaveBeenCalledTimes(1) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(repoSpy).toHaveBeenCalledWith((agent as any).agentContext, storedCredential.id) + expect(serviceSpy).toHaveBeenCalledTimes(1) + }) + + it('Should successfully remove a credential by id', async () => { + const repoSpy = jest.spyOn(w3cCredentialRepository, 'delete') + const serviceSpy = jest.spyOn(w3cCredentialService, 'removeCredentialRecord') + + const storedCredential = await agent.w3cCredentials.storeCredential({ + credential: testCredential, + }) + + await agent.w3cCredentials.removeCredentialRecord(storedCredential.id) + + expect(repoSpy).toHaveBeenCalledTimes(1) + expect(serviceSpy).toHaveBeenCalledTimes(1) + expect(serviceSpy).toHaveBeenCalledWith((agent as any).agentContext, storedCredential.id) + + const allCredentials = await agent.w3cCredentials.getAllCredentialRecords() + expect(allCredentials).toHaveLength(0) + }) +}) diff --git a/packages/core/src/modules/vc/__tests__/W3cVcModuleConfig.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialsModuleConfig.test.ts similarity index 59% rename from packages/core/src/modules/vc/__tests__/W3cVcModuleConfig.test.ts rename to packages/core/src/modules/vc/__tests__/W3cCredentialsModuleConfig.test.ts index e97d51389d..8ccbf3b919 100644 --- a/packages/core/src/modules/vc/__tests__/W3cVcModuleConfig.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialsModuleConfig.test.ts @@ -1,16 +1,16 @@ -import { W3cVcModuleConfig } from '../W3cVcModuleConfig' +import { W3cCredentialsModuleConfig } from '../W3cCredentialsModuleConfig' import { defaultDocumentLoader } from '../libraries/documentLoader' -describe('W3cVcModuleConfig', () => { +describe('W3cCredentialsModuleConfig', () => { test('sets default values', () => { - const config = new W3cVcModuleConfig() + const config = new W3cCredentialsModuleConfig() expect(config.documentLoader).toBe(defaultDocumentLoader) }) test('sets values', () => { const documentLoader = jest.fn() - const config = new W3cVcModuleConfig({ + const config = new W3cCredentialsModuleConfig({ documentLoader, }) diff --git a/packages/core/src/modules/vc/index.ts b/packages/core/src/modules/vc/index.ts index 289e8fbcd9..d97dd0dcd2 100644 --- a/packages/core/src/modules/vc/index.ts +++ b/packages/core/src/modules/vc/index.ts @@ -1,6 +1,6 @@ export * from './W3cCredentialService' export * from './repository/W3cCredentialRecord' -export * from './W3cVcModule' +export * from './W3cCredentialsModule' export * from './models' export type { DocumentLoader, Proof } from './jsonldUtil' export { w3cDate, orArrayToArray } from './jsonldUtil' diff --git a/packages/core/src/modules/vc/models/index.ts b/packages/core/src/modules/vc/models/index.ts index 45d0a281f7..6dcfacbc39 100644 --- a/packages/core/src/modules/vc/models/index.ts +++ b/packages/core/src/modules/vc/models/index.ts @@ -1,6 +1,7 @@ export * from './credential/W3cCredential' export * from './credential/W3cVerifiableCredential' export * from './credential/W3cVerifyCredentialResult' +export * from './W3cCredentialServiceOptions' export * from './presentation/VerifyPresentationResult' export * from './presentation/W3cPresentation' export * from './presentation/W3cVerifiablePresentation' diff --git a/packages/core/tests/jsonld.ts b/packages/core/tests/jsonld.ts index 354599cfb3..5ac2385157 100644 --- a/packages/core/tests/jsonld.ts +++ b/packages/core/tests/jsonld.ts @@ -14,7 +14,7 @@ import { CredentialsModule, JsonLdCredentialFormatService, V2CredentialProtocol, - W3cVcModule, + W3cCredentialsModule, } from '../src' import { customDocumentLoader } from '../src/modules/vc/__tests__/documentLoader' @@ -33,7 +33,7 @@ export const getJsonLdModules = ({ credentialProtocols: [new V2CredentialProtocol({ credentialFormats: [new JsonLdCredentialFormatService()] })], autoAcceptCredentials, }), - w3cVc: new W3cVcModule({ + w3cVc: new W3cCredentialsModule({ documentLoader: customDocumentLoader, }), proofs: new ProofsModule({ diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index ce85f54bd9..58716ab557 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -1,6 +1,6 @@ import type { KeyDidCreateOptions } from '@aries-framework/core' -import { Agent, KeyType, TypedArrayEncoder, W3cCredentialRecord, W3cVcModule } from '@aries-framework/core' +import { Agent, KeyType, TypedArrayEncoder, W3cCredentialRecord, W3cCredentialsModule } from '@aries-framework/core' import nock, { cleanAll, enableNetConnect } from 'nock' import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' @@ -14,7 +14,7 @@ import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' const modules = { openId4VcClient: new OpenId4VcClientModule(), - w3cVc: new W3cVcModule({ + w3cCredentials: new W3cCredentialsModule({ documentLoader: customDocumentLoader, }), indySdk: new IndySdkModule({ @@ -29,6 +29,7 @@ describe('OpenId4VcClient', () => { const agentOptions = getAgentOptions('OpenId4VcClient Agent', {}, modules) agent = new Agent(agentOptions) + await agent.initialize() }) From c8b16a6fec8bb693e67e65709ded05d19fd1919f Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Tue, 4 Apr 2023 16:01:21 +0200 Subject: [PATCH 116/139] fix: remove `deleteOnFinish` and added documentation (#1418) Signed-off-by: blu3beri --- .../src/IndySdkToAskarMigrationUpdater.ts | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index 73db32bdce..9460e479ee 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -30,32 +30,22 @@ export class IndySdkToAskarMigrationUpdater { private agent: Agent private dbPath: string private fs: FileSystem - private deleteOnFinish: boolean - private constructor( - walletConfig: WalletConfig, - agent: Agent, - dbPath: string, - deleteOnFinish = false, - defaultLinkSecretId?: string - ) { + private constructor(walletConfig: WalletConfig, agent: Agent, dbPath: string, defaultLinkSecretId?: string) { this.walletConfig = walletConfig this.dbPath = dbPath this.agent = agent this.fs = this.agent.dependencyManager.resolve(InjectionSymbols.FileSystem) this.defaultLinkSecretId = defaultLinkSecretId ?? walletConfig.id - this.deleteOnFinish = deleteOnFinish } public static async initialize({ dbPath, agent, - deleteOnFinish, defaultLinkSecretId, }: { dbPath: string agent: Agent - deleteOnFinish?: boolean defaultLinkSecretId?: string }) { const { @@ -83,7 +73,7 @@ export class IndySdkToAskarMigrationUpdater { throw new IndySdkToAskarMigrationError("Wallet on the agent must be of instance 'AskarWallet'") } - return new IndySdkToAskarMigrationUpdater(walletConfig, agent, dbPath, deleteOnFinish, defaultLinkSecretId) + return new IndySdkToAskarMigrationUpdater(walletConfig, agent, dbPath, defaultLinkSecretId) } /** @@ -186,14 +176,16 @@ export class IndySdkToAskarMigrationUpdater { // Copy the file from the database path to the new location await this.fs.copyFile(src, dest) - - // Delete the original, only if specified by the user - if (this.deleteOnFinish) await this.fs.delete(this.dbPath) } /** * Function that updates the values from an indy-sdk structure to the new askar structure. * + * > NOTE: It is very important that this script is ran before the 0.3.x to + * 0.4.x migration script. This can easily be done by calling this when you + * upgrade, before you initialize the agent with `autoUpdateStorageOnStartup: + * true`. + * * - Assert that the paths that will be used are free * - Create a backup of the database * - Migrate the database to askar structure From fe10fb44056b7592ff4b5b4bae263d7d3621e20d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 4 Apr 2023 16:21:54 +0200 Subject: [PATCH 117/139] chore: update shared components libraries (#1419) Signed-off-by: Timo Glastra --- demo/package.json | 6 +-- packages/anoncreds-rs/package.json | 4 +- packages/anoncreds/package.json | 2 +- packages/askar/package.json | 4 +- packages/askar/src/utils/askarWalletConfig.ts | 16 +++--- packages/askar/src/wallet/AskarWallet.ts | 13 +++-- .../indy-sdk-to-askar-migration/package.json | 4 +- .../src/IndySdkToAskarMigrationUpdater.ts | 13 ++--- .../indy-sdk-to-askar-migration/src/utils.ts | 13 +++++ packages/indy-vdr/package.json | 4 +- yarn.lock | 54 +++++++++---------- 11 files changed, 71 insertions(+), 62 deletions(-) diff --git a/demo/package.json b/demo/package.json index 81f6d992d5..5b5eb93d5e 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,9 +14,9 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.13", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", + "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.13", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", "inquirer": "^8.2.5" }, "devDependencies": { diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index d69a68df0d..e4a42ac41a 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.13", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.14", "class-transformer": "^0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.13", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 2994f8462c..5104079b86 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -32,7 +32,7 @@ }, "devDependencies": { "@aries-framework/node": "0.3.3", - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.13", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.4.0", "rxjs": "^7.8.0", diff --git a/packages/askar/package.json b/packages/askar/package.json index 3e7661e31c..29d8f86786 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@aries-framework/core": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.6", + "@hyperledger/aries-askar-shared": "^0.1.0-dev.8", "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.0", @@ -34,7 +34,7 @@ }, "devDependencies": { "@types/bn.js": "^5.1.0", - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/askar/src/utils/askarWalletConfig.ts b/packages/askar/src/utils/askarWalletConfig.ts index b6e885ebe5..50424bcd84 100644 --- a/packages/askar/src/utils/askarWalletConfig.ts +++ b/packages/askar/src/utils/askarWalletConfig.ts @@ -2,20 +2,16 @@ import type { AskarWalletPostgresStorageConfig } from '../wallet/AskarWalletPost import type { WalletConfig } from '@aries-framework/core' import { KeyDerivationMethod, WalletError } from '@aries-framework/core' -import { StoreKeyMethod } from '@hyperledger/aries-askar-shared' - -export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod?: KeyDerivationMethod) => { - if (!keyDerivationMethod) { - return undefined - } +import { KdfMethod, StoreKeyMethod } from '@hyperledger/aries-askar-shared' +export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod: KeyDerivationMethod) => { const correspondenceTable = { - [KeyDerivationMethod.Raw]: StoreKeyMethod.Raw, - [KeyDerivationMethod.Argon2IInt]: `${StoreKeyMethod.Kdf}:argon2i:int`, - [KeyDerivationMethod.Argon2IMod]: `${StoreKeyMethod.Kdf}:argon2i:mod`, + [KeyDerivationMethod.Raw]: KdfMethod.Raw, + [KeyDerivationMethod.Argon2IInt]: KdfMethod.Argon2IInt, + [KeyDerivationMethod.Argon2IMod]: KdfMethod.Argon2IMod, } - return correspondenceTable[keyDerivationMethod] as StoreKeyMethod + return new StoreKeyMethod(correspondenceTable[keyDerivationMethod]) } /** diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 1b51e55d09..2ca0597a36 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -8,7 +8,6 @@ import type { Wallet, WalletConfigRekey, KeyPair, - KeyDerivationMethod, WalletExportImportConfig, } from '@aries-framework/core' import type { KeyEntryObject, Session } from '@hyperledger/aries-askar-shared' @@ -33,8 +32,10 @@ import { TypedArrayEncoder, FileSystem, WalletNotFoundError, + KeyDerivationMethod, } from '@aries-framework/core' import { + KdfMethod, StoreKeyMethod, KeyAlgs, CryptoBox, @@ -233,9 +234,7 @@ export class AskarWallet implements Wallet { if (rekey) { await this._store.rekey({ passKey: rekey, - keyMethod: - keyDerivationMethodToStoreKeyMethod(rekeyDerivation) ?? - (`${StoreKeyMethod.Kdf}:argon2i:int` as StoreKeyMethod), + keyMethod: keyDerivationMethodToStoreKeyMethod(rekeyDerivation ?? KeyDerivationMethod.Argon2IInt), }) } this._session = await this._store.openSession() @@ -823,9 +822,9 @@ export class AskarWallet implements Wallet { uri, profile: walletConfig.id, // FIXME: Default derivation method should be set somewhere in either agent config or some constants - keyMethod: - keyDerivationMethodToStoreKeyMethod(walletConfig.keyDerivationMethod) ?? - (`${StoreKeyMethod.Kdf}:argon2i:int` as StoreKeyMethod), + keyMethod: keyDerivationMethodToStoreKeyMethod( + walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IInt + ), passKey: walletConfig.key, } } diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index 2d1faff8e0..5b8d30917d 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -28,10 +28,10 @@ "@aries-framework/askar": "0.3.3", "@aries-framework/core": "0.3.3", "@aries-framework/node": "0.3.3", - "@hyperledger/aries-askar-shared": "^0.1.0-dev.6" + "@hyperledger/aries-askar-shared": "^0.1.0-dev.8" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.6", + "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", "@aries-framework/indy-sdk": "0.3.3", "indy-sdk": "^1.16.0-dev-1655", "rimraf": "^4.4.0", diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index 9460e479ee..1f2649933a 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -5,10 +5,10 @@ import type { EntryObject } from '@hyperledger/aries-askar-shared' import { AnonCredsCredentialRecord, AnonCredsLinkSecretRecord } from '@aries-framework/anoncreds' import { AskarWallet } from '@aries-framework/askar' import { InjectionSymbols, KeyDerivationMethod, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' -import { Migration, Key, KeyAlgs, Store, StoreKeyMethod } from '@hyperledger/aries-askar-shared' +import { Migration, Key, KeyAlgs, Store } from '@hyperledger/aries-askar-shared' import { IndySdkToAskarMigrationError } from './errors/IndySdkToAskarMigrationError' -import { transformFromRecordTagValues } from './utils' +import { keyDerivationMethodToStoreKeyMethod, transformFromRecordTagValues } from './utils' /** * @@ -79,7 +79,7 @@ export class IndySdkToAskarMigrationUpdater { /** * This function migrates the old database to the new structure. * - * This doubles checks some fields as later it might be possiblt to run this function + * This doubles checks some fields as later it might be possible to run this function */ private async migrate() { const specUri = this.backupFile @@ -204,8 +204,9 @@ export class IndySdkToAskarMigrationUpdater { // Migrate the database await this.migrate() - const keyMethod = - this.walletConfig?.keyDerivationMethod == KeyDerivationMethod.Raw ? StoreKeyMethod.Raw : StoreKeyMethod.Kdf + const keyMethod = keyDerivationMethodToStoreKeyMethod( + this.walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IInt + ) this.store = await Store.open({ uri: `sqlite://${this.backupFile}`, passKey: this.walletConfig.key, keyMethod }) // Update the values to reflect the new structure @@ -217,7 +218,7 @@ export class IndySdkToAskarMigrationUpdater { // Move the migrated and updated file to the expected location for afj await this.moveToNewLocation() } catch (err) { - this.agent.config.logger.error('Migration failed. Restoring state.') + this.agent.config.logger.error(`Migration failed. Restoring state. ${err.message}`) throw new IndySdkToAskarMigrationError(`Migration failed. State has been restored. ${err.message}`, { cause: err.cause, diff --git a/packages/indy-sdk-to-askar-migration/src/utils.ts b/packages/indy-sdk-to-askar-migration/src/utils.ts index 74bccccec8..86998ecb4a 100644 --- a/packages/indy-sdk-to-askar-migration/src/utils.ts +++ b/packages/indy-sdk-to-askar-migration/src/utils.ts @@ -1,5 +1,8 @@ import type { TagsBase } from '@aries-framework/core' +import { KeyDerivationMethod } from '@aries-framework/core' +import { KdfMethod, StoreKeyMethod } from '@hyperledger/aries-askar-shared' + /** * Adopted from `AskarStorageService` implementation and should be kept in sync. */ @@ -38,3 +41,13 @@ export const transformFromRecordTagValues = (tags: TagsBase): { [key: string]: s return transformedTags } + +export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod: KeyDerivationMethod) => { + const correspondenceTable = { + [KeyDerivationMethod.Raw]: KdfMethod.Raw, + [KeyDerivationMethod.Argon2IInt]: KdfMethod.Argon2IInt, + [KeyDerivationMethod.Argon2IMod]: KdfMethod.Argon2IMod, + } + + return new StoreKeyMethod(correspondenceTable[keyDerivationMethod]) +} diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index ec0839247e..a4cf2dc94b 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -26,10 +26,10 @@ "dependencies": { "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", - "@hyperledger/indy-vdr-shared": "0.1.0-dev.12" + "@hyperledger/indy-vdr-shared": "0.1.0-dev.13" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.12", + "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.13", "@stablelib/ed25519": "^1.0.2", "rimraf": "^4.4.0", "rxjs": "^7.2.0", diff --git a/yarn.lock b/yarn.lock index 93024b56e7..bbad52ea17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -881,12 +881,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.13": - version "0.1.0-dev.13" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.13.tgz#65b60be1c5ff077457ccd2f8298e71f198a8984f" - integrity sha512-W6Hoxp4lzcdv6yIQruK0CJDH52n79yQ8XCekFNmkUsXxpycB8Ts2M1o3KKRPa68AYM3CzmcN2Nw8pE7XqqEMyQ== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.14": + version "0.1.0-dev.14" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.14.tgz#00d4dca419f04a580cc02611d0c803d3d5c04fbf" + integrity sha512-N0H89rRBOaLeX+j5NHSibU1m88lWOXWg2t/orZZtY9iTt8op1oFV0IcWHeUJ1lPDJnOiozpn+AF/f5G0kipK7w== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.13" + "@hyperledger/anoncreds-shared" "0.1.0-dev.14" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -894,17 +894,17 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.13", "@hyperledger/anoncreds-shared@^0.1.0-dev.13": - version "0.1.0-dev.13" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.13.tgz#e78768366e6d7dd6e65839b769b857fbd828bce7" - integrity sha512-UtR2zCrugTa/Mu6LqAiEX1NJw1bdaf65wqcdS/k9efcq0iY1slQb+qg/KWEf+pZZFVa6NmkjAwmdyrzVbu9WTQ== +"@hyperledger/anoncreds-shared@0.1.0-dev.14", "@hyperledger/anoncreds-shared@^0.1.0-dev.14": + version "0.1.0-dev.14" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.14.tgz#0cdaa18a59b8223bb3eb2116970b1aa56cf5acfc" + integrity sha512-4yS7NgZZF9nfE50OquvbLWuZSzjOOiPufC/n2Ywb5lL2VloBblXMI2CezCZU1POmyAl7xnoT99pi2Od1fQaJxQ== -"@hyperledger/aries-askar-nodejs@^0.1.0-dev.6": - version "0.1.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.6.tgz#1067196269e3dc7904afa946234e069c31dfc2d9" - integrity sha512-VHORF/VbXXNA0zevS8diocVFfHpqp8XS33VuIEDFEG9n87Sc4sO0KxxCr5KdGeAf46yhiCdJd2bOKRjDHCObyQ== +"@hyperledger/aries-askar-nodejs@^0.1.0-dev.8": + version "0.1.0-dev.8" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-nodejs/-/aries-askar-nodejs-0.1.0-dev.8.tgz#a8a6b2969a03f6af8610db27f80fcd847384edc3" + integrity sha512-HzAJ7yZb+NwadV4P9a5gXRlqbYMPWy+3wFZvEkzfTl7Km2LxZat9WyHkMrcc9i2PXH1NZEK56XchzTmqG2zw3Q== dependencies: - "@hyperledger/aries-askar-shared" "0.1.0-dev.6" + "@hyperledger/aries-askar-shared" "0.1.0-dev.8" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "^4.0.3" node-cache "^5.1.2" @@ -912,19 +912,19 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/aries-askar-shared@0.1.0-dev.6", "@hyperledger/aries-askar-shared@^0.1.0-dev.6": - version "0.1.0-dev.6" - resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.6.tgz#af3bf4318312ee1af0ead8c0bea6a4445a265525" - integrity sha512-P62u1GNw2hvFh3T8hYTBiD2YsIzHIQOwa8+p8wEhB0AJi7Ixc3OcAtxxgbreosDtGrW+cxkinuSqufveuK9V1g== +"@hyperledger/aries-askar-shared@0.1.0-dev.8", "@hyperledger/aries-askar-shared@^0.1.0-dev.8": + version "0.1.0-dev.8" + resolved "https://registry.yarnpkg.com/@hyperledger/aries-askar-shared/-/aries-askar-shared-0.1.0-dev.8.tgz#9cafec424e6390f38c8098b908f6434881217a54" + integrity sha512-Zf0njf/4Lx8u12WGdEC0yYlPcczdk38yLCn9w6UdczzK0Lp+4YGDkSvpbvtCP7EXAykuMOQ3/P1iRBWnjygGpg== dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@0.1.0-dev.12": - version "0.1.0-dev.12" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.12.tgz#c8966bf5f446fff4c7bf0bf5afb4159159d2dd9e" - integrity sha512-rJDkk4u0zyn3n/PBw0pf3qSgbZ0n5VAJY5ykeMU2/bBfFXQ1KJex/148M4YsGEOxY0XUSoXgcT/oJYS3MSA6Nw== +"@hyperledger/indy-vdr-nodejs@0.1.0-dev.13": + version "0.1.0-dev.13" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.13.tgz#1630d7e00366160d3ef6ae0237528faa749ca831" + integrity sha512-r5s5GuKjTP1ALkW4sG6oGdSGPX407nCEHB0lTapVuDF7aUHRsYQkcGUkyMESH2LmCSoxAvwM05RVKJOHYnK7zg== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.12" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.13" "@mapbox/node-pre-gyp" "^1.0.10" "@types/ref-array-di" "^1.2.5" ffi-napi "^4.0.3" @@ -932,10 +932,10 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.12": - version "0.1.0-dev.12" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.12.tgz#a5051cb91b7698f80265b7506789fb50490477e2" - integrity sha512-CPVGTHVLFAVVU6uIhcbhAUWqDrn3u2R3D+ALdqgKwJY1Ca8kFiUvhFN1/DkHtZuEo549wPQmFqH2hCkXaiuF7Q== +"@hyperledger/indy-vdr-shared@0.1.0-dev.13": + version "0.1.0-dev.13" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.13.tgz#ccecd11b706253e61be16c1e8098ba045a5a42d1" + integrity sha512-MxSlxZSy9lpfFB6/APHclxOqFwWbyEGJtg+8u9CgAVtyHA8AqdM5MQc7A485VzUFYqV95f1FmE+8W5qpPSLTZw== "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" From 7b5962917488cfd0c5adc170d3c3fc64aa82ef2c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 4 Apr 2023 21:29:05 +0200 Subject: [PATCH 118/139] fix(askar): default key derivation method (#1420) Signed-off-by: Timo Glastra --- packages/askar/src/wallet/AskarWallet.ts | 4 ++-- .../src/IndySdkToAskarMigrationUpdater.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 2ca0597a36..d850a18f3f 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -234,7 +234,7 @@ export class AskarWallet implements Wallet { if (rekey) { await this._store.rekey({ passKey: rekey, - keyMethod: keyDerivationMethodToStoreKeyMethod(rekeyDerivation ?? KeyDerivationMethod.Argon2IInt), + keyMethod: keyDerivationMethodToStoreKeyMethod(rekeyDerivation ?? KeyDerivationMethod.Argon2IMod), }) } this._session = await this._store.openSession() @@ -823,7 +823,7 @@ export class AskarWallet implements Wallet { profile: walletConfig.id, // FIXME: Default derivation method should be set somewhere in either agent config or some constants keyMethod: keyDerivationMethodToStoreKeyMethod( - walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IInt + walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IMod ), passKey: walletConfig.key, } diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index 1f2649933a..006c3657a0 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -83,7 +83,7 @@ export class IndySdkToAskarMigrationUpdater { */ private async migrate() { const specUri = this.backupFile - const kdfLevel = this.walletConfig.keyDerivationMethod ?? 'ARGON2I_MOD' + const kdfLevel = this.walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IMod const walletName = this.walletConfig.id const walletKey = this.walletConfig.key const storageType = this.walletConfig.storage?.type ?? 'sqlite' @@ -205,7 +205,7 @@ export class IndySdkToAskarMigrationUpdater { await this.migrate() const keyMethod = keyDerivationMethodToStoreKeyMethod( - this.walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IInt + this.walletConfig.keyDerivationMethod ?? KeyDerivationMethod.Argon2IMod ) this.store = await Store.open({ uri: `sqlite://${this.backupFile}`, passKey: this.walletConfig.key, keyMethod }) From 644e860a05f40166e26c497a2e8619c9a38df11d Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:15:39 +0200 Subject: [PATCH 119/139] fix(anoncreds): make revocation status list inline with the spec (#1421) Signed-off-by: blu3beri --- packages/anoncreds/src/models/registry.ts | 2 +- packages/anoncreds/tests/anoncreds.test.ts | 2 +- packages/indy-sdk/src/anoncreds/utils/transform.ts | 2 +- .../indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts | 4 ++-- packages/indy-vdr/src/anoncreds/utils/transform.ts | 2 +- .../indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/anoncreds/src/models/registry.ts b/packages/anoncreds/src/models/registry.ts index 31314ada51..bc4ad8a152 100644 --- a/packages/anoncreds/src/models/registry.ts +++ b/packages/anoncreds/src/models/registry.ts @@ -36,7 +36,7 @@ export interface AnonCredsRevocationRegistryDefinition { export interface AnonCredsRevocationStatusList { issuerId: string - revRegId: string + revRegDefId: string revocationList: number[] currentAccumulator: string timestamp: number diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index 39d07eee22..db7c6e1def 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -65,7 +65,7 @@ const existingRevocationStatusLists = { currentAccumulator: 'ab81257c-be63-4051-9e21-c7d384412f64', issuerId: 'VsKV7grR1BUE29mG2Fm2kX', revocationList: [1, 0, 1], - revRegId: 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG', + revRegDefId: 'VsKV7grR1BUE29mG2Fm2kX:4:VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG:CL_ACCUM:TAG', timestamp: 10123, }, }, diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index e7eac06ecc..9ddc18f81d 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -103,7 +103,7 @@ export function anonCredsRevocationStatusListFromIndySdk( return { issuerId: revocationRegistryDefinition.issuerId, currentAccumulator: delta.value.accum, - revRegId: revocationRegistryDefinitionId, + revRegDefId: revocationRegistryDefinitionId, revocationList, timestamp, } diff --git a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts index 12a7cd6848..0efa8d533d 100644 --- a/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-sdk-anoncreds-registry.e2e.test.ts @@ -304,7 +304,7 @@ describe('IndySdkAnonCredsRegistry', () => { issuerId: legacyIssuerId, currentAccumulator: '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', - revRegId: legacyRevocationRegistryId, + revRegDefId: legacyRevocationRegistryId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -329,7 +329,7 @@ describe('IndySdkAnonCredsRegistry', () => { issuerId: didIndyIssuerId, currentAccumulator: '1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000 1 0000000000000000000000000000000000000000000000000000000000000000', - revRegId: didIndyRevocationRegistryId, + revRegDefId: didIndyRevocationRegistryId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/packages/indy-vdr/src/anoncreds/utils/transform.ts b/packages/indy-vdr/src/anoncreds/utils/transform.ts index b0b0bd5fd6..36f4628bb4 100644 --- a/packages/indy-vdr/src/anoncreds/utils/transform.ts +++ b/packages/indy-vdr/src/anoncreds/utils/transform.ts @@ -26,7 +26,7 @@ export function anonCredsRevocationStatusListFromIndyVdr( return { issuerId: revocationRegistryDefinition.issuerId, currentAccumulator: delta.accum, - revRegId: revocationRegistryDefinitionId, + revRegDefId: revocationRegistryDefinitionId, revocationList, timestamp, } diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index 1cba5741e0..b96f5ec8bf 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -320,7 +320,7 @@ describe('IndyVdrAnonCredsRegistry', () => { revocationStatusList: { issuerId: legacyIssuerId, currentAccumulator: '1', - revRegId: legacyRevocationRegistryId, + revRegDefId: legacyRevocationRegistryId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -344,7 +344,7 @@ describe('IndyVdrAnonCredsRegistry', () => { revocationStatusList: { issuerId: didIndyIssuerId, currentAccumulator: '1', - revRegId: didIndyRevocationRegistryId, + revRegDefId: didIndyRevocationRegistryId, revocationList: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, From ec5c2335394e2df6bd8717907f03e5d2a430e9f9 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 10 Apr 2023 07:53:32 -0300 Subject: [PATCH 120/139] fix(anoncreds-rs): revocation status list as JSON (#1422) Signed-off-by: Ariel Gentile --- .../services/AnonCredsRsVerifierService.ts | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts index 81edd11c56..26573309ff 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsVerifierService.ts @@ -3,7 +3,7 @@ import type { AgentContext } from '@aries-framework/core' import type { JsonObject } from '@hyperledger/anoncreds-shared' import { injectable } from '@aries-framework/core' -import { Presentation, RevocationRegistryDefinition, RevocationStatusList } from '@hyperledger/anoncreds-shared' +import { Presentation } from '@hyperledger/anoncreds-shared' @injectable() export class AnonCredsRsVerifierService implements AnonCredsVerifierService { @@ -24,27 +24,15 @@ export class AnonCredsRsVerifierService implements AnonCredsVerifierService { rsSchemas[schemaId] = schemas[schemaId] as unknown as JsonObject } - const revocationRegistryDefinitions: Record = {} - const lists = [] + const revocationRegistryDefinitions: Record = {} + const lists: JsonObject[] = [] for (const revocationRegistryDefinitionId in revocationRegistries) { const { definition, revocationStatusLists } = options.revocationRegistries[revocationRegistryDefinitionId] - revocationRegistryDefinitions[revocationRegistryDefinitionId] = RevocationRegistryDefinition.fromJson( - definition as unknown as JsonObject - ) - - for (const timestamp in revocationStatusLists) { - lists.push( - RevocationStatusList.create({ - issuerId: definition.issuerId, - issuanceByDefault: true, - revocationRegistryDefinition: revocationRegistryDefinitions[revocationRegistryDefinitionId], - revocationRegistryDefinitionId, - timestamp: Number(timestamp), - }) - ) - } + revocationRegistryDefinitions[revocationRegistryDefinitionId] = definition as unknown as JsonObject + + lists.push(...(Object.values(revocationStatusLists) as unknown as Array)) } return presentation.verify({ From b35fec433f8fab513be2b8b6d073f23c6371b2ee Mon Sep 17 00:00:00 2001 From: "artem.ivanov" Date: Mon, 10 Apr 2023 13:57:52 +0300 Subject: [PATCH 121/139] feat: allow sending problem report when declining a proof request (#1408) Signed-off-by: Artemkaaas --- demo/src/AliceInquirer.ts | 2 +- packages/core/src/modules/proofs/ProofsApi.ts | 68 +++++++++---- .../src/modules/proofs/ProofsApiOptions.ts | 8 ++ .../proofs/protocol/BaseProofProtocol.ts | 6 +- .../v2-indy-connectionless-proofs.e2e.test.ts | 95 +++++++++++++++++++ 5 files changed, 158 insertions(+), 21 deletions(-) diff --git a/demo/src/AliceInquirer.ts b/demo/src/AliceInquirer.ts index 9752eab5aa..a665d5e80b 100644 --- a/demo/src/AliceInquirer.ts +++ b/demo/src/AliceInquirer.ts @@ -81,7 +81,7 @@ export class AliceInquirer extends BaseInquirer { public async acceptProofRequest(proofRecord: ProofExchangeRecord) { const confirm = await prompt([this.inquireConfirmation(Title.ProofRequestTitle)]) if (confirm.options === ConfirmOptions.No) { - await this.alice.agent.proofs.declineRequest(proofRecord.id) + await this.alice.agent.proofs.declineRequest({ proofRecordId: proofRecord.id }) } else if (confirm.options === ConfirmOptions.Yes) { await this.alice.acceptProofRequest(proofRecord) } diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 1a327f71b8..ed78afec2d 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -17,6 +17,7 @@ import type { SelectCredentialsForProofRequestOptions, SelectCredentialsForProofRequestReturn, SendProofProblemReportOptions, + DeclineProofRequestOptions, } from './ProofsApiOptions' import type { ProofProtocol } from './protocol/ProofProtocol' import type { ProofFormatsFromProtocols } from './protocol/ProofProtocolOptions' @@ -49,7 +50,7 @@ export interface ProofsApi { // Request methods requestProof(options: RequestProofOptions): Promise acceptRequest(options: AcceptProofRequestOptions): Promise - declineRequest(proofRecordId: string): Promise + declineRequest(options: DeclineProofRequestOptions): Promise negotiateRequest(options: NegotiateProofRequestOptions): Promise // Present @@ -368,11 +369,15 @@ export class ProofsApi implements ProofsApi { } } - public async declineRequest(proofRecordId: string): Promise { - const proofRecord = await this.getById(proofRecordId) + public async declineRequest(options: DeclineProofRequestOptions): Promise { + const proofRecord = await this.getById(options.proofRecordId) proofRecord.assertState(ProofState.RequestReceived) const protocol = this.getProtocol(proofRecord.protocolVersion) + if (options.sendProblemReport) { + await this.sendProblemReport({ proofRecordId: options.proofRecordId, description: 'Request declined' }) + } + await protocol.updateState(this.agentContext, proofRecord, ProofState.Declined) return proofRecord @@ -555,29 +560,60 @@ export class ProofsApi implements ProofsApi { */ public async sendProblemReport(options: SendProofProblemReportOptions): Promise { const proofRecord = await this.getById(options.proofRecordId) - if (!proofRecord.connectionId) { - throw new AriesFrameworkError(`No connectionId found for proof record '${proofRecord.id}'.`) - } const protocol = this.getProtocol(proofRecord.protocolVersion) - const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) - // Assert - connectionRecord.assertReady() + const requestMessage = await protocol.findRequestMessage(this.agentContext, proofRecord.id) const { message: problemReport } = await protocol.createProblemReport(this.agentContext, { proofRecord, description: options.description, }) - const outboundMessageContext = new OutboundMessageContext(problemReport, { - agentContext: this.agentContext, - connection: connectionRecord, - associatedRecord: proofRecord, - }) + if (proofRecord.connectionId) { + const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) - await this.messageSender.sendMessage(outboundMessageContext) - return proofRecord + // Assert + connectionRecord.assertReady() + + const outboundMessageContext = new OutboundMessageContext(problemReport, { + agentContext: this.agentContext, + connection: connectionRecord, + associatedRecord: proofRecord, + }) + + await this.messageSender.sendMessage(outboundMessageContext) + return proofRecord + } else if (requestMessage?.service) { + proofRecord.assertState(ProofState.RequestReceived) + + // Create ~service decorator + const routing = await this.routingService.getRouting(this.agentContext) + const ourService = new ServiceDecorator({ + serviceEndpoint: routing.endpoints[0], + recipientKeys: [routing.recipientKey.publicKeyBase58], + routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), + }) + const recipientService = requestMessage.service + + await this.messageSender.sendMessageToService( + new OutboundMessageContext(problemReport, { + agentContext: this.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, + }) + ) + + return proofRecord + } + // Cannot send message without connectionId or ~service decorator + else { + throw new AriesFrameworkError( + `Cannot send problem report without connectionId or ~service decorator on presentation request.` + ) + } } public async getFormatData(proofRecordId: string): Promise>> { diff --git a/packages/core/src/modules/proofs/ProofsApiOptions.ts b/packages/core/src/modules/proofs/ProofsApiOptions.ts index 9fcb5cefa3..0e9911febc 100644 --- a/packages/core/src/modules/proofs/ProofsApiOptions.ts +++ b/packages/core/src/modules/proofs/ProofsApiOptions.ts @@ -173,3 +173,11 @@ export interface SendProofProblemReportOptions { proofRecordId: string description: string } + +/** + * Interface for ProofsApi.declineRequest. Decline a received proof request and optionally send a problem-report message to Verifier + */ +export interface DeclineProofRequestOptions { + proofRecordId: string + sendProblemReport?: boolean +} diff --git a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts index 9e4e9e8b1c..ce2a30df05 100644 --- a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts @@ -110,16 +110,14 @@ export abstract class BaseProofProtocol ): Promise { - const { message: proofProblemReportMessage, agentContext } = messageContext - - const connection = messageContext.assertReadyConnection() + const { message: proofProblemReportMessage, agentContext, connection } = messageContext agentContext.config.logger.debug(`Processing problem report with message id ${proofProblemReportMessage.id}`) const proofRecord = await this.getByThreadAndConnectionId( agentContext, proofProblemReportMessage.threadId, - connection.id + connection?.id ) // Update record diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index 3312a27f19..55f1e8bbbb 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -18,6 +18,7 @@ import { makeConnection, testLogger, setupEventReplaySubjects, + waitForProofExchangeRecord, } from '../../../../../../tests' import { Agent } from '../../../../../agent/Agent' import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' @@ -543,4 +544,98 @@ describe('V2 Connectionless Proofs - Indy', () => { threadId: requestMessage.threadId, }) }) + + test('Faber starts with connection-less proof requests to Alice but gets Problem Reported', async () => { + testLogger.test('Faber sends presentation request to Alice') + + const { + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + credentialDefinitionId, + issuerHolderConnectionId: faberConnectionId, + } = await setupAnonCredsTests({ + issuerName: 'Faber connection-less Proofs v2 - Reject Request', + holderName: 'Alice connection-less Proofs v2 - Reject Request', + autoAcceptProofs: AutoAcceptProof.Never, + attributeNames: ['name', 'age'], + }) + + await issueLegacyAnonCredsCredential({ + issuerAgent: faberAgent, + issuerReplay: faberReplay, + holderAgent: aliceAgent, + holderReplay: aliceReplay, + issuerHolderConnectionId: faberConnectionId, + offer: { + credentialDefinitionId, + attributes: [ + { + name: 'name', + value: 'Alice', + }, + { + name: 'age', + value: '99', + }, + ], + }, + }) + + agents = [aliceAgent, faberAgent] + + // eslint-disable-next-line prefer-const + // eslint-disable-next-line prefer-const + let { message, proofRecord: faberProofExchangeRecord } = await faberAgent.proofs.createRequest({ + protocolVersion: 'v2', + proofFormats: { + indy: { + name: 'test-proof-request', + version: '1.0', + requested_attributes: { + name: { + name: 'name', + restrictions: [ + { + cred_def_id: credentialDefinitionId, + }, + ], + }, + }, + requested_predicates: {}, + }, + }, + autoAcceptProof: AutoAcceptProof.ContentApproved, + }) + + const { message: requestMessage } = await faberAgent.oob.createLegacyConnectionlessInvitation({ + recordId: faberProofExchangeRecord.id, + message, + domain: 'rxjs:faber', + }) + + for (const transport of faberAgent.outboundTransports) { + await faberAgent.unregisterOutboundTransport(transport) + } + + const aliceProofExchangeRecordPromise = waitForProofExchangeRecord(aliceAgent, { + state: ProofState.RequestReceived, + }) + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + const aliceProofExchangeRecord = await aliceProofExchangeRecordPromise + + await aliceAgent.proofs.declineRequest({ proofRecordId: aliceProofExchangeRecord.id, sendProblemReport: true }) + + await waitForProofExchangeRecordSubject(aliceReplay, { + state: ProofState.Declined, + threadId: requestMessage.threadId, + }) + + await waitForProofExchangeRecordSubject(faberReplay, { + state: ProofState.Abandoned, + threadId: requestMessage.threadId, + }) + }) }) From 4e7a380f7c53487209694ddf2091e9f47ebca2b5 Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Wed, 12 Apr 2023 15:03:13 -0700 Subject: [PATCH 122/139] build: test large builders (#1429) build: test large builders Signed-off-by: Ry Jones --- .github/workflows/continuous-integration.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index f1afad5234..250bec536c 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -26,7 +26,7 @@ jobs: # validation scripts. To still be able to run the CI we can manually trigger it by adding the 'ci-test' # label to the pull request ci-trigger: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest-m outputs: triggered: ${{ steps.check.outputs.triggered }} steps: @@ -45,7 +45,7 @@ jobs: echo "::set-output name=triggered::${SHOULD_RUN}" validate: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest-m name: Validate steps: - name: Checkout aries-framework-javascript @@ -79,7 +79,7 @@ jobs: run: yarn build integration-test: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest-m name: Integration Tests strategy: @@ -126,7 +126,7 @@ jobs: if: always() version-stable: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest-m name: Release stable needs: [integration-test, validate] if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' From b38525f3433e50418ea149949108b4218ac9ba2a Mon Sep 17 00:00:00 2001 From: DaevMithran <61043607+DaevMithran@users.noreply.github.com> Date: Thu, 13 Apr 2023 02:24:59 -0700 Subject: [PATCH 123/139] feat: Add cheqd-sdk module (#1334) Signed-off-by: DaevMithran --- packages/cheqd/jest.config.ts | 13 + packages/cheqd/package.json | 45 ++ packages/cheqd/src/CheqdModule.ts | 26 + packages/cheqd/src/CheqdModuleConfig.ts | 25 + packages/cheqd/src/anoncreds/index.ts | 1 + .../services/CheqdAnonCredsRegistry.ts | 340 ++++++++++++ .../cheqd/src/anoncreds/utils/identifiers.ts | 59 ++ .../cheqd/src/anoncreds/utils/transform.ts | 149 ++++++ packages/cheqd/src/dids/CheqdDidRegistrar.ts | 406 ++++++++++++++ packages/cheqd/src/dids/CheqdDidResolver.ts | 192 +++++++ packages/cheqd/src/dids/didCheqdUtil.ts | 152 ++++++ packages/cheqd/src/dids/index.ts | 8 + packages/cheqd/src/index.ts | 17 + .../cheqd/src/ledger/CheqdLedgerService.ts | 123 +++++ packages/cheqd/src/ledger/index.ts | 1 + .../tests/cheqd-did-registrar.e2e.test.ts | 132 +++++ .../tests/cheqd-did-resolver.e2e.test.ts | 69 +++ .../cheqd/tests/cheqd-did-utils.e2e.test.ts | 48 ++ .../cheqd-sdk-anoncreds-registry.e2e.test.ts | 243 +++++++++ packages/cheqd/tests/setup.ts | 33 ++ packages/cheqd/tests/setupCheqdModule.ts | 33 ++ packages/cheqd/tsconfig.build.json | 7 + packages/cheqd/tsconfig.json | 8 + packages/core/src/index.ts | 3 +- .../domain/key-type/__tests__/ed25519.test.ts | 1 + .../modules/dids/domain/key-type/ed25519.ts | 9 +- .../MediationRecipientService.test.ts | 1 - packages/core/src/utils/TypedArrayEncoder.ts | 18 + packages/core/src/utils/uuid.ts | 6 +- yarn.lock | 505 +++++++++++++++++- 30 files changed, 2659 insertions(+), 14 deletions(-) create mode 100644 packages/cheqd/jest.config.ts create mode 100644 packages/cheqd/package.json create mode 100644 packages/cheqd/src/CheqdModule.ts create mode 100644 packages/cheqd/src/CheqdModuleConfig.ts create mode 100644 packages/cheqd/src/anoncreds/index.ts create mode 100644 packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts create mode 100644 packages/cheqd/src/anoncreds/utils/identifiers.ts create mode 100644 packages/cheqd/src/anoncreds/utils/transform.ts create mode 100644 packages/cheqd/src/dids/CheqdDidRegistrar.ts create mode 100644 packages/cheqd/src/dids/CheqdDidResolver.ts create mode 100644 packages/cheqd/src/dids/didCheqdUtil.ts create mode 100644 packages/cheqd/src/dids/index.ts create mode 100644 packages/cheqd/src/index.ts create mode 100644 packages/cheqd/src/ledger/CheqdLedgerService.ts create mode 100644 packages/cheqd/src/ledger/index.ts create mode 100644 packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts create mode 100644 packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts create mode 100644 packages/cheqd/tests/cheqd-did-utils.e2e.test.ts create mode 100644 packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts create mode 100644 packages/cheqd/tests/setup.ts create mode 100644 packages/cheqd/tests/setupCheqdModule.ts create mode 100644 packages/cheqd/tsconfig.build.json create mode 100644 packages/cheqd/tsconfig.json diff --git a/packages/cheqd/jest.config.ts b/packages/cheqd/jest.config.ts new file mode 100644 index 0000000000..93c0197296 --- /dev/null +++ b/packages/cheqd/jest.config.ts @@ -0,0 +1,13 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json new file mode 100644 index 0000000000..65a5feee4c --- /dev/null +++ b/packages/cheqd/package.json @@ -0,0 +1,45 @@ +{ + "name": "@aries-framework/cheqd", + "main": "build/index", + "types": "build/index", + "version": "0.3.3", + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/cheqd", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/cheqd" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/anoncreds": "0.3.3", + "@aries-framework/core": "0.3.3", + "@cheqd/sdk": "cjs", + "@cheqd/ts-proto": "cjs", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "rxjs": "^7.2.0", + "tsyringe": "^4.7.0", + "@cosmjs/proto-signing": "^0.29.5", + "@cosmjs/crypto": "^0.29.5", + "@stablelib/ed25519": "^1.0.3" + }, + "devDependencies": { + "rimraf": "^4.0.7", + "typescript": "~4.9.4", + "@aries-framework/indy-sdk": "*", + "@types/indy-sdk": "*" + } +} diff --git a/packages/cheqd/src/CheqdModule.ts b/packages/cheqd/src/CheqdModule.ts new file mode 100644 index 0000000000..a52968f83c --- /dev/null +++ b/packages/cheqd/src/CheqdModule.ts @@ -0,0 +1,26 @@ +import type { CheqdModuleConfigOptions } from './CheqdModuleConfig' +import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' + +import { CheqdModuleConfig } from './CheqdModuleConfig' +import { CheqdLedgerService } from './ledger' + +export class CheqdModule implements Module { + public readonly config: CheqdModuleConfig + + public constructor(config: CheqdModuleConfigOptions) { + this.config = new CheqdModuleConfig(config) + } + + public register(dependencyManager: DependencyManager) { + // Register config + dependencyManager.registerInstance(CheqdModuleConfig, this.config) + + dependencyManager.registerSingleton(CheqdLedgerService) + } + + public async initialize(agentContext: AgentContext): Promise { + // not required + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + await cheqdLedgerService.connect() + } +} diff --git a/packages/cheqd/src/CheqdModuleConfig.ts b/packages/cheqd/src/CheqdModuleConfig.ts new file mode 100644 index 0000000000..3d0077883c --- /dev/null +++ b/packages/cheqd/src/CheqdModuleConfig.ts @@ -0,0 +1,25 @@ +/** + * CheqdModuleConfigOptions defines the interface for the options of the CheqdModuleConfig class. + */ +export interface CheqdModuleConfigOptions { + networks: NetworkConfig[] +} + +export interface NetworkConfig { + rpcUrl?: string + cosmosPayerSeed: string + network: string +} + +export class CheqdModuleConfig { + private options: CheqdModuleConfigOptions + + public constructor(options: CheqdModuleConfigOptions) { + this.options = options + } + + /** See {@link CheqdModuleConfigOptions.networks} */ + public get networks() { + return this.options.networks + } +} diff --git a/packages/cheqd/src/anoncreds/index.ts b/packages/cheqd/src/anoncreds/index.ts new file mode 100644 index 0000000000..6a8bd47548 --- /dev/null +++ b/packages/cheqd/src/anoncreds/index.ts @@ -0,0 +1 @@ +export { CheqdAnonCredsRegistry } from './services/CheqdAnonCredsRegistry' diff --git a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts new file mode 100644 index 0000000000..4b3d5db18c --- /dev/null +++ b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts @@ -0,0 +1,340 @@ +import type { CheqdCreateResourceOptions } from '../../dids' +import type { + AnonCredsRegistry, + GetCredentialDefinitionReturn, + GetRevocationStatusListReturn, + GetRevocationRegistryDefinitionReturn, + GetSchemaReturn, + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, + RegisterSchemaReturn, + RegisterSchemaOptions, +} from '@aries-framework/anoncreds' +import type { AgentContext } from '@aries-framework/core' + +import { AriesFrameworkError, JsonTransformer, utils } from '@aries-framework/core' + +import { CheqdDidResolver, CheqdDidRegistrar } from '../../dids' +import { cheqdSdkAnonCredsRegistryIdentifierRegex, parseCheqdDid } from '../utils/identifiers' +import { + CheqdCredentialDefinition, + CheqdRevocationRegistryDefinition, + CheqdRevocationStatusList, + CheqdSchema, +} from '../utils/transform' + +export class CheqdAnonCredsRegistry implements AnonCredsRegistry { + public methodName = 'cheqd' + + /** + * This class supports resolving and registering objects with cheqd identifiers. + * It needs to include support for the schema, credential definition, revocation registry as well + * as the issuer id (which is needed when registering objects). + */ + public readonly supportedIdentifier = cheqdSdkAnonCredsRegistryIdentifierRegex + + public async getSchema(agentContext: AgentContext, schemaId: string): Promise { + try { + const cheqdDidResolver = agentContext.dependencyManager.resolve(CheqdDidResolver) + const parsedDid = parseCheqdDid(schemaId) + if (!parsedDid) { + throw new Error(`Invalid schemaId: ${schemaId}`) + } + + agentContext.config.logger.trace(`Submitting get schema request for schema '${schemaId}' to ledger`) + + const response = await cheqdDidResolver.resolveResource(agentContext, schemaId) + const schema = JsonTransformer.fromJSON(response.resource, CheqdSchema) + + return { + schema: { + attrNames: schema.attrNames, + name: schema.name, + version: schema.version, + issuerId: parsedDid.did, + }, + schemaId, + resolutionMetadata: {}, + schemaMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`, { + error, + schemaId, + }) + + return { + schemaId, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve schema: ${error.message}`, + }, + schemaMetadata: {}, + } + } + } + + public async registerSchema( + agentContext: AgentContext, + options: RegisterSchemaOptions + ): Promise { + try { + const cheqdDidRegistrar = agentContext.dependencyManager.resolve(CheqdDidRegistrar) + + const schema = options.schema + const schemaResource = { + id: utils.uuid(), + name: `${schema.name}-Schema`, + resourceType: 'anonCredsSchema', + data: { + name: schema.name, + version: schema.version, + attrNames: schema.attrNames, + }, + version: schema.version, + } satisfies CheqdCreateResourceOptions + + const response = await cheqdDidRegistrar.createResource(agentContext, schema.issuerId, schemaResource) + if (response.resourceState.state !== 'finished') { + throw new Error(response.resourceState.reason) + } + + return { + schemaState: { + state: 'finished', + schema, + schemaId: `${schema.issuerId}/resources/${schemaResource.id}`, + }, + registrationMetadata: {}, + schemaMetadata: {}, + } + } catch (error) { + agentContext.config.logger.debug(`Error registering schema for did '${options.schema.issuerId}'`, { + error, + did: options.schema.issuerId, + schema: options, + }) + + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + state: 'failed', + schema: options.schema, + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async registerCredentialDefinition( + agentContext: AgentContext, + options: RegisterCredentialDefinitionOptions + ): Promise { + try { + const cheqdDidRegistrar = agentContext.dependencyManager.resolve(CheqdDidRegistrar) + const { credentialDefinition } = options + const schema = await this.getSchema(agentContext, credentialDefinition.schemaId) + const credDefResource = { + id: utils.uuid(), + name: `${schema.schema?.name}-${credentialDefinition.tag}-CredDef`, + resourceType: 'anonCredsCredDef', + data: { + type: credentialDefinition.type, + tag: credentialDefinition.tag, + value: credentialDefinition.value, + schemaId: credentialDefinition.schemaId, + }, + version: utils.uuid(), + } satisfies CheqdCreateResourceOptions + + const response = await cheqdDidRegistrar.createResource( + agentContext, + credentialDefinition.issuerId, + credDefResource + ) + if (response.resourceState.state !== 'finished') { + throw new Error(response.resourceState.reason) + } + + return { + credentialDefinitionState: { + state: 'finished', + credentialDefinition, + credentialDefinitionId: `${credentialDefinition.issuerId}/resources/${credDefResource.id}`, + }, + registrationMetadata: {}, + credentialDefinitionMetadata: {}, + } + } catch (error) { + agentContext.config.logger.error( + `Error registering credential definition for did '${options.credentialDefinition.issuerId}'`, + { + error, + did: options.credentialDefinition.issuerId, + schema: options, + } + ) + + return { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + state: 'failed', + credentialDefinition: options.credentialDefinition, + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async getCredentialDefinition( + agentContext: AgentContext, + credentialDefinitionId: string + ): Promise { + try { + const cheqdDidResolver = agentContext.dependencyManager.resolve(CheqdDidResolver) + const parsedDid = parseCheqdDid(credentialDefinitionId) + if (!parsedDid) { + throw new Error(`Invalid credentialDefinitionId: ${credentialDefinitionId}`) + } + + agentContext.config.logger.trace( + `Submitting get credential definition request for '${credentialDefinitionId}' to ledger` + ) + + const response = await cheqdDidResolver.resolveResource(agentContext, credentialDefinitionId) + const credentialDefinition = JsonTransformer.fromJSON(response.resource, CheqdCredentialDefinition) + return { + credentialDefinition: { + ...credentialDefinition, + issuerId: parsedDid.did, + }, + credentialDefinitionId, + resolutionMetadata: {}, + credentialDefinitionMetadata: (response.resourceMetadata ?? {}) as Record, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { + error, + credentialDefinitionId, + }) + + return { + credentialDefinitionId, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve credential definition: ${error.message}`, + }, + credentialDefinitionMetadata: {}, + } + } + } + + public async getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise { + try { + const cheqdDidResolver = agentContext.dependencyManager.resolve(CheqdDidResolver) + const parsedDid = parseCheqdDid(revocationRegistryDefinitionId) + if (!parsedDid) { + throw new Error(`Invalid revocationRegistryDefinitionId: ${revocationRegistryDefinitionId}`) + } + + agentContext.config.logger.trace( + `Submitting get revocation registry definition request for '${revocationRegistryDefinitionId}' to ledger` + ) + + const response = await cheqdDidResolver.resolveResource( + agentContext, + `${revocationRegistryDefinitionId}&resourceType=anonCredsRevocRegDef` + ) + const revocationRegistryDefinition = JsonTransformer.fromJSON( + response.resource, + CheqdRevocationRegistryDefinition + ) + return { + revocationRegistryDefinition: { + ...revocationRegistryDefinition, + issuerId: parsedDid.did, + }, + revocationRegistryDefinitionId, + resolutionMetadata: {}, + revocationRegistryDefinitionMetadata: (response.resourceMetadata ?? {}) as Record, + } + } catch (error) { + agentContext.config.logger.error( + `Error retrieving revocation registry definition '${revocationRegistryDefinitionId}'`, + { + error, + revocationRegistryDefinitionId, + } + ) + + return { + revocationRegistryDefinitionId, + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve revocation registry definition: ${error.message}`, + }, + revocationRegistryDefinitionMetadata: {}, + } + } + } + + // FIXME: this method doesn't retrieve the revocation status list at a specified time, it just resolves the revocation registry definition + public async getRevocationStatusList( + agentContext: AgentContext, + revocationRegistryId: string, + timestamp: number + ): Promise { + try { + const cheqdDidResolver = agentContext.dependencyManager.resolve(CheqdDidResolver) + const parsedDid = parseCheqdDid(revocationRegistryId) + if (!parsedDid) { + throw new Error(`Invalid revocationRegistryId: ${revocationRegistryId}`) + } + + agentContext.config.logger.trace( + `Submitting get revocation status request for '${revocationRegistryId}' to ledger` + ) + + const response = await cheqdDidResolver.resolveResource( + agentContext, + `${revocationRegistryId}&resourceType=anonCredsStatusList&resourceVersionTime=${timestamp}` + ) + const revocationStatusList = JsonTransformer.fromJSON(response.resource, CheqdRevocationStatusList) + + const statusListTimestamp = response.resourceMetadata?.created?.getUTCSeconds() + if (!statusListTimestamp) { + throw new AriesFrameworkError( + `Unable to extract revocation status list timestamp from resource ${revocationRegistryId}` + ) + } + + return { + revocationStatusList: { + ...revocationStatusList, + issuerId: parsedDid.did, + timestamp: statusListTimestamp, + }, + resolutionMetadata: {}, + revocationStatusListMetadata: (response.resourceMetadata ?? {}) as Record, + } + } catch (error) { + agentContext.config.logger.error(`Error retrieving revocation registry status list '${revocationRegistryId}'`, { + error, + revocationRegistryId, + }) + + return { + resolutionMetadata: { + error: 'notFound', + message: `unable to resolve revocation registry status list: ${error.message}`, + }, + revocationStatusListMetadata: {}, + } + } + } +} diff --git a/packages/cheqd/src/anoncreds/utils/identifiers.ts b/packages/cheqd/src/anoncreds/utils/identifiers.ts new file mode 100644 index 0000000000..ff21b32065 --- /dev/null +++ b/packages/cheqd/src/anoncreds/utils/identifiers.ts @@ -0,0 +1,59 @@ +import type { ParsedDid } from '@aries-framework/core' + +import { TypedArrayEncoder, utils } from '@aries-framework/core' +import { isBase58 } from 'class-validator' + +const ID_CHAR = '([a-z,A-Z,0-9,-])' +const NETWORK = '(testnet|mainnet)' +const IDENTIFIER = `((?:${ID_CHAR}*:)*(${ID_CHAR}+))` +const PATH = `(/[^#?]*)?` +const QUERY = `([?][^#]*)?` +const VERSION_ID = `(.*?)` + +export const cheqdSdkAnonCredsRegistryIdentifierRegex = new RegExp( + `^did:cheqd:${NETWORK}:${IDENTIFIER}${PATH}${QUERY}$` +) + +export const cheqdDidRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}${QUERY}$`) +export const cheqdDidVersionRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/version/${VERSION_ID}${QUERY}$`) +export const cheqdDidVersionsRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/versions${QUERY}$`) +export const cheqdDidMetadataRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/metadata${QUERY}$`) +export const cheqdResourceRegex = new RegExp(`^did:cheqd:${NETWORK}:${IDENTIFIER}/resources/${IDENTIFIER}${QUERY}$`) +export const cheqdResourceMetadataRegex = new RegExp( + `^did:cheqd:${NETWORK}:${IDENTIFIER}/resources/${IDENTIFIER}/metadata${QUERY}` +) + +export type ParsedCheqdDid = ParsedDid & { network: string } +export function parseCheqdDid(didUrl: string): ParsedCheqdDid | null { + if (didUrl === '' || !didUrl) return null + const sections = didUrl.match(cheqdSdkAnonCredsRegistryIdentifierRegex) + if (sections) { + if ( + !( + utils.isValidUuid(sections[2]) || + (isBase58(sections[2]) && TypedArrayEncoder.fromBase58(sections[2]).length == 16) + ) + ) { + return null + } + const parts: ParsedCheqdDid = { + did: `did:cheqd:${sections[1]}:${sections[2]}`, + method: 'cheqd', + network: sections[1], + id: sections[2], + didUrl, + } + if (sections[7]) { + const params = sections[7].slice(1).split('&') + parts.params = {} + for (const p of params) { + const kv = p.split('=') + parts.params[kv[0]] = kv[1] + } + } + if (sections[6]) parts.path = sections[6] + if (sections[8]) parts.fragment = sections[8].slice(1) + return parts + } + return null +} diff --git a/packages/cheqd/src/anoncreds/utils/transform.ts b/packages/cheqd/src/anoncreds/utils/transform.ts new file mode 100644 index 0000000000..47c7b076a7 --- /dev/null +++ b/packages/cheqd/src/anoncreds/utils/transform.ts @@ -0,0 +1,149 @@ +// These functions will throw an error if the JSON doesn't +// match the expected interface, even if the JSON is valid. + +import type { + AnonCredsCredentialDefinition, + AnonCredsRevocationRegistryDefinition, + AnonCredsRevocationStatusList, + AnonCredsSchema, +} from '@aries-framework/anoncreds' + +import { Type } from 'class-transformer' +import { + ArrayMinSize, + Contains, + IsArray, + IsInstance, + IsNumber, + IsObject, + IsOptional, + IsString, + ValidateNested, +} from 'class-validator' + +export class CheqdSchema { + public constructor(options: Omit) { + if (options) { + this.name = options.name + this.attrNames = options.attrNames + this.version = options.version + } + } + + @IsString() + public name!: string + + @IsArray() + @IsString({ each: true }) + @ArrayMinSize(1) + public attrNames!: string[] + + @IsString() + public version!: string +} + +export class CheqdCredentialDefinitionValue { + @IsObject() + public primary!: Record + + @IsObject() + @IsOptional() + public revocation?: unknown +} + +export class CheqdCredentialDefinition { + public constructor(options: Omit) { + if (options) { + this.schemaId = options.schemaId + this.type = options.type + this.tag = options.tag + this.value = options.value + } + } + + @IsString() + public schemaId!: string + + @Contains('CL') + public type!: 'CL' + + @IsString() + public tag!: string + + @ValidateNested() + @IsInstance(CheqdCredentialDefinitionValue) + @Type(() => CheqdCredentialDefinitionValue) + public value!: CheqdCredentialDefinitionValue +} + +export class AccumKey { + @IsString() + public z!: string +} + +export class PublicKeys { + @ValidateNested() + @IsInstance(AccumKey) + @Type(() => AccumKey) + public accumKey!: AccumKey +} + +export class CheqdRevocationRegistryDefinitionValue { + @ValidateNested() + @IsInstance(PublicKeys) + @Type(() => PublicKeys) + public publicKeys!: PublicKeys + + @IsNumber() + public maxCredNum!: number + + @IsString() + public tailsLocation!: string + + @IsString() + public tailsHash!: string +} + +export class CheqdRevocationRegistryDefinition { + public constructor(options: Omit) { + if (options) { + this.revocDefType = options.revocDefType + this.credDefId = options.credDefId + this.tag = options.tag + this.value = options.value + } + } + + @Contains('CL_ACCUM') + public revocDefType!: 'CL_ACCUM' + + @IsString() + public credDefId!: string + + @IsString() + public tag!: string + + @ValidateNested() + @IsInstance(CheqdRevocationRegistryDefinitionValue) + @Type(() => CheqdRevocationRegistryDefinitionValue) + public value!: CheqdRevocationRegistryDefinitionValue +} + +export class CheqdRevocationStatusList { + public constructor(options: Omit) { + if (options) { + this.revRegDefId = options.revRegDefId + this.revocationList = options.revocationList + this.currentAccumulator = options.currentAccumulator + } + } + + @IsString() + public revRegDefId!: string + + @IsNumber({}, { each: true }) + public revocationList!: number[] + + @IsString() + public currentAccumulator!: string +} diff --git a/packages/cheqd/src/dids/CheqdDidRegistrar.ts b/packages/cheqd/src/dids/CheqdDidRegistrar.ts new file mode 100644 index 0000000000..37de51ad1f --- /dev/null +++ b/packages/cheqd/src/dids/CheqdDidRegistrar.ts @@ -0,0 +1,406 @@ +import type { + AgentContext, + DidRegistrar, + DidCreateOptions, + DidCreateResult, + DidDeactivateResult, + DidUpdateResult, + DidDocument, + VerificationMethod, +} from '@aries-framework/core' +import type { CheqdNetwork, DIDDocument, DidStdFee, TVerificationKey, VerificationMethods } from '@cheqd/sdk' +import type { SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' + +import { + DidDocumentRole, + DidRecord, + DidRepository, + KeyType, + Buffer, + isValidPrivateKey, + utils, + TypedArrayEncoder, + getKeyFromVerificationMethod, +} from '@aries-framework/core' +import { MethodSpecificIdAlgo, createDidVerificationMethod } from '@cheqd/sdk' +import { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' + +import { CheqdLedgerService } from '../ledger' + +import { + createMsgCreateDidDocPayloadToSign, + generateDidDoc, + validateSpecCompliantPayload, + createMsgDeactivateDidDocPayloadToSign, +} from './didCheqdUtil' + +export class CheqdDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['cheqd'] + + public async create(agentContext: AgentContext, options: CheqdDidCreateOptions): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + + const { methodSpecificIdAlgo, network, versionId = utils.uuid() } = options.options + const verificationMethod = options.secret?.verificationMethod + let didDocument: DidDocument + + try { + if (options.didDocument && validateSpecCompliantPayload(options.didDocument)) { + didDocument = options.didDocument + } else if (verificationMethod) { + const privateKey = verificationMethod.privateKey + if (privateKey && !isValidPrivateKey(privateKey, KeyType.Ed25519)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid private key provided', + }, + } + } + + const key = await agentContext.wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey: privateKey, + }) + didDocument = generateDidDoc({ + verificationMethod: verificationMethod.type as VerificationMethods, + verificationMethodId: verificationMethod.id || 'key-1', + methodSpecificIdAlgo: (methodSpecificIdAlgo as MethodSpecificIdAlgo) || MethodSpecificIdAlgo.Uuid, + network: network as CheqdNetwork, + publicKey: TypedArrayEncoder.toHex(key.publicKey), + }) satisfies DidDocument + } else { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Provide a didDocument or atleast one verificationMethod with seed in secret', + }, + } + } + + const payloadToSign = await createMsgCreateDidDocPayloadToSign(didDocument as DIDDocument, versionId) + const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) + + const response = await cheqdLedgerService.create(didDocument as DIDDocument, signInputs, versionId) + if (response.code !== 0) { + throw new Error(`${response.rawLog}`) + } + + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + did: didDocument.id, + role: DidDocumentRole.Created, + didDocument, + }) + await didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: didDocument.id, + didDocument, + secret: options.secret, + }, + } + } catch (error) { + agentContext.config.logger.error(`Error registering DID`, error) + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async update(agentContext: AgentContext, options: CheqdDidUpdateOptions): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + + const versionId = options.options?.versionId || utils.uuid() + const verificationMethod = options.secret?.verificationMethod + let didDocument: DidDocument + let didRecord: DidRecord | null + + try { + if (options.didDocument && validateSpecCompliantPayload(options.didDocument)) { + didDocument = options.didDocument + const resolvedDocument = await cheqdLedgerService.resolve(didDocument.id) + didRecord = await didRepository.findCreatedDid(agentContext, didDocument.id) + if (!resolvedDocument.didDocument || resolvedDocument.didDocumentMetadata.deactivated || !didRecord) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Did not found', + }, + } + } + + if (verificationMethod) { + const privateKey = verificationMethod.privateKey + if (privateKey && !isValidPrivateKey(privateKey, KeyType.Ed25519)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid private key provided', + }, + } + } + + const key = await agentContext.wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey: privateKey, + }) + + didDocument.verificationMethod?.concat( + createDidVerificationMethod( + [verificationMethod.type as VerificationMethods], + [ + { + methodSpecificId: didDocument.id.split(':')[3], + didUrl: didDocument.id, + keyId: `${didDocument.id}#${verificationMethod.id}`, + publicKey: TypedArrayEncoder.toHex(key.publicKey), + }, + ] + ) + ) + } + } else { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Provide a valid didDocument', + }, + } + } + + const payloadToSign = await createMsgCreateDidDocPayloadToSign(didDocument as DIDDocument, versionId) + const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) + + const response = await cheqdLedgerService.update(didDocument as DIDDocument, signInputs, versionId) + if (response.code !== 0) { + throw new Error(`${response.rawLog}`) + } + + // Save the did so we know we created it and can issue with it + didRecord.didDocument = didDocument + await didRepository.update(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: didDocument.id, + didDocument, + secret: options.secret, + }, + } + } catch (error) { + agentContext.config.logger.error(`Error updating DID`, error) + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async deactivate( + agentContext: AgentContext, + options: CheqdDidDeactivateOptions + ): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + + const did = options.did + const versionId = options.options?.versionId || utils.uuid() + + try { + const { didDocument, didDocumentMetadata } = await cheqdLedgerService.resolve(did) + const didRecord = await didRepository.findCreatedDid(agentContext, did) + if (!didDocument || didDocumentMetadata.deactivated || !didRecord) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Did not found', + }, + } + } + const payloadToSign = createMsgDeactivateDidDocPayloadToSign(didDocument, versionId) + const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) + const response = await cheqdLedgerService.deactivate(didDocument, signInputs, versionId) + if (response.code !== 0) { + throw new Error(`${response.rawLog}`) + } + + await didRepository.update(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: didDocument.id, + didDocument: didDocument as DidDocument, + secret: options.secret, + }, + } + } catch (error) { + agentContext.config.logger.error(`Error deactivating DID`, error) + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async createResource(agentContext: AgentContext, did: string, resource: CheqdCreateResourceOptions) { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + const { didDocument, didDocumentMetadata } = await cheqdLedgerService.resolve(did) + const didRecord = await didRepository.findCreatedDid(agentContext, did) + if (!didDocument || didDocumentMetadata.deactivated || !didRecord) { + return { + resourceMetadata: {}, + resourceRegistrationMetadata: {}, + resourceState: { + state: 'failed', + reason: `DID: ${did} not found`, + }, + } + } + + try { + let data: Uint8Array + if (typeof resource.data === 'string') { + data = TypedArrayEncoder.fromBase64(resource.data) + } else if (typeof resource.data == 'object') { + data = TypedArrayEncoder.fromString(JSON.stringify(resource.data)) + } else { + data = resource.data + } + + const resourcePayload = MsgCreateResourcePayload.fromPartial({ + collectionId: did.split(':')[3], + id: resource.id, + resourceType: resource.resourceType, + name: resource.name, + version: resource.version, + alsoKnownAs: resource.alsoKnownAs, + data, + }) + const payloadToSign = MsgCreateResourcePayload.encode(resourcePayload).finish() + const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) + const response = await cheqdLedgerService.createResource(did, resourcePayload, signInputs) + if (response.code !== 0) { + throw new Error(`${response.rawLog}`) + } + + return { + resourceMetadata: {}, + resourceRegistrationMetadata: {}, + resourceState: { + state: 'finished', + resourceId: resourcePayload.id, + resource: resourcePayload, + }, + } + } catch (error) { + return { + resourceMetadata: {}, + resourceRegistrationMetadata: {}, + resourceState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + private async signPayload( + agentContext: AgentContext, + payload: Uint8Array, + verificationMethod: VerificationMethod[] = [] + ) { + return await Promise.all( + verificationMethod.map(async (method) => { + const key = getKeyFromVerificationMethod(method) + return { + verificationMethodId: method.id, + signature: await agentContext.wallet.sign({ data: Buffer.from(payload), key }), + } satisfies SignInfo + }) + ) + } +} + +export interface CheqdDidCreateOptions extends DidCreateOptions { + method: 'cheqd' + options: { + network: `${CheqdNetwork}` + fee?: DidStdFee + versionId?: string + methodSpecificIdAlgo?: `${MethodSpecificIdAlgo}` + } + secret: { + verificationMethod?: IVerificationMethod + } +} + +export interface CheqdDidUpdateOptions extends DidCreateOptions { + method: 'cheqd' + did: string + didDocument: DidDocument + options: { + fee?: DidStdFee + versionId?: string + } + secret?: { + verificationMethod: IVerificationMethod + } +} + +export interface CheqdDidDeactivateOptions extends DidCreateOptions { + method: 'cheqd' + did: string + options: { + fee?: DidStdFee + versionId?: string + } +} + +export interface CheqdCreateResourceOptions extends Omit, 'data'> { + data: string | Uint8Array | object +} + +interface IVerificationMethod { + type: `${VerificationMethods}` + id: TVerificationKey + privateKey?: Buffer +} diff --git a/packages/cheqd/src/dids/CheqdDidResolver.ts b/packages/cheqd/src/dids/CheqdDidResolver.ts new file mode 100644 index 0000000000..d599e8d596 --- /dev/null +++ b/packages/cheqd/src/dids/CheqdDidResolver.ts @@ -0,0 +1,192 @@ +import type { ParsedCheqdDid } from '../anoncreds/utils/identifiers' +import type { AgentContext, DidDocument, DidResolutionResult, DidResolver, ParsedDid } from '@aries-framework/core' +import type { Metadata } from '@cheqd/ts-proto/cheqd/resource/v2' + +import { AriesFrameworkError, utils } from '@aries-framework/core' + +import { + cheqdDidMetadataRegex, + cheqdDidRegex, + cheqdDidVersionRegex, + cheqdDidVersionsRegex, + cheqdResourceMetadataRegex, + cheqdResourceRegex, + parseCheqdDid, +} from '../anoncreds/utils/identifiers' +import { CheqdLedgerService } from '../ledger' + +import { filterResourcesByNameAndType, getClosestResourceVersion, renderResourceData } from './didCheqdUtil' + +export class CheqdDidResolver implements DidResolver { + public readonly supportedMethods = ['cheqd'] + + public async resolve(agentContext: AgentContext, did: string, parsed: ParsedDid): Promise { + const didDocumentMetadata = {} + + try { + const parsedDid = parseCheqdDid(parsed.didUrl) + if (!parsedDid) { + throw new Error('Invalid DID') + } + + switch (did) { + case did.match(cheqdDidRegex)?.input: + return await this.resolveDidDoc(agentContext, parsedDid.did) + case did.match(cheqdDidVersionRegex)?.input: { + const version = did.split('/')[2] + return await this.resolveDidDoc(agentContext, parsedDid.did, version) + } + case did.match(cheqdDidVersionsRegex)?.input: + return await this.resolveAllDidDocVersions(agentContext, parsedDid) + case did.match(cheqdDidMetadataRegex)?.input: + return await this.dereferenceCollectionResources(agentContext, parsedDid) + case did.match(cheqdResourceMetadataRegex)?.input: + return await this.dereferenceResourceMetadata(agentContext, parsedDid) + default: + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'Invalid request', + message: `Unsupported did Url: '${did}'`, + }, + } + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } + + public async resolveResource(agentContext: AgentContext, did: string) { + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + try { + const parsedDid = parseCheqdDid(did) + if (!parsedDid) { + throw new Error('Invalid DID') + } + + const { params, id } = parsedDid + let resourceId: string + if (did.match(cheqdResourceRegex)?.input) { + resourceId = did.split('/')[2] + } else if (params && params.resourceName && params.resourceType) { + let resources = (await cheqdLedgerService.resolveCollectionResources(parsedDid.did, id)).resources + resources = filterResourcesByNameAndType(resources, params.resourceName, params.resourceType) + if (!resources.length) { + throw new Error(`No resources found`) + } + + let resource: Metadata | undefined + if (params.version) { + resource = resources.find((resource) => resource.version == params.version) + } else { + const date = params.resourceVersionTime ? new Date(Number(params.resourceVersionTime) * 1000) : new Date() + // find the resourceId closest to the created time + resource = getClosestResourceVersion(resources, date) + } + + if (!resource) { + throw new Error(`No resources found`) + } + + resourceId = resource.id + } else { + return { + error: 'notFound', + message: `resolver_error: Invalid did url '${did}'`, + } + } + if (!utils.isValidUuid(resourceId)) { + throw new Error('Invalid resource Id') + } + + const { resource, metadata } = await cheqdLedgerService.resolveResource(parsedDid.did, id, resourceId) + if (!resource || !metadata) { + throw new Error('resolver_error: Unable to resolve resource, Please try again') + } + + const result = await renderResourceData(resource.data, metadata.mediaType) + return { + resource: result, + resourceMetadata: metadata, + resourceResolutionMetadata: {}, + } + } catch (error) { + return { + error: 'notFound', + message: `resolver_error: Unable to resolve resource '${did}': ${error}`, + } + } + } + + private async resolveAllDidDocVersions(agentContext: AgentContext, parsedDid: ParsedCheqdDid) { + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + const { did } = parsedDid + + const { didDocumentVersionsMetadata } = await cheqdLedgerService.resolveMetadata(did) + return { + didDocument: { id: did } as DidDocument, + didDocumentMetadata: didDocumentVersionsMetadata, + didResolutionMetadata: {}, + } + } + + private async dereferenceCollectionResources(agentContext: AgentContext, parsedDid: ParsedCheqdDid) { + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + const { did, id } = parsedDid + + const metadata = await cheqdLedgerService.resolveCollectionResources(did, id) + return { + didDocument: { id: did } as DidDocument, + didDocumentMetadata: { + linkedResourceMetadata: metadata, + }, + didResolutionMetadata: {}, + } + } + + private async dereferenceResourceMetadata(agentContext: AgentContext, parsedDid: ParsedCheqdDid) { + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + const { did, id } = parsedDid + + if (!parsedDid.path) { + throw new AriesFrameworkError(`Missing path in did ${parsedDid.did}`) + } + + const [, , resourceId] = parsedDid.path.split('/') + + if (!resourceId) { + throw new AriesFrameworkError(`Missing resource id in didUrl ${parsedDid.didUrl}`) + } + + const metadata = await cheqdLedgerService.resolveResourceMetadata(did, id, resourceId) + return { + didDocument: { id: did } as DidDocument, + didDocumentMetadata: { + linkedResourceMetadata: metadata, + }, + didResolutionMetadata: {}, + } + } + + private async resolveDidDoc(agentContext: AgentContext, did: string, version?: string): Promise { + const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService) + + const { didDocument, didDocumentMetadata } = await cheqdLedgerService.resolve(did, version) + const { resources } = await cheqdLedgerService.resolveCollectionResources(did, did.split(':')[3]) + didDocumentMetadata.linkedResourceMetadata = resources + + return { + didDocument: didDocument as DidDocument, + didDocumentMetadata, + didResolutionMetadata: {}, + } + } +} diff --git a/packages/cheqd/src/dids/didCheqdUtil.ts b/packages/cheqd/src/dids/didCheqdUtil.ts new file mode 100644 index 0000000000..37a2c94ad1 --- /dev/null +++ b/packages/cheqd/src/dids/didCheqdUtil.ts @@ -0,0 +1,152 @@ +import type { DidDocument } from '@aries-framework/core' +import type { CheqdNetwork, DIDDocument, MethodSpecificIdAlgo, TVerificationKey } from '@cheqd/sdk' +import type { Metadata } from '@cheqd/ts-proto/cheqd/resource/v2' + +import { AriesFrameworkError, JsonEncoder, TypedArrayEncoder } from '@aries-framework/core' +import { + createDidPayload, + createDidVerificationMethod, + createVerificationKeys, + DIDModule, + VerificationMethods, +} from '@cheqd/sdk' +import { MsgCreateDidDocPayload, MsgDeactivateDidDocPayload } from '@cheqd/ts-proto/cheqd/did/v2' +import { EnglishMnemonic as _ } from '@cosmjs/crypto' +import { DirectSecp256k1HdWallet, DirectSecp256k1Wallet } from '@cosmjs/proto-signing' + +export function validateSpecCompliantPayload(didDocument: DidDocument): SpecValidationResult { + // id is required, validated on both compile and runtime + if (!didDocument.id && !didDocument.id.startsWith('did:cheqd:')) return { valid: false, error: 'id is required' } + + // verificationMethod is required + if (!didDocument.verificationMethod) return { valid: false, error: 'verificationMethod is required' } + + // verificationMethod must be an array + if (!Array.isArray(didDocument.verificationMethod)) + return { valid: false, error: 'verificationMethod must be an array' } + + // verificationMethod must be not be empty + if (!didDocument.verificationMethod.length) return { valid: false, error: 'verificationMethod must be not be empty' } + + // verificationMethod types must be supported + const isValidVerificationMethod = didDocument.verificationMethod.every((vm) => { + switch (vm.type) { + case VerificationMethods.Ed255192020: + return vm.publicKeyMultibase != null + case VerificationMethods.JWK: + return vm.publicKeyJwk != null + case VerificationMethods.Ed255192018: + return vm.publicKeyBase58 != null + default: + return false + } + }) + + if (!isValidVerificationMethod) return { valid: false, error: 'verificationMethod publicKey is Invalid' } + + const isValidService = didDocument.service + ? didDocument?.service?.every((s) => { + return s?.serviceEndpoint && s?.id && s?.type + }) + : true + + if (!isValidService) return { valid: false, error: 'Service is Invalid' } + return { valid: true } as SpecValidationResult +} + +// Create helpers in sdk like MsgCreateDidDocPayload.fromDIDDocument to replace the below +export async function createMsgCreateDidDocPayloadToSign(didPayload: DIDDocument, versionId: string) { + didPayload.service = didPayload.service?.map((e) => { + return { + ...e, + serviceEndpoint: Array.isArray(e.serviceEndpoint) ? e.serviceEndpoint : [e.serviceEndpoint], + } + }) + const { protobufVerificationMethod, protobufService } = await DIDModule.validateSpecCompliantPayload(didPayload) + return MsgCreateDidDocPayload.encode( + MsgCreateDidDocPayload.fromPartial({ + context: didPayload?.['@context'], + id: didPayload.id, + controller: didPayload.controller, + verificationMethod: protobufVerificationMethod, + authentication: didPayload.authentication, + assertionMethod: didPayload.assertionMethod, + capabilityInvocation: didPayload.capabilityInvocation, + capabilityDelegation: didPayload.capabilityDelegation, + keyAgreement: didPayload.keyAgreement, + service: protobufService, + alsoKnownAs: didPayload.alsoKnownAs, + versionId, + }) + ).finish() +} + +export function createMsgDeactivateDidDocPayloadToSign(didPayload: DIDDocument, versionId?: string) { + return MsgDeactivateDidDocPayload.encode( + MsgDeactivateDidDocPayload.fromPartial({ + id: didPayload.id, + versionId, + }) + ).finish() +} + +export type SpecValidationResult = { + valid: boolean + error?: string +} + +export function generateDidDoc(options: IDidDocOptions) { + const { verificationMethod, methodSpecificIdAlgo, verificationMethodId, network, publicKey } = options + const verificationKeys = createVerificationKeys(publicKey, methodSpecificIdAlgo, verificationMethodId, network) + if (!verificationKeys) { + throw new Error('Invalid DID options') + } + const verificationMethods = createDidVerificationMethod([verificationMethod], [verificationKeys]) + + return createDidPayload(verificationMethods, [verificationKeys]) as DidDocument +} + +export interface IDidDocOptions { + verificationMethod: VerificationMethods + verificationMethodId: TVerificationKey + methodSpecificIdAlgo: MethodSpecificIdAlgo + network: CheqdNetwork + publicKey: string +} + +export function getClosestResourceVersion(resources: Metadata[], date: Date) { + const result = resources.sort(function (a, b) { + if (!a.created || !b.created) throw new AriesFrameworkError("Missing required property 'created' on resource") + const distancea = Math.abs(date.getTime() - a.created.getTime()) + const distanceb = Math.abs(date.getTime() - b.created.getTime()) + return distancea - distanceb + }) + return result[0] +} + +export function filterResourcesByNameAndType(resources: Metadata[], name: string, type: string) { + return resources.filter((resource) => resource.name == name && resource.resourceType == type) +} + +export async function renderResourceData(data: Uint8Array, mimeType: string) { + if (mimeType == 'application/json') { + return await JsonEncoder.fromBuffer(data) + } else if (mimeType == 'text/plain') { + return TypedArrayEncoder.toUtf8String(data) + } else { + return TypedArrayEncoder.toBase64URL(data) + } +} + +export class EnglishMnemonic extends _ { + public static readonly _mnemonicMatcher = /^[a-z]+( [a-z]+)*$/ +} + +export function getCosmosPayerWallet(cosmosPayerSeed?: string) { + if (!cosmosPayerSeed || cosmosPayerSeed === '') { + return DirectSecp256k1HdWallet.generate() + } + return EnglishMnemonic._mnemonicMatcher.test(cosmosPayerSeed) + ? DirectSecp256k1HdWallet.fromMnemonic(cosmosPayerSeed, { prefix: 'cheqd' }) + : DirectSecp256k1Wallet.fromKey(TypedArrayEncoder.fromString(cosmosPayerSeed.replace(/^0x/, '')), 'cheqd') +} diff --git a/packages/cheqd/src/dids/index.ts b/packages/cheqd/src/dids/index.ts new file mode 100644 index 0000000000..315b1c0982 --- /dev/null +++ b/packages/cheqd/src/dids/index.ts @@ -0,0 +1,8 @@ +export { + CheqdDidRegistrar, + CheqdDidCreateOptions, + CheqdDidDeactivateOptions, + CheqdDidUpdateOptions, + CheqdCreateResourceOptions, +} from './CheqdDidRegistrar' +export { CheqdDidResolver } from './CheqdDidResolver' diff --git a/packages/cheqd/src/index.ts b/packages/cheqd/src/index.ts new file mode 100644 index 0000000000..4270e5c072 --- /dev/null +++ b/packages/cheqd/src/index.ts @@ -0,0 +1,17 @@ +// Dids +export { + CheqdDidRegistrar, + CheqdDidCreateOptions, + CheqdDidDeactivateOptions, + CheqdDidUpdateOptions, + CheqdDidResolver, +} from './dids' + +// AnonCreds +export { CheqdAnonCredsRegistry } from './anoncreds' + +export { CheqdLedgerService } from './ledger' + +export { CheqdModule } from './CheqdModule' + +export { CheqdModuleConfig, CheqdModuleConfigOptions } from './CheqdModuleConfig' diff --git a/packages/cheqd/src/ledger/CheqdLedgerService.ts b/packages/cheqd/src/ledger/CheqdLedgerService.ts new file mode 100644 index 0000000000..36adf6eb2a --- /dev/null +++ b/packages/cheqd/src/ledger/CheqdLedgerService.ts @@ -0,0 +1,123 @@ +import type { AbstractCheqdSDKModule, CheqdSDK, DidStdFee, DIDDocument } from '@cheqd/sdk' +import type { SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' +import type { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' +import type { DirectSecp256k1HdWallet, DirectSecp256k1Wallet } from '@cosmjs/proto-signing' + +import { injectable } from '@aries-framework/core' +import { createCheqdSDK, DIDModule, ResourceModule, CheqdNetwork } from '@cheqd/sdk' + +import { CheqdModuleConfig } from '../CheqdModuleConfig' +import { parseCheqdDid } from '../anoncreds/utils/identifiers' +import { getCosmosPayerWallet } from '../dids/didCheqdUtil' + +export interface ICheqdLedgerConfig { + network: string + rpcUrl: string + readonly cosmosPayerWallet: Promise + sdk?: CheqdSDK +} + +export enum DefaultRPCUrl { + Mainnet = 'https://rpc.cheqd.net', + Testnet = 'https://rpc.cheqd.network', +} + +@injectable() +export class CheqdLedgerService { + private networks: ICheqdLedgerConfig[] + + public constructor(cheqdSdkModuleConfig: CheqdModuleConfig) { + this.networks = cheqdSdkModuleConfig.networks.map((config) => { + const { network, rpcUrl, cosmosPayerSeed } = config + return { + network, + rpcUrl: rpcUrl ? rpcUrl : network === CheqdNetwork.Mainnet ? DefaultRPCUrl.Mainnet : DefaultRPCUrl.Testnet, + cosmosPayerWallet: getCosmosPayerWallet(cosmosPayerSeed), + } + }) + } + + public async connect() { + for (const network of this.networks) { + network.sdk = await createCheqdSDK({ + modules: [DIDModule as unknown as AbstractCheqdSDKModule, ResourceModule as unknown as AbstractCheqdSDKModule], + rpcUrl: network.rpcUrl, + wallet: await network.cosmosPayerWallet.catch(() => { + throw new Error(`[did-provider-cheqd]: valid cosmosPayerSeed is required`) + }), + }) + } + } + + private getSdk(did: string) { + const parsedDid = parseCheqdDid(did) + if (!parsedDid) { + throw new Error('Invalid DID') + } + if (this.networks.length === 0) { + throw new Error('No cheqd networks configured') + } + + const network = this.networks.find((network) => network.network === parsedDid.network) + if (!network || !network.sdk) { + throw new Error('Network not configured') + } + return network.sdk + } + + public async create( + didPayload: DIDDocument, + signInputs: SignInfo[], + versionId?: string | undefined, + fee?: DidStdFee + ) { + return await this.getSdk(didPayload.id).createDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) + } + + public async update( + didPayload: DIDDocument, + signInputs: SignInfo[], + versionId?: string | undefined, + fee?: DidStdFee + ) { + return await this.getSdk(didPayload.id).updateDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) + } + + public async deactivate( + didPayload: DIDDocument, + signInputs: SignInfo[], + versionId?: string | undefined, + fee?: DidStdFee + ) { + return await this.getSdk(didPayload.id).deactivateDidDocTx(signInputs, didPayload, '', fee, undefined, versionId) + } + + public async resolve(did: string, version?: string) { + return version ? await this.getSdk(did).queryDidDocVersion(did, version) : await this.getSdk(did).queryDidDoc(did) + } + + public async resolveMetadata(did: string) { + return await this.getSdk(did).queryAllDidDocVersionsMetadata(did) + } + + public async createResource( + did: string, + resourcePayload: Partial, + signInputs: SignInfo[], + fee?: DidStdFee + ) { + return await this.getSdk(did).createLinkedResourceTx(signInputs, resourcePayload, '', fee, undefined) + } + + public async resolveResource(did: string, collectionId: string, resourceId: string) { + return await this.getSdk(did).queryLinkedResource(collectionId, resourceId) + } + + public async resolveCollectionResources(did: string, collectionId: string) { + return await this.getSdk(did).queryLinkedResources(collectionId) + } + + public async resolveResourceMetadata(did: string, collectionId: string, resourceId: string) { + return await this.getSdk(did).queryLinkedResourceMetadata(collectionId, resourceId) + } +} diff --git a/packages/cheqd/src/ledger/index.ts b/packages/cheqd/src/ledger/index.ts new file mode 100644 index 0000000000..db2eec776a --- /dev/null +++ b/packages/cheqd/src/ledger/index.ts @@ -0,0 +1 @@ +export { CheqdLedgerService } from './CheqdLedgerService' diff --git a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts new file mode 100644 index 0000000000..e2b8e1dd1b --- /dev/null +++ b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts @@ -0,0 +1,132 @@ +import type { CheqdDidCreateOptions } from '../src' +import type { DidDocument } from '@aries-framework/core' + +import { Agent, TypedArrayEncoder } from '@aries-framework/core' +import { generateKeyPairFromSeed } from '@stablelib/ed25519' + +import { getAgentOptions } from '../../core/tests/helpers' + +import { validService } from './setup' +import { getCheqdModules } from './setupCheqdModule' + +const agentOptions = getAgentOptions('Faber Dids Registrar', {}, getCheqdModules()) + +describe('Cheqd DID registrar', () => { + let agent: Agent> + + beforeAll(async () => { + agent = new Agent(agentOptions) + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should create a did:cheqd did', async () => { + // Generate a seed and the cheqd did. This allows us to create a new did every time + // but still check if the created output document is as expected. + const privateKey = TypedArrayEncoder.fromString( + Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + ) + const publicKeyEd25519 = generateKeyPairFromSeed(privateKey).publicKey + const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) + const did = await agent.dids.create({ + method: 'cheqd', + secret: { + verificationMethod: { + id: 'key-1', + type: 'Ed25519VerificationKey2018', + privateKey, + }, + }, + options: { + network: 'testnet', + methodSpecificIdAlgo: 'base58btc', + }, + }) + expect(did).toMatchObject({ + didState: { + state: 'finished', + didDocument: { + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + publicKeyBase58: ed25519PublicKeyBase58, + }, + ], + }, + }, + }) + }) + + it('should create a did:cheqd using Ed25519VerificationKey2020', async () => { + const did = await agent.dids.create({ + method: 'cheqd', + secret: { + verificationMethod: { + id: 'key-1', + type: 'Ed25519VerificationKey2020', + }, + }, + options: { + network: 'testnet', + methodSpecificIdAlgo: 'uuid', + }, + }) + expect(did.didState).toMatchObject({ state: 'finished' }) + }) + + it('should create a did:cheqd using JsonWebKey2020', async () => { + const createResult = await agent.dids.create({ + method: 'cheqd', + secret: { + verificationMethod: { + id: 'key-11', + type: 'JsonWebKey2020', + }, + }, + options: { + network: 'testnet', + methodSpecificIdAlgo: 'base58btc', + }, + }) + expect(createResult).toMatchObject({ + didState: { + state: 'finished', + didDocument: { + verificationMethod: [{ type: 'JsonWebKey2020' }], + }, + }, + }) + expect(createResult.didState.did).toBeDefined() + const did = createResult.didState.did as string + const didDocument = createResult.didState.didDocument as DidDocument + didDocument.service = [validService(did)] + + const updateResult = await agent.dids.update({ + did, + didDocument, + }) + expect(updateResult).toMatchObject({ + didState: { + state: 'finished', + didDocument, + }, + }) + + const deactivateResult = await agent.dids.deactivate({ did }) + expect(deactivateResult).toMatchObject({ + didState: { + state: 'finished', + didDocument, + }, + }) + + const resolvedDocument = await agent.dids.resolve(did) + expect(resolvedDocument.didDocumentMetadata.deactivated).toBe(true) + }) +}) diff --git a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts new file mode 100644 index 0000000000..b7778ec41c --- /dev/null +++ b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts @@ -0,0 +1,69 @@ +import { Agent, JsonTransformer } from '@aries-framework/core' + +import { getAgentOptions } from '../../core/tests/helpers' +import { getClosestResourceVersion } from '../src/dids/didCheqdUtil' + +import { getCheqdModules } from './setupCheqdModule' + +const agent = new Agent(getAgentOptions('Indy SDK Sov DID resolver', {}, getCheqdModules())) + +describe('Cheqd DID resolver', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + it('should resolve a did:cheqd:testnet did', async () => { + const did = await agent.dids.resolve('did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8') + expect(JsonTransformer.toJSON(did)).toMatchObject({ + didDocument: { + '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/ed25519-2020/v1'], + id: 'did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8', + controller: ['did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8'], + verificationMethod: [ + { + controller: 'did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8', + id: 'did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8#key-1', + publicKeyMultibase: 'z6MksPpyxgw5aFymMboa81CQ7h1kJJ9yehNzPgo714y1HrAA', + type: 'Ed25519VerificationKey2020', + }, + ], + authentication: ['did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8#key-1'], + }, + didDocumentMetadata: { + created: '2022-10-17T13:42:37.000Z', + updated: '0001-01-01T00:00:00.000Z', + deactivated: false, + versionId: '7314e3e5-f9cc-50e9-b249-348963937c96', + nextVersionId: '', + }, + didResolutionMetadata: {}, + }) + }) + + it('should getClosestResourceVersion', async () => { + const did = await agent.dids.resolve('did:cheqd:testnet:SiVQgrFZ7jFZFrTGstT4ZD') + let resource = getClosestResourceVersion(did.didDocumentMetadata.linkedResourceMetadata, new Date()) + expect(resource).toMatchObject({ + id: '0b02ebf4-07c4-4df7-9015-e93c21108240', + }) + resource = getClosestResourceVersion( + did.didDocumentMetadata.linkedResourceMetadata, + new Date('2022-11-16T10:56:34Z') + ) + expect(resource).toMatchObject({ + id: '8140ec3a-d8bb-4f59-9784-a1cbf91a4a35', + }) + resource = getClosestResourceVersion( + did.didDocumentMetadata.linkedResourceMetadata, + new Date('2022-11-16T11:41:48Z') + ) + expect(resource).toMatchObject({ + id: 'a20aa56a-a76f-4828-8a98-4c85d9494545', + }) + }) +}) diff --git a/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts b/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts new file mode 100644 index 0000000000..b5cc7b0401 --- /dev/null +++ b/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts @@ -0,0 +1,48 @@ +import type { DIDDocument } from '@cheqd/sdk' + +import { DidDocument } from '@aries-framework/core' + +import { + createMsgCreateDidDocPayloadToSign, + createMsgDeactivateDidDocPayloadToSign, + validateSpecCompliantPayload, +} from '../src/dids/didCheqdUtil' + +import { validDid, validDidDoc } from './setup' + +describe('Test Cheqd Did Utils', () => { + it('should validate did spec compliant payload', () => { + const didDoc = validDidDoc() + const result = validateSpecCompliantPayload(didDoc) + expect(result.valid).toBe(true) + expect(result.error).toBeUndefined() + }) + + it('should detect invalid verification method', () => { + const result = validateSpecCompliantPayload( + new DidDocument({ + id: validDid, + verificationMethod: [ + { + id: validDid + '#key-1', + publicKeyBase58: 'asca12e3as', + type: 'JsonWebKey2020', + controller: validDid, + }, + ], + }) + ) + expect(result.valid).toBe(false) + expect(result.error).toBeDefined() + }) + + it('should create MsgCreateDidDocPayloadToSign', async () => { + const result = await createMsgCreateDidDocPayloadToSign(validDidDoc() as DIDDocument, '1.0') + expect(result).toBeDefined() + }) + + it('should create MsgDeactivateDidDocPayloadToSign', async () => { + const result = createMsgDeactivateDidDocPayloadToSign({ id: validDid }, '2.0') + expect(result).toBeDefined() + }) +}) diff --git a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts new file mode 100644 index 0000000000..1f872e344b --- /dev/null +++ b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts @@ -0,0 +1,243 @@ +import type { CheqdDidCreateOptions } from '../src' + +import { Agent, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' + +import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' +import { CheqdAnonCredsRegistry } from '../src/anoncreds' + +import { getCheqdModules } from './setupCheqdModule' + +const agentConfig = getAgentConfig('cheqdAnonCredsRegistry') + +const agent = new Agent({ + config: agentConfig, + dependencies: agentDependencies, + modules: getCheqdModules('000000000000000000000000000cheqd'), +}) + +const cheqdAnonCredsRegistry = new CheqdAnonCredsRegistry() + +let issuerId: string + +describe('cheqdAnonCredsRegistry', () => { + beforeAll(async () => { + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + // One test as the credential definition depends on the schema + test('register and resolve a schema and credential definition', async () => { + const privateKey = TypedArrayEncoder.fromString('000000000000000000000000000cheqd') + + const did = await agent.dids.create({ + method: 'cheqd', + secret: { + verificationMethod: { + id: 'key-10', + type: 'Ed25519VerificationKey2020', + privateKey, + }, + }, + options: { + network: 'testnet', + methodSpecificIdAlgo: 'uuid', + }, + }) + expect(did.didState).toMatchObject({ state: 'finished' }) + issuerId = did.didState.did as string + + const dynamicVersion = `1.${Math.random() * 100}` + + const schemaResult = await cheqdAnonCredsRegistry.registerSchema(agent.context, { + schema: { + attrNames: ['name'], + issuerId, + name: 'test11', + version: dynamicVersion, + }, + options: {}, + }) + + expect(JsonTransformer.toJSON(schemaResult)).toMatchObject({ + schemaState: { + state: 'finished', + schema: { + attrNames: ['name'], + issuerId, + name: 'test11', + version: dynamicVersion, + }, + schemaId: `${schemaResult.schemaState.schemaId}`, + }, + registrationMetadata: {}, + schemaMetadata: {}, + }) + + const schemaResponse = await cheqdAnonCredsRegistry.getSchema(agent.context, `${schemaResult.schemaState.schemaId}`) + expect(schemaResponse).toMatchObject({ + schema: { + attrNames: ['name'], + name: 'test11', + version: dynamicVersion, + issuerId, + }, + schemaId: `${schemaResult.schemaState.schemaId}`, + resolutionMetadata: {}, + schemaMetadata: {}, + }) + + const credentialDefinitionResult = await cheqdAnonCredsRegistry.registerCredentialDefinition(agent.context, { + credentialDefinition: { + issuerId, + tag: 'TAG', + schemaId: `${schemaResult.schemaState.schemaId}`, + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + options: {}, + }) + + expect(credentialDefinitionResult).toMatchObject({ + credentialDefinitionState: { + credentialDefinition: { + issuerId, + tag: 'TAG', + schemaId: `${schemaResult.schemaState.schemaId}`, + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + credentialDefinitionId: `${credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId}`, + state: 'finished', + }, + }) + + const credentialDefinitionResponse = await cheqdAnonCredsRegistry.getCredentialDefinition( + agent.context, + credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId as string + ) + + expect(credentialDefinitionResponse).toMatchObject({ + credentialDefinitionId: `${credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId}`, + credentialDefinition: { + issuerId, + schemaId: `${schemaResult.schemaState.schemaId}`, + tag: 'TAG', + type: 'CL', + value: { + primary: { + n: '92511867718854414868106363741369833735017762038454769060600859608405811709675033445666654908195955460485998711087020152978597220168927505650092431295783175164390266561239892662085428655566792056852960599485298025843840058914610127716620252006466964070280255168745873592143068949458568751438337748294055976926080232538440619420568859737673474560851456027625679328271511966332808025880807996449998057729417608399774744254122385012832309402226532031122728445959276178939234308090390331654445053482963947804769291501664200141562885660084823885847247231002821472258218384342423605116504024514572826071246440130942849549441', + r: { + age: '676933340341980399002624386891134393471002096508227567343731826159610079436978196421307099268754545293545727546242372579987825752872485684085629459107300175443328323289748793060894500514926703654606851666031895448970879827423190730510730624784665299646624113512701254199984520803796529034094958026048762178753193812250643294518237843809104055653333871102658177900702978008644780459400512716361564897282969982554031820285585105004870317861287847206222714589633178648982299799311192432563797220854755882933052881306804544233529886513105815543097685128456041780804442879272476590077760678785460726492895806240870944398', + master_secret: + '57770757113548032970308439965749734133430520933173186296299026579579930337912607419798836831937319372744879560676750427054135869214212225572618340088847222727882935159356459822445182287686057012197046378986248048722180093079919306125315662058290895629438767985427829790980355162853804522854494960613869765167538645624719923127052541372069255024631093663068055100579264049925388231368871107383977060590248865498902704546409806115171120555709438784189721957301548212242748685629860268468247494986146122636455769804467583612610341632602695197189514316033637331733820369170763954604394734655429769801516997967996980978751', + }, + rctxt: + '19574881057684356733946284215946569464410211018678168661028327420122678446653210056362495902735819742274128834330867933095119512313591151219353395069123546495720010325822330866859140765940839241212947354612836044244554152389691282543839111284006009168728161183863936810142428875817934316327118674532328892591410224676539770085459540786747902789677759379901079898127879301595929571621032704093287675668250862222728331030586585586110859977896767318814398026750215625180255041545607499673023585546720788973882263863911222208020438685873501025545464213035270207099419236974668665979962146355749687924650853489277747454993', + s: '80388543865249952799447792504739237616187770512259677275061283897050980768551818104137338144380636412773836688624071360386172349725818126495487584981520630638409717065318132420766896092370913800616033623618952639023946750307405126873476182540669638841562357523429245685476919178722373320218824590869735129801004394337640642997250464303104754942997839179333543643110326022824394934965538190976474473353762308333205671176627192797138375084260446324344637548455228161138089974447059481109651156379803576163576511072261388342837813901850712083922506433336723723235701670225584863772222447543742649328218950436824219992164', + z: '18569464356833363098514177097771727133940629758890641648661259687745137028161881113251218061243607037717553708179509640909238773964066423807945164288256211132195919975343578956381001087353353060599758005375631247614777454313440511375923345538396573548499287265163879524050255226779884271432737062283353279122281220812931572456820130441114446870167673796490210349453498315913599982158253821945225264065364670730546176140788405935081171854642125236557475395879246419105888077042924382595999612137336915304205628167917473420377397118829734604949103124514367857266518654728464539418834291071874052392799652266418817991437', + }, + }, + }, + }) + }) + + // Should not resolve invalid schema + test('false test cases', async () => { + const invalidSchemaResourceId = + 'did:cheqd:testnet:d8ac0372-0d4b-413e-8ef5-8e8f07822b2c/resources/ffd001c2-1f80-4cd8-84b2-945fba309457' + const schemaResponse = await cheqdAnonCredsRegistry.getSchema(agent.context, `${invalidSchemaResourceId}`) + + expect(schemaResponse).toMatchObject({ + resolutionMetadata: { + error: 'notFound', + }, + schemaMetadata: {}, + }) + }) + + // Should resolve query based url + test('resolve query based url', async () => { + const schemaResourceId = + 'did:cheqd:testnet:d8ac0372-0d4b-413e-8ef5-8e8f07822b2c?resourceName=test - 11&resourceType=anonCredsSchema' + const schemaResponse = await cheqdAnonCredsRegistry.getSchema(agent.context, `${schemaResourceId}`) + + expect(schemaResponse).toMatchObject({ + schema: { + attrNames: ['name'], + name: 'test - 11', + }, + }) + }) + + // Should resolve revocationRegistryDefinition and statusList + test('resolve revocation registry definition and statusList', async () => { + const revocationRegistryId = 'did:cheqd:testnet:e42ccb8b-78e8-4e54-9d11-f375153d63f8?resourceName=universityDegree' + const revocationDefinitionResponse = await cheqdAnonCredsRegistry.getRevocationRegistryDefinition( + agent.context, + revocationRegistryId + ) + + expect(revocationDefinitionResponse.revocationRegistryDefinition).toMatchObject({ + revocDefType: 'CL_ACCUM', + credDefId: 'did:cheqd:mainnet:zF7rhDBfUt9d1gJPjx7s1J/resources/77465164-5646-42d9-9a0a-f7b2dcb855c0', + tag: '2.0', + value: { + publicKeys: { + accumKey: { + z: '1 08C6E71D1CE1D1690AED25BC769646538BEC69600829CE1FB7AA788479E0B878 1 026909513F9901655B3F9153071DB43A846418F00F305BA25FE851730ED41102 1 10E9D5438AE95AE2BED78A33716BFF923A0F4CA980A9A599C25A24A2295658DA 1 0A04C318A0DFD29ABB1F1D8DD697999F9B89D6682272C591B586D53F8A9D3DC4 1 0501E5FFCE863E08D209C2FA7B390A5AA91F462BB71957CF8DB41EACDC9EB222 1 14BED208817ACB398D8476212C987E7FF77265A72F145EF2853DDB631758AED4 1 180774B2F67179FB62BD452A15F6C034599DA7BF45CC15AA2138212B53A0C110 1 00A0B87DDFFC047BE07235DD11D31226A9F5FA1E03D49C03843AA36A8AF68194 1 10218703955E0B53DB93A8D2D593EB8120A9C9739F127325CB0865ECA4B2B42F 1 08685A263CD0A045FD845AAC6DAA0FDDAAD0EC222C1A0286799B69F37CD75919 1 1FA3D27E70C185C1A16D9A83D3EE7D8CACE727A99C882EE649F87BD52E9EEE47 1 054704706B95A154F5AFC3FBB536D38DC9DCB9702EA0BFDCCB2E36A3AA23F3EC', + }, + }, + maxCredNum: 666, + tailsLocation: 'https://my.revocations.tails/tailsfile.txt', + tailsHash: '91zvq2cFmBZmHCcLqFyzv7bfehHH5rMhdAG5wTjqy2PE', + }, + }) + + const revocationStatusListResponse = await cheqdAnonCredsRegistry.getRevocationStatusList( + agent.context, + revocationRegistryId, + 1680789403 + ) + + expect(revocationStatusListResponse.revocationStatusList).toMatchObject({ + revRegDefId: `${revocationRegistryId}&resourceType=anonCredsRevocRegDef`, + revocationList: [ + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + currentAccumulator: + '21 124C594B6B20E41B681E92B2C43FD165EA9E68BC3C9D63A82C8893124983CAE94 21 124C5341937827427B0A3A32113BD5E64FB7AB39BD3E5ABDD7970874501CA4897 6 5438CB6F442E2F807812FD9DC0C39AFF4A86B1E6766DBB5359E86A4D70401B0F 4 39D1CA5C4716FFC4FE0853C4FF7F081DFD8DF8D2C2CA79705211680AC77BF3A1 6 70504A5493F89C97C225B68310811A41AD9CD889301F238E93C95AD085E84191 4 39582252194D756D5D86D0EED02BF1B95CE12AED2FA5CD3C53260747D891993C', + }) + }) +}) diff --git a/packages/cheqd/tests/setup.ts b/packages/cheqd/tests/setup.ts new file mode 100644 index 0000000000..9af7086f02 --- /dev/null +++ b/packages/cheqd/tests/setup.ts @@ -0,0 +1,33 @@ +jest.setTimeout(60000) + +import { DidDocument, DidDocumentService, VerificationMethod } from '@aries-framework/core' + +export const validDid = 'did:cheqd:testnet:SiVQgrFZ7jFZFrTGstT4ZD' + +export function validVerificationMethod(did: string) { + return new VerificationMethod({ + id: did + '#key-1', + type: 'Ed25519VerificationKey2020', + controller: did, + publicKeyMultibase: 'z6MkkBaWtQKyx7Mr54XaXyMAEpNKqphK4x7ztuBpSfR6Wqwr', + }) +} + +export function validService(did: string) { + return new DidDocumentService({ + id: did + '#service-1', + type: 'DIDCommMessaging', + serviceEndpoint: 'https://rand.io', + }) +} + +export function validDidDoc() { + const service = [validService(validDid)] + const verificationMethod = [validVerificationMethod(validDid)] + + return new DidDocument({ + id: validDid, + verificationMethod, + service, + }) +} diff --git a/packages/cheqd/tests/setupCheqdModule.ts b/packages/cheqd/tests/setupCheqdModule.ts new file mode 100644 index 0000000000..0e095b3aa7 --- /dev/null +++ b/packages/cheqd/tests/setupCheqdModule.ts @@ -0,0 +1,33 @@ +import type { CheqdModuleConfigOptions } from '../src' + +import { DidsModule, KeyDidRegistrar, KeyDidResolver } from '@aries-framework/core' +import { IndySdkModule, IndySdkModuleConfig } from '@aries-framework/indy-sdk' +import indySdk from 'indy-sdk' + +import { CheqdModule, CheqdDidRegistrar, CheqdDidResolver } from '../src' + +export const getIndySdkModuleConfig = () => + new IndySdkModuleConfig({ + indySdk, + }) + +export const getCheqdModuleConfig = (seed?: string) => + ({ + networks: [ + { + network: 'testnet', + cosmosPayerSeed: + seed || + 'sketch mountain erode window enact net enrich smoke claim kangaroo another visual write meat latin bacon pulp similar forum guilt father state erase bright', + }, + ], + } satisfies CheqdModuleConfigOptions) + +export const getCheqdModules = (seed?: string) => ({ + cheqdSdk: new CheqdModule(getCheqdModuleConfig(seed)), + dids: new DidsModule({ + registrars: [new CheqdDidRegistrar(), new KeyDidRegistrar()], + resolvers: [new CheqdDidResolver(), new KeyDidResolver()], + }), + indySdk: new IndySdkModule(getIndySdkModuleConfig()), +}) diff --git a/packages/cheqd/tsconfig.build.json b/packages/cheqd/tsconfig.build.json new file mode 100644 index 0000000000..2b75d0adab --- /dev/null +++ b/packages/cheqd/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["src/**/*"] +} diff --git a/packages/cheqd/tsconfig.json b/packages/cheqd/tsconfig.json new file mode 100644 index 0000000000..7958700c2b --- /dev/null +++ b/packages/cheqd/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"], + "moduleResolution": "node", + "resolveJsonModule": true + } +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 3ffd7de1f4..9a159f0eb7 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -75,10 +75,11 @@ export { Hasher } from './utils/Hasher' export { MessageValidator } from './utils/MessageValidator' export { LinkedAttachment, LinkedAttachmentOptions } from './utils/LinkedAttachment' import { parseInvitationUrl } from './utils/parseInvitation' -import { uuid } from './utils/uuid' +import { uuid, isValidUuid } from './utils/uuid' const utils = { uuid, + isValidUuid, parseInvitationUrl, } diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts index c66b4fc7aa..0b654c1a53 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts @@ -55,6 +55,7 @@ describe('ed25519', () => { expect(keyDidEd25519.supportedVerificationMethodTypes).toMatchObject([ 'Ed25519VerificationKey2018', 'Ed25519VerificationKey2020', + 'JsonWebKey2020', ]) }) diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts index 5058accff3..cee29095e6 100644 --- a/packages/core/src/modules/dids/domain/key-type/ed25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -1,13 +1,14 @@ import type { KeyDidMapping } from './keyDidMapping' +import type { Jwk } from '../../../../crypto' import type { VerificationMethod } from '../verificationMethod' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' -import { KeyType } from '../../../../crypto' -import { Key } from '../../../../crypto/Key' +import { Key, KeyType } from '../../../../crypto' export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 = 'Ed25519VerificationKey2018' export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 = 'Ed25519VerificationKey2020' +export const VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 = 'JsonWebKey2020' export function getEd25519VerificationMethod({ key, id, controller }: { id: string; key: Key; controller: string }) { return { @@ -22,6 +23,7 @@ export const keyDidEd25519: KeyDidMapping = { supportedVerificationMethodTypes: [ VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, + VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, ], getVerificationMethods: (did, key) => [ getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }), @@ -39,6 +41,9 @@ export const keyDidEd25519: KeyDidMapping = { ) { return Key.fromFingerprint(verificationMethod.publicKeyMultibase) } + if (verificationMethod.type === VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 && verificationMethod.publicKeyJwk) { + return Key.fromJwk(verificationMethod.publicKeyJwk as unknown as Jwk) + } throw new Error('Invalid verification method passed') }, diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index f8c2077310..a5ac4eff54 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -13,7 +13,6 @@ import { ConnectionRepository } from '../../../connections/repository/Connection import { ConnectionService } from '../../../connections/services/ConnectionService' import { DidRepository } from '../../../dids/repository/DidRepository' import { DidRegistrarService } from '../../../dids/services/DidRegistrarService' -import { MediationRecipientModuleConfig } from '../../MediationRecipientModuleConfig' import { RoutingEventTypes } from '../../RoutingEvents' import { KeylistUpdateAction, diff --git a/packages/core/src/utils/TypedArrayEncoder.ts b/packages/core/src/utils/TypedArrayEncoder.ts index 83ee5d89ca..b98a5350c2 100644 --- a/packages/core/src/utils/TypedArrayEncoder.ts +++ b/packages/core/src/utils/TypedArrayEncoder.ts @@ -48,6 +48,24 @@ export class TypedArrayEncoder { return Buffer.from(decodeFromBase58(base58)) } + /** + * Encode buffer into base64 string. + * + * @param buffer the buffer to encode into base64 string + */ + public static toHex(buffer: Buffer | Uint8Array) { + return Buffer.from(buffer).toString('hex') + } + + /** + * Decode hex string into buffer + * + * @param hex the hex string to decode into buffer format + */ + public static fromHex(hex: string) { + return Buffer.from(hex, 'hex') + } + /** * Decode string into buffer. * diff --git a/packages/core/src/utils/uuid.ts b/packages/core/src/utils/uuid.ts index 77a29fd525..a6dd97a7f2 100644 --- a/packages/core/src/utils/uuid.ts +++ b/packages/core/src/utils/uuid.ts @@ -1,5 +1,9 @@ -import { v4 } from 'uuid' +import { v4, validate } from 'uuid' export function uuid() { return v4() } + +export function isValidUuid(id: string) { + return validate(id) +} diff --git a/yarn.lock b/yarn.lock index bbad52ea17..165f1ed715 100644 --- a/yarn.lock +++ b/yarn.lock @@ -750,6 +750,159 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@cheqd/sdk@cjs": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-3.3.1.tgz#6ca44df56b28e3f8a4918b49115de0f10c01eed7" + integrity sha512-8qPWGaD8Mc/pEFdJh/Tz9/YFbOvxFMpFKOI8xGalQAcv+KCIs/qKqPCkcxBNquqR4MAWe2ovCWOXGPmx0IrNxQ== + dependencies: + "@cheqd/ts-proto" cjs + "@cosmjs/amino" "^0.29.5" + "@cosmjs/crypto" "^0.29.5" + "@cosmjs/encoding" "^0.29.5" + "@cosmjs/math" "^0.29.5" + "@cosmjs/proto-signing" "^0.29.5" + "@cosmjs/stargate" "^0.29.5" + "@cosmjs/tendermint-rpc" "^0.29.5" + "@cosmjs/utils" "^0.29.5" + "@stablelib/ed25519" "^1.0.3" + cosmjs-types "^0.5.2" + did-jwt "^6.11.6" + did-resolver "^4.1.0" + multiformats "^9.9.0" + uuid "^9.0.0" + +"@cheqd/ts-proto@cjs": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@cheqd/ts-proto/-/ts-proto-2.2.0.tgz#c296a9fff23f47fba84f9c3354439b8fc91129f4" + integrity sha512-COTDndE/haSUPndVYaJGAVT4OMIrVSibGfLrKol9CXZBasmUUJx5rVFOpL34wYq6VcOrfF2TN+63TRePRUBWpA== + dependencies: + long "^5.2.1" + protobufjs "^7.2.3" + +"@confio/ics23@^0.6.8": + version "0.6.8" + resolved "https://registry.yarnpkg.com/@confio/ics23/-/ics23-0.6.8.tgz#2a6b4f1f2b7b20a35d9a0745bb5a446e72930b3d" + integrity sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w== + dependencies: + "@noble/hashes" "^1.0.0" + protobufjs "^6.8.8" + +"@cosmjs/amino@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.29.5.tgz#053b4739a90b15b9e2b781ccd484faf64bd49aec" + integrity sha512-Qo8jpC0BiziTSUqpkNatBcwtKNhCovUnFul9SlT/74JUCdLYaeG5hxr3q1cssQt++l4LvlcpF+OUXL48XjNjLw== + dependencies: + "@cosmjs/crypto" "^0.29.5" + "@cosmjs/encoding" "^0.29.5" + "@cosmjs/math" "^0.29.5" + "@cosmjs/utils" "^0.29.5" + +"@cosmjs/crypto@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.29.5.tgz#ab99fc382b93d8a8db075780cf07487a0f9519fd" + integrity sha512-2bKkaLGictaNL0UipQCL6C1afaisv6k8Wr/GCLx9FqiyFkh9ZgRHDyetD64ZsjnWV/N/D44s/esI+k6oPREaiQ== + dependencies: + "@cosmjs/encoding" "^0.29.5" + "@cosmjs/math" "^0.29.5" + "@cosmjs/utils" "^0.29.5" + "@noble/hashes" "^1" + bn.js "^5.2.0" + elliptic "^6.5.4" + libsodium-wrappers "^0.7.6" + +"@cosmjs/encoding@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.29.5.tgz#009a4b1c596cdfd326f30ccfa79f5e56daa264f2" + integrity sha512-G4rGl/Jg4dMCw5u6PEZHZcoHnUBlukZODHbm/wcL4Uu91fkn5jVo5cXXZcvs4VCkArVGrEj/52eUgTZCmOBGWQ== + dependencies: + base64-js "^1.3.0" + bech32 "^1.1.4" + readonly-date "^1.0.0" + +"@cosmjs/json-rpc@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.29.5.tgz#5e483a9bd98a6270f935adf0dfd8a1e7eb777fe4" + integrity sha512-C78+X06l+r9xwdM1yFWIpGl03LhB9NdM1xvZpQHwgCOl0Ir/WV8pw48y3Ez2awAoUBRfTeejPe4KvrE6NoIi/w== + dependencies: + "@cosmjs/stream" "^0.29.5" + xstream "^11.14.0" + +"@cosmjs/math@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.29.5.tgz#722c96e080d6c2b62215ce9f4c70da7625b241b6" + integrity sha512-2GjKcv+A9f86MAWYLUkjhw1/WpRl2R1BTb3m9qPG7lzMA7ioYff9jY5SPCfafKdxM4TIQGxXQlYGewQL16O68Q== + dependencies: + bn.js "^5.2.0" + +"@cosmjs/proto-signing@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.29.5.tgz#af3b62a46c2c2f1d2327d678b13b7262db1fe87c" + integrity sha512-QRrS7CiKaoETdgIqvi/7JC2qCwCR7lnWaUsTzh/XfRy3McLkEd+cXbKAW3cygykv7IN0VAEIhZd2lyIfT8KwNA== + dependencies: + "@cosmjs/amino" "^0.29.5" + "@cosmjs/crypto" "^0.29.5" + "@cosmjs/encoding" "^0.29.5" + "@cosmjs/math" "^0.29.5" + "@cosmjs/utils" "^0.29.5" + cosmjs-types "^0.5.2" + long "^4.0.0" + +"@cosmjs/socket@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/socket/-/socket-0.29.5.tgz#a48df6b4c45dc6a6ef8e47232725dd4aa556ac2d" + integrity sha512-5VYDupIWbIXq3ftPV1LkS5Ya/T7Ol/AzWVhNxZ79hPe/mBfv1bGau/LqIYOm2zxGlgm9hBHOTmWGqNYDwr9LNQ== + dependencies: + "@cosmjs/stream" "^0.29.5" + isomorphic-ws "^4.0.1" + ws "^7" + xstream "^11.14.0" + +"@cosmjs/stargate@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.29.5.tgz#d597af1c85a3c2af7b5bdbec34d5d40692cc09e4" + integrity sha512-hjEv8UUlJruLrYGJcUZXM/CziaINOKwfVm2BoSdUnNTMxGvY/jC1ABHKeZUYt9oXHxEJ1n9+pDqzbKc8pT0nBw== + dependencies: + "@confio/ics23" "^0.6.8" + "@cosmjs/amino" "^0.29.5" + "@cosmjs/encoding" "^0.29.5" + "@cosmjs/math" "^0.29.5" + "@cosmjs/proto-signing" "^0.29.5" + "@cosmjs/stream" "^0.29.5" + "@cosmjs/tendermint-rpc" "^0.29.5" + "@cosmjs/utils" "^0.29.5" + cosmjs-types "^0.5.2" + long "^4.0.0" + protobufjs "~6.11.3" + xstream "^11.14.0" + +"@cosmjs/stream@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/stream/-/stream-0.29.5.tgz#350981cac496d04939b92ee793b9b19f44bc1d4e" + integrity sha512-TToTDWyH1p05GBtF0Y8jFw2C+4783ueDCmDyxOMM6EU82IqpmIbfwcdMOCAm0JhnyMh+ocdebbFvnX/sGKzRAA== + dependencies: + xstream "^11.14.0" + +"@cosmjs/tendermint-rpc@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.29.5.tgz#f205c10464212bdf843f91bb2e4a093b618cb5c2" + integrity sha512-ar80twieuAxsy0x2za/aO3kBr2DFPAXDmk2ikDbmkda+qqfXgl35l9CVAAjKRqd9d+cRvbQyb5M4wy6XQpEV6w== + dependencies: + "@cosmjs/crypto" "^0.29.5" + "@cosmjs/encoding" "^0.29.5" + "@cosmjs/json-rpc" "^0.29.5" + "@cosmjs/math" "^0.29.5" + "@cosmjs/socket" "^0.29.5" + "@cosmjs/stream" "^0.29.5" + "@cosmjs/utils" "^0.29.5" + axios "^0.21.2" + readonly-date "^1.0.0" + xstream "^11.14.0" + +"@cosmjs/utils@^0.29.5": + version "0.29.5" + resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.29.5.tgz#3fed1b3528ae8c5f1eb5d29b68755bebfd3294ee" + integrity sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ== + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -1341,6 +1494,11 @@ resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== +"@noble/hashes@^1", "@noble/hashes@^1.0.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" + integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1778,6 +1936,59 @@ tiny-glob "^0.2.9" tslib "^2.4.0" +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== + "@react-native-community/cli-clean@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-10.1.1.tgz#4c73ce93a63a24d70c0089d4025daac8184ff504" @@ -2025,6 +2236,11 @@ dependencies: jwt-decode "^3.1.2" +"@stablelib/aead@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/aead/-/aead-1.0.1.tgz#c4b1106df9c23d1b867eb9b276d8f42d5fc4c0c3" + integrity sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg== + "@stablelib/binary@^1.0.0", "@stablelib/binary@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" @@ -2032,6 +2248,36 @@ dependencies: "@stablelib/int" "^1.0.1" +"@stablelib/bytes@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/bytes/-/bytes-1.0.1.tgz#0f4aa7b03df3080b878c7dea927d01f42d6a20d8" + integrity sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ== + +"@stablelib/chacha20poly1305@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/chacha20poly1305/-/chacha20poly1305-1.0.1.tgz#de6b18e283a9cb9b7530d8767f99cde1fec4c2ee" + integrity sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA== + dependencies: + "@stablelib/aead" "^1.0.1" + "@stablelib/binary" "^1.0.1" + "@stablelib/chacha" "^1.0.1" + "@stablelib/constant-time" "^1.0.1" + "@stablelib/poly1305" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/chacha@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/chacha/-/chacha-1.0.1.tgz#deccfac95083e30600c3f92803a3a1a4fa761371" + integrity sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/constant-time@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/constant-time/-/constant-time-1.0.1.tgz#bde361465e1cf7b9753061b77e376b0ca4c77e35" + integrity sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg== + "@stablelib/ed25519@^1.0.2", "@stablelib/ed25519@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@stablelib/ed25519/-/ed25519-1.0.3.tgz#f8fdeb6f77114897c887bb6a3138d659d3f35996" @@ -2051,6 +2297,21 @@ resolved "https://registry.yarnpkg.com/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008" integrity sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w== +"@stablelib/keyagreement@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz#4612efb0a30989deb437cd352cee637ca41fc50f" + integrity sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg== + dependencies: + "@stablelib/bytes" "^1.0.1" + +"@stablelib/poly1305@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/poly1305/-/poly1305-1.0.1.tgz#93bfb836c9384685d33d70080718deae4ddef1dc" + integrity sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA== + dependencies: + "@stablelib/constant-time" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + "@stablelib/random@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.0.tgz#f441495075cdeaa45de16d7ddcc269c0b8edb16b" @@ -2090,6 +2351,35 @@ resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36" integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg== +"@stablelib/x25519@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@stablelib/x25519/-/x25519-1.0.3.tgz#13c8174f774ea9f3e5e42213cbf9fc68a3c7b7fd" + integrity sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw== + dependencies: + "@stablelib/keyagreement" "^1.0.1" + "@stablelib/random" "^1.0.2" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/xchacha20@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/xchacha20/-/xchacha20-1.0.1.tgz#e98808d1f7d8b20e3ff37c71a3062a2a955d9a8c" + integrity sha512-1YkiZnFF4veUwBVhDnDYwo6EHeKzQK4FnLiO7ezCl/zu64uG0bCCAUROJaBkaLH+5BEsO3W7BTXTguMbSLlWSw== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/chacha" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/xchacha20poly1305@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/xchacha20poly1305/-/xchacha20poly1305-1.0.1.tgz#addcaf30b92dd956f76b3357888e2f91b92e7a61" + integrity sha512-B1Abj0sMJ8h3HNmGnJ7vHBrAvxuNka6cJJoZ1ILN7iuacXp7sUYcgOVEOTLWj+rtQMpspY9tXSCRLPmN1mQNWg== + dependencies: + "@stablelib/aead" "^1.0.1" + "@stablelib/chacha20poly1305" "^1.0.1" + "@stablelib/constant-time" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + "@stablelib/xchacha20" "^1.0.1" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -2240,7 +2530,7 @@ dependencies: "@types/node" "*" -"@types/indy-sdk@1.16.26", "@types/indy-sdk@^1.16.26": +"@types/indy-sdk@*", "@types/indy-sdk@1.16.26", "@types/indy-sdk@^1.16.26": version "1.16.26" resolved "https://registry.yarnpkg.com/@types/indy-sdk/-/indy-sdk-1.16.26.tgz#871f82c3f7d241d649aff5eb6800048890efb8f8" integrity sha512-KlnjsVsX/7yTmyyIlHWcytlBHoQ1vPGeiLnLv5y1vDftL6OQ5V+hebfAr7d3roMEsjCTH3qKkklwGcj1qS90YA== @@ -2292,6 +2582,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/long@^4.0.1": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" + integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== + "@types/luxon@^3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.2.0.tgz#99901b4ab29a5fdffc88fff59b3b47fbfbe0557b" @@ -2320,7 +2615,7 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@^16.11.7": +"@types/node@*", "@types/node@>=13.7.0", "@types/node@^16.11.7": version "16.18.16" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.16.tgz#09ff98b144abae2d7cce3e9fe9040ab2bf73222c" integrity sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA== @@ -2961,6 +3256,13 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +axios@^0.21.2: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + axios@^1.0.0: version "1.3.4" resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024" @@ -3146,6 +3448,16 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +bech32@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + +bech32@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" + integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== + before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" @@ -3184,7 +3496,12 @@ bl@^4.0.3, bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -bn.js@^5.2.1: +bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.2.0, bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== @@ -3258,6 +3575,11 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + browserslist@^4.21.3, browserslist@^4.21.5: version "4.21.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" @@ -3461,6 +3783,11 @@ canonicalize@^1.0.1: resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.8.tgz#24d1f1a00ed202faafd9bf8e63352cd4450c6df1" integrity sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A== +canonicalize@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-2.0.0.tgz#32be2cef4446d67fd5348027a384cae28f17226a" + integrity sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w== + chalk@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" @@ -3536,7 +3863,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -class-validator@0.14.0: +class-validator@0.14.0, class-validator@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.0.tgz#40ed0ecf3c83b2a8a6a320f4edb607be0f0df159" integrity sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A== @@ -4009,6 +4336,14 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: js-yaml "^3.13.1" parse-json "^4.0.0" +cosmjs-types@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.5.2.tgz#2d42b354946f330dfb5c90a87fdc2a36f97b965d" + integrity sha512-zxCtIJj8v3Di7s39uN4LNcN3HIE1z0B9Z0SPE8ZNQR0oSzsuSe1ACgxoFkvhkS7WBasCAFcglS11G2hyfd5tPg== + dependencies: + long "^4.0.0" + protobufjs "~6.11.2" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -4252,6 +4587,24 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" +did-jwt@^6.11.6: + version "6.11.6" + resolved "https://registry.yarnpkg.com/did-jwt/-/did-jwt-6.11.6.tgz#3eeb30d6bd01f33bfa17089574915845802a7d44" + integrity sha512-OfbWknRxJuUqH6Lk0x+H1FsuelGugLbBDEwsoJnicFOntIG/A4y19fn0a8RLxaQbWQ5gXg0yDq5E2huSBiiXzw== + dependencies: + "@stablelib/ed25519" "^1.0.2" + "@stablelib/random" "^1.0.1" + "@stablelib/sha256" "^1.0.1" + "@stablelib/x25519" "^1.0.2" + "@stablelib/xchacha20poly1305" "^1.0.1" + bech32 "^2.0.0" + canonicalize "^2.0.0" + did-resolver "^4.0.0" + elliptic "^6.5.4" + js-sha3 "^0.8.0" + multiformats "^9.6.5" + uint8arrays "^3.0.0" + did-resolver@^4.0.0, did-resolver@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.1.0.tgz#740852083c4fd5bf9729d528eca5d105aff45eb6" @@ -4329,6 +4682,19 @@ electron-to-chromium@^1.4.284: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.333.tgz#ebb21f860f8a29923717b06ec0cb54e77ed34c04" integrity sha512-YyE8+GKyGtPEP1/kpvqsdhD6rA/TP1DUFDN4uiU/YI52NzDxmwHkEb3qjId8hLBa5siJvG0sfC3O66501jMruQ== +elliptic@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -5149,7 +5515,7 @@ flow-parser@^0.185.0: resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.185.2.tgz#cb7ee57f77377d6c5d69a469e980f6332a15e492" integrity sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ== -follow-redirects@^1.15.0: +follow-redirects@^1.14.0, follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== @@ -5534,7 +5900,7 @@ globals@^13.19.0: dependencies: type-fest "^0.20.2" -globalthis@^1.0.3: +globalthis@^1.0.1, globalthis@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== @@ -5695,6 +6061,14 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + hermes-estree@0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.8.0.tgz#530be27243ca49f008381c1f3e8b18fb26bf9ec0" @@ -5714,6 +6088,15 @@ hermes-profile-transformer@^0.0.6: dependencies: source-map "^0.7.3" +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -6363,6 +6746,11 @@ isomorphic-webcrypto@^2.3.8: expo-random "*" react-native-securerandom "^0.1.1" +isomorphic-ws@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" @@ -6843,6 +7231,11 @@ js-sdsl@^4.1.4: resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== +js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -7156,6 +7549,18 @@ libphonenumber-js@^1.10.14: resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.24.tgz#a1744cf29df86d5a587562ea28dde12320eb6ab6" integrity sha512-3Dk8f5AmrcWqg+oHhmm9hwSTqpWHBdSqsHmjCJGroULFubi0+x7JEIGmRZCuL3TI8Tx39xaKqfnhsDQ4ALa/Nw== +libsodium-wrappers@^0.7.6: + version "0.7.11" + resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.11.tgz#53bd20606dffcc54ea2122133c7da38218f575f7" + integrity sha512-SrcLtXj7BM19vUKtQuyQKiQCRJPgbpauzl3s0rSwD+60wtHqSUuqcoawlMDheCJga85nKOQwxNYQxf/CKAvs6Q== + dependencies: + libsodium "^0.7.11" + +libsodium@^0.7.11: + version "0.7.11" + resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.11.tgz#cd10aae7bcc34a300cc6ad0ac88fcca674cfbc2e" + integrity sha512-WPfJ7sS53I2s4iM58QxY3Inb83/6mjlYgcmZs7DJsvDlnmVUwNinBCi5vBT43P6bHRy01O4zsMU2CoVR6xJ40A== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -7268,6 +7673,16 @@ logkitty@^0.7.1: dayjs "^1.8.15" yargs "^15.1.0" +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + +long@^5.0.0, long@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f" + integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A== + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -7785,6 +8200,16 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + minimatch@3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.5.tgz#4da8f1290ee0f0f8e83d60ca69f8f134068604a3" @@ -7974,7 +8399,7 @@ msrcrypto@^1.5.6: resolved "https://registry.yarnpkg.com/msrcrypto/-/msrcrypto-1.5.8.tgz#be419be4945bf134d8af52e9d43be7fa261f4a1c" integrity sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q== -multiformats@^9.4.2: +multiformats@^9.4.2, multiformats@^9.6.5, multiformats@^9.9.0: version "9.9.0" resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== @@ -9092,6 +9517,43 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== +protobufjs@^6.8.8, protobufjs@~6.11.2, protobufjs@~6.11.3: + version "6.11.3" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" + integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" ">=13.7.0" + long "^4.0.0" + +protobufjs@^7.2.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.3.tgz#01af019e40d9c6133c49acbb3ff9e30f4f0f70b2" + integrity sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + protocols@^2.0.0, protocols@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" @@ -9428,6 +9890,11 @@ readline@^1.3.0: resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c" integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== +readonly-date@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/readonly-date/-/readonly-date-1.0.0.tgz#5af785464d8c7d7c40b9d738cbde8c646f97dcd9" + integrity sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ== + recast@^0.20.4: version "0.20.5" resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" @@ -9637,6 +10104,13 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +rimraf@^4.0.7: + version "4.4.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.1.tgz#bd33364f67021c5b79e93d7f4fa0568c7c21b755" + integrity sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og== + dependencies: + glob "^9.2.0" + rimraf@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.0.tgz#c7a9f45bb2ec058d2e60ef9aca5167974313d605" @@ -10287,6 +10761,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +symbol-observable@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" + integrity sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== + synckit@^0.8.4: version "0.8.5" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" @@ -10691,7 +11170,7 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -"typescript@^3 || ^4", typescript@~4.9.5: +"typescript@^3 || ^4", typescript@~4.9.4, typescript@~4.9.5: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== @@ -10719,7 +11198,7 @@ uglify-js@^3.1.4: resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== -uint8arrays@^3.1.1: +uint8arrays@^3.0.0, uint8arrays@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" integrity sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg== @@ -11160,6 +11639,14 @@ ws@^8.13.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== +xstream@^11.14.0: + version "11.14.0" + resolved "https://registry.yarnpkg.com/xstream/-/xstream-11.14.0.tgz#2c071d26b18310523b6877e86b4e54df068a9ae5" + integrity sha512-1bLb+kKKtKPbgTK6i/BaoAn03g47PpFstlbe1BA+y3pNS/LfvcaghS5BFf9+EE1J+KwSQsEpfJvFN5GqFtiNmw== + dependencies: + globalthis "^1.0.1" + symbol-observable "^2.0.3" + xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" From de90cafb8d12b7a940f881184cd745c4b5043cbc Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 14 Apr 2023 14:25:47 +0200 Subject: [PATCH 124/139] fix: issuance with unqualified identifiers (#1431) Signed-off-by: Timo Glastra --- .../src/services/AnonCredsRsHolderService.ts | 4 +- .../src/services/AnonCredsRsIssuerService.ts | 39 +++- .../__tests__/AnonCredsRsServices.test.ts | 212 ++++++++++++++++++ .../anoncreds-rs/tests/anoncreds-flow.test.ts | 2 +- packages/anoncreds-rs/tests/indy-flow.test.ts | 40 +++- .../LegacyIndyCredentialFormatService.ts | 11 +- .../formats/LegacyIndyProofFormatService.ts | 9 +- .../legacy-indy-format-services.test.ts | 24 +- packages/anoncreds/src/index.ts | 2 +- .../v1/messages/V1ProposeCredentialMessage.ts | 18 +- .../proofs/v1/models/V1PresentationPreview.ts | 6 +- .../AnonCredsCredentialDefinitionRecord.ts | 19 ++ ...AnonCredsCredentialDefinitionRepository.ts | 22 +- .../src/repository/AnonCredsSchemaRecord.ts | 14 ++ .../repository/AnonCredsSchemaRepository.ts | 22 +- .../services/AnonCredsHolderServiceOptions.ts | 14 +- .../__tests__/__snapshots__/0.3.test.ts.snap | 4 + .../utils/__tests__/indyIdentifiers.test.ts | 147 ++++++++++++ .../__tests__/legacyIndyIdentifiers.test.ts | 34 --- packages/anoncreds/src/utils/index.ts | 10 +- .../anoncreds/src/utils/indyIdentifiers.ts | 196 ++++++++++++++++ .../src/utils/legacyIndyIdentifiers.ts | 5 - .../tests/InMemoryAnonCredsRegistry.ts | 75 +++---- packages/anoncreds/tests/anoncreds.test.ts | 2 + .../anoncreds/tests/legacyAnonCredsSetup.ts | 22 +- .../askar/src/storage/AskarStorageService.ts | 2 +- ...f.credentials.propose-offerED25519.test.ts | 2 +- .../src/modules/vc/W3cCredentialsModule.ts | 4 +- .../modules/vc/W3cCredentialsModuleConfig.ts | 10 +- .../vc/__tests__/W3cCredentialsApi.test.ts | 9 +- packages/core/tests/jsonld.ts | 2 +- .../services/IndySdkAnonCredsRegistry.ts | 40 ++-- .../services/IndySdkHolderService.ts | 39 +++- .../services/IndySdkIssuerService.ts | 20 +- .../services/IndySdkVerifierService.ts | 11 +- .../utils/__tests__/assertUnqualified.test.ts | 152 +++++++++++++ .../utils/__tests__/identifiers.test.ts | 106 --------- .../src/anoncreds/utils/assertUnqualified.ts | 133 +++++++++++ .../src/anoncreds/utils/identifiers.ts | 177 ++------------- .../indy-sdk/src/anoncreds/utils/transform.ts | 6 +- .../src/dids/IndySdkIndyDidRegistrar.ts | 3 +- .../src/dids/IndySdkIndyDidResolver.ts | 4 +- packages/indy-sdk/src/dids/didIndyUtil.ts | 12 - .../indy-sdk/src/ledger/IndySdkPoolService.ts | 7 +- packages/indy-sdk/src/utils/did.ts | 1 - .../tests/sov-did-resolver.e2e.test.ts | 2 +- .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 41 ++-- .../utils/__tests__/identifiers.test.ts | 79 +++++++ .../utils/_tests_/identifiers.test.ts | 185 --------------- .../src/anoncreds/utils/identifiers.ts | 175 ++------------- .../src/dids/IndyVdrIndyDidRegistrar.ts | 2 +- .../src/dids/IndyVdrIndyDidResolver.ts | 3 +- packages/indy-vdr/src/dids/didIndyUtil.ts | 13 +- .../indy-vdr/src/pool/IndyVdrPoolService.ts | 5 +- packages/indy-vdr/src/utils/did.ts | 1 - .../tests/indy-vdr-did-registrar.e2e.test.ts | 8 +- .../indy-vdr-sov-did-resolver.e2e.test.ts | 2 +- 57 files changed, 1328 insertions(+), 881 deletions(-) create mode 100644 packages/anoncreds/src/utils/__tests__/indyIdentifiers.test.ts delete mode 100644 packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts create mode 100644 packages/anoncreds/src/utils/indyIdentifiers.ts delete mode 100644 packages/anoncreds/src/utils/legacyIndyIdentifiers.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts create mode 100644 packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts create mode 100644 packages/indy-vdr/src/anoncreds/utils/__tests__/identifiers.test.ts delete mode 100644 packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts index ea0c64d337..20b9c5a74d 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts @@ -32,7 +32,7 @@ import { AnonCredsCredentialRepository, AnonCredsLinkSecretRepository, AnonCredsRestrictionWrapper, - legacyIndyCredentialDefinitionIdRegex, + unqualifiedCredentialDefinitionIdRegex, AnonCredsRegistryService, } from '@aries-framework/anoncreds' import { AriesFrameworkError, JsonTransformer, TypedArrayEncoder, injectable, utils } from '@aries-framework/core' @@ -213,7 +213,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService { throw new AnonCredsRsError('Link Secret value not stored') } - const isLegacyIdentifier = credentialOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex) + const isLegacyIdentifier = credentialOffer.cred_def_id.match(unqualifiedCredentialDefinitionIdRegex) if (!isLegacyIdentifier && useLegacyProverDid) { throw new AriesFrameworkError('Cannot use legacy prover_did with non-legacy identifiers') } diff --git a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts index 91c439d4b1..383c6e94e7 100644 --- a/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts +++ b/packages/anoncreds-rs/src/services/AnonCredsRsIssuerService.ts @@ -15,6 +15,10 @@ import type { AgentContext } from '@aries-framework/core' import type { CredentialDefinitionPrivate, JsonObject, KeyCorrectnessProof } from '@hyperledger/anoncreds-shared' import { + parseIndyDid, + getUnqualifiedSchemaId, + parseIndySchemaId, + isUnqualifiedCredentialDefinitionId, AnonCredsKeyCorrectnessProofRepository, AnonCredsCredentialDefinitionPrivateRepository, AnonCredsCredentialDefinitionRepository, @@ -87,22 +91,35 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { let credentialOffer: CredentialOffer | undefined try { + // The getByCredentialDefinitionId supports both qualified and unqualified identifiers, even though the + // record is always stored using the qualified identifier. const credentialDefinitionRecord = await agentContext.dependencyManager .resolve(AnonCredsCredentialDefinitionRepository) .getByCredentialDefinitionId(agentContext, options.credentialDefinitionId) + // We fetch the keyCorrectnessProof based on the credential definition record id, as the + // credential definition id passed to this module could be unqualified, and the key correctness + // proof is only stored using the qualified identifier. const keyCorrectnessProofRecord = await agentContext.dependencyManager .resolve(AnonCredsKeyCorrectnessProofRepository) - .getByCredentialDefinitionId(agentContext, options.credentialDefinitionId) + .getByCredentialDefinitionId(agentContext, credentialDefinitionRecord.credentialDefinitionId) if (!credentialDefinitionRecord) { throw new AnonCredsRsError(`Credential Definition ${credentialDefinitionId} not found`) } + let schemaId = credentialDefinitionRecord.credentialDefinition.schemaId + + // if the credentialDefinitionId is not qualified, we need to transform the schemaId to also be unqualified + if (isUnqualifiedCredentialDefinitionId(options.credentialDefinitionId)) { + const { namespaceIdentifier, schemaName, schemaVersion } = parseIndySchemaId(schemaId) + schemaId = getUnqualifiedSchemaId(namespaceIdentifier, schemaName, schemaVersion) + } + credentialOffer = CredentialOffer.create({ credentialDefinitionId, keyCorrectnessProof: keyCorrectnessProofRecord?.value, - schemaId: credentialDefinitionRecord.credentialDefinition.schemaId, + schemaId, }) return credentialOffer.toJson() as unknown as AnonCredsCredentialOffer @@ -135,9 +152,25 @@ export class AnonCredsRsIssuerService implements AnonCredsIssuerService { .resolve(AnonCredsCredentialDefinitionRepository) .getByCredentialDefinitionId(agentContext, options.credentialRequest.cred_def_id) + // We fetch the private record based on the cred def id from the cred def record, as the + // credential definition id passed to this module could be unqualified, and the private record + // is only stored using the qualified identifier. const credentialDefinitionPrivateRecord = await agentContext.dependencyManager .resolve(AnonCredsCredentialDefinitionPrivateRepository) - .getByCredentialDefinitionId(agentContext, options.credentialRequest.cred_def_id) + .getByCredentialDefinitionId(agentContext, credentialDefinitionRecord.credentialDefinitionId) + + let credentialDefinition = credentialDefinitionRecord.credentialDefinition + + if (isUnqualifiedCredentialDefinitionId(options.credentialRequest.cred_def_id)) { + const { namespaceIdentifier, schemaName, schemaVersion } = parseIndySchemaId(credentialDefinition.schemaId) + const { namespaceIdentifier: unqualifiedDid } = parseIndyDid(credentialDefinition.issuerId) + parseIndyDid + credentialDefinition = { + ...credentialDefinition, + schemaId: getUnqualifiedSchemaId(namespaceIdentifier, schemaName, schemaVersion), + issuerId: unqualifiedDid, + } + } credential = Credential.create({ credentialDefinition: credentialDefinitionRecord.credentialDefinition as unknown as JsonObject, diff --git a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts index 49b73ed7f3..1b5d3db10d 100644 --- a/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts +++ b/packages/anoncreds-rs/src/services/__tests__/AnonCredsRsServices.test.ts @@ -1,6 +1,10 @@ import type { AnonCredsProofRequest } from '@aries-framework/anoncreds' import { + getUnqualifiedSchemaId, + parseIndySchemaId, + getUnqualifiedCredentialDefinitionId, + parseIndyCredentialDefinitionId, AnonCredsModuleConfig, AnonCredsHolderServiceSymbol, AnonCredsIssuerServiceSymbol, @@ -236,4 +240,212 @@ describeRunInNodeVersion([18], 'AnonCredsRsServices', () => { expect(verifiedProof).toBeTruthy() }) + + test('issuance flow with unqualified identifiers', async () => { + // Use qualified identifiers to create schema and credential definition (we only support qualified identifiers for these) + const issuerId = 'did:indy:pool:localtest:A4CYPASJYRZRt98YWrac3H' + + const schema = await anonCredsIssuerService.createSchema(agentContext, { + attrNames: ['name', 'age'], + issuerId, + name: 'Employee Credential', + version: '1.0.0', + }) + + const { schemaState } = await registry.registerSchema(agentContext, { + schema, + options: {}, + }) + + const { credentialDefinition, credentialDefinitionPrivate, keyCorrectnessProof } = + await anonCredsIssuerService.createCredentialDefinition(agentContext, { + issuerId, + schemaId: schemaState.schemaId as string, + schema, + tag: 'Employee Credential', + supportRevocation: false, + }) + + const { credentialDefinitionState } = await registry.registerCredentialDefinition(agentContext, { + credentialDefinition, + options: {}, + }) + + if ( + !credentialDefinitionState.credentialDefinition || + !credentialDefinitionState.credentialDefinitionId || + !schemaState.schema || + !schemaState.schemaId + ) { + throw new Error('Failed to create schema or credential definition') + } + + if (!credentialDefinitionPrivate || !keyCorrectnessProof) { + throw new Error('Failed to get private part of credential definition') + } + + await agentContext.dependencyManager.resolve(AnonCredsSchemaRepository).save( + agentContext, + new AnonCredsSchemaRecord({ + schema: schemaState.schema, + schemaId: schemaState.schemaId, + methodName: 'inMemory', + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionRepository).save( + agentContext, + new AnonCredsCredentialDefinitionRecord({ + credentialDefinition: credentialDefinitionState.credentialDefinition, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + methodName: 'inMemory', + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsCredentialDefinitionPrivateRepository).save( + agentContext, + new AnonCredsCredentialDefinitionPrivateRecord({ + value: credentialDefinitionPrivate, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + await agentContext.dependencyManager.resolve(AnonCredsKeyCorrectnessProofRepository).save( + agentContext, + new AnonCredsKeyCorrectnessProofRecord({ + value: keyCorrectnessProof, + credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + }) + ) + + const { namespaceIdentifier, schemaSeqNo, tag } = parseIndyCredentialDefinitionId( + credentialDefinitionState.credentialDefinitionId + ) + const unqualifiedCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( + namespaceIdentifier, + schemaSeqNo, + tag + ) + + const parsedSchema = parseIndySchemaId(schemaState.schemaId) + const unqualifiedSchemaId = getUnqualifiedSchemaId( + parsedSchema.namespaceIdentifier, + parsedSchema.schemaName, + parsedSchema.schemaVersion + ) + + // Create offer with unqualified credential definition id + const credentialOffer = await anonCredsIssuerService.createCredentialOffer(agentContext, { + credentialDefinitionId: unqualifiedCredentialDefinitionId, + }) + + const linkSecret = await anonCredsHolderService.createLinkSecret(agentContext, { linkSecretId: 'someLinkSecretId' }) + expect(linkSecret.linkSecretId).toBe('someLinkSecretId') + + await agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository).save( + agentContext, + new AnonCredsLinkSecretRecord({ + value: linkSecret.linkSecretValue, + linkSecretId: linkSecret.linkSecretId, + }) + ) + + const unqualifiedCredentialDefinition = await registry.getCredentialDefinition( + agentContext, + credentialOffer.cred_def_id + ) + const unqualifiedSchema = await registry.getSchema(agentContext, credentialOffer.schema_id) + if (!unqualifiedCredentialDefinition.credentialDefinition || !unqualifiedSchema.schema) { + throw new Error('unable to fetch credential definition or schema') + } + + const credentialRequestState = await anonCredsHolderService.createCredentialRequest(agentContext, { + credentialDefinition: unqualifiedCredentialDefinition.credentialDefinition, + credentialOffer, + linkSecretId: linkSecret.linkSecretId, + }) + + const { credential } = await anonCredsIssuerService.createCredential(agentContext, { + credentialOffer, + credentialRequest: credentialRequestState.credentialRequest, + credentialValues: { + name: { raw: 'John', encoded: encodeCredentialValue('John') }, + age: { raw: '25', encoded: encodeCredentialValue('25') }, + }, + }) + + const credentialId = 'holderCredentialId2' + + const storedId = await anonCredsHolderService.storeCredential(agentContext, { + credential, + credentialDefinition: unqualifiedCredentialDefinition.credentialDefinition, + schema: unqualifiedSchema.schema, + credentialDefinitionId: credentialOffer.cred_def_id, + credentialRequestMetadata: credentialRequestState.credentialRequestMetadata, + credentialId, + }) + + expect(storedId).toEqual(credentialId) + + const credentialInfo = await anonCredsHolderService.getCredential(agentContext, { + credentialId, + }) + + expect(credentialInfo).toEqual({ + credentialId, + attributes: { + age: '25', + name: 'John', + }, + schemaId: unqualifiedSchemaId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, + revocationRegistryId: null, + credentialRevocationId: undefined, // Should it be null in this case? + methodName: 'inMemory', + }) + + const proofRequest: AnonCredsProofRequest = { + nonce: anoncreds.generateNonce(), + name: 'pres_req_1', + version: '0.1', + requested_attributes: { + attr1_referent: { + name: 'name', + }, + attr2_referent: { + name: 'age', + }, + }, + requested_predicates: { + predicate1_referent: { name: 'age', p_type: '>=' as const, p_value: 18 }, + }, + } + + const proof = await anonCredsHolderService.createProof(agentContext, { + credentialDefinitions: { [unqualifiedCredentialDefinitionId]: credentialDefinition }, + proofRequest, + selectedCredentials: { + attributes: { + attr1_referent: { credentialId, credentialInfo, revealed: true }, + attr2_referent: { credentialId, credentialInfo, revealed: true }, + }, + predicates: { + predicate1_referent: { credentialId, credentialInfo }, + }, + selfAttestedAttributes: {}, + }, + schemas: { [unqualifiedSchemaId]: schema }, + revocationRegistries: {}, + }) + + const verifiedProof = await anonCredsVerifierService.verifyProof(agentContext, { + credentialDefinitions: { [unqualifiedCredentialDefinitionId]: credentialDefinition }, + proof, + proofRequest, + schemas: { [unqualifiedSchemaId]: schema }, + revocationRegistries: {}, + }) + + expect(verifiedProof).toBeTruthy() + }) }) diff --git a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts index 0014f217e3..b3465a543d 100644 --- a/packages/anoncreds-rs/tests/anoncreds-flow.test.ts +++ b/packages/anoncreds-rs/tests/anoncreds-flow.test.ts @@ -38,7 +38,7 @@ import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderServi import { AnonCredsRsIssuerService } from '../src/services/AnonCredsRsIssuerService' import { AnonCredsRsVerifierService } from '../src/services/AnonCredsRsVerifierService' -const registry = new InMemoryAnonCredsRegistry({ useLegacyIdentifiers: false }) +const registry = new InMemoryAnonCredsRegistry() const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], }) diff --git a/packages/anoncreds-rs/tests/indy-flow.test.ts b/packages/anoncreds-rs/tests/indy-flow.test.ts index d5768f79ca..e254ee7dc6 100644 --- a/packages/anoncreds-rs/tests/indy-flow.test.ts +++ b/packages/anoncreds-rs/tests/indy-flow.test.ts @@ -2,6 +2,10 @@ import type { AnonCredsCredentialRequest } from '@aries-framework/anoncreds' import type { Wallet } from '@aries-framework/core' import { + getUnqualifiedSchemaId, + parseIndySchemaId, + getUnqualifiedCredentialDefinitionId, + parseIndyCredentialDefinitionId, AnonCredsModuleConfig, LegacyIndyCredentialFormatService, AnonCredsHolderServiceSymbol, @@ -38,7 +42,7 @@ import { AnonCredsRsHolderService } from '../src/services/AnonCredsRsHolderServi import { AnonCredsRsIssuerService } from '../src/services/AnonCredsRsIssuerService' import { AnonCredsRsVerifierService } from '../src/services/AnonCredsRsVerifierService' -const registry = new InMemoryAnonCredsRegistry({ useLegacyIdentifiers: true }) +const registry = new InMemoryAnonCredsRegistry() const anonCredsModuleConfig = new AnonCredsModuleConfig({ registries: [registry], }) @@ -70,7 +74,7 @@ const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService( const legacyIndyProofFormatService = new LegacyIndyProofFormatService() // This is just so we don't have to register an actually indy did (as we don't have the indy did registrar configured) -const indyDid = 'LjgpST2rjsoxYegQDRm7EL' +const indyDid = 'did:indy:bcovrin:test:LjgpST2rjsoxYegQDRm7EL' // FIXME: Re-include in tests when NodeJS wrapper performance is improved describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', () => { @@ -191,6 +195,20 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', }), ] + const parsedCredentialDefinition = parseIndyCredentialDefinitionId(credentialDefinitionState.credentialDefinitionId) + const unqualifiedCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( + parsedCredentialDefinition.namespaceIdentifier, + parsedCredentialDefinition.schemaSeqNo, + parsedCredentialDefinition.tag + ) + + const parsedSchemaId = parseIndySchemaId(schemaState.schemaId) + const unqualifiedSchemaId = getUnqualifiedSchemaId( + parsedSchemaId.namespaceIdentifier, + parsedSchemaId.schemaName, + parsedSchemaId.schemaVersion + ) + // Holder creates proposal holderCredentialRecord.credentialAttributes = credentialAttributes const { attachment: proposalAttachment } = await legacyIndyCredentialFormatService.createProposal(agentContext, { @@ -198,7 +216,7 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', credentialFormats: { indy: { attributes: credentialAttributes, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, }, }, }) @@ -266,8 +284,8 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', age: '25', name: 'John', }, - schemaId: schemaState.schemaId, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + schemaId: unqualifiedSchemaId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, revocationRegistryId: null, credentialRevocationId: undefined, // FIXME: should be null? methodName: 'inMemory', @@ -275,8 +293,8 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', expect(holderCredentialRecord.metadata.data).toEqual({ '_anoncreds/credential': { - schemaId: schemaState.schemaId, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + schemaId: unqualifiedSchemaId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, }, '_anoncreds/credentialRequest': { link_secret_blinding_data: expect.any(Object), @@ -287,8 +305,8 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', expect(issuerCredentialRecord.metadata.data).toEqual({ '_anoncreds/credential': { - schemaId: schemaState.schemaId, - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + schemaId: unqualifiedSchemaId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, }, }) @@ -309,14 +327,14 @@ describeRunInNodeVersion([18], 'Legacy indy format services using anoncreds-rs', attributes: [ { name: 'name', - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, value: 'John', referent: '1', }, ], predicates: [ { - credentialDefinitionId: credentialDefinitionState.credentialDefinitionId, + credentialDefinitionId: unqualifiedCredentialDefinitionId, name: 'age', predicate: '>=', threshold: 18, diff --git a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts index 6c6630c6f7..98a45b70bc 100644 --- a/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyCredentialFormatService.ts @@ -45,7 +45,6 @@ import { AnonCredsError } from '../error' import { AnonCredsCredentialProposal } from '../models/AnonCredsCredentialProposal' import { AnonCredsIssuerServiceSymbol, AnonCredsHolderServiceSymbol } from '../services' import { AnonCredsRegistryService } from '../services/registry/AnonCredsRegistryService' -import { legacyIndyCredentialDefinitionIdRegex, legacyIndySchemaIdRegex } from '../utils' import { convertAttributesToCredentialValues, assertCredentialValuesMatch, @@ -53,6 +52,7 @@ import { assertAttributesMatch, createAndLinkAttachmentsToPreview, } from '../utils/credential' +import { isUnqualifiedCredentialDefinitionId, isUnqualifiedSchemaId } from '../utils/indyIdentifiers' import { AnonCredsCredentialMetadataKey, AnonCredsCredentialRequestMetadataKey } from '../utils/metadata' import { generateLegacyProverDidLikeString } from '../utils/proverDid' @@ -151,7 +151,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic ) } - if (!credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex)) { + if (!isUnqualifiedCredentialDefinitionId(credentialDefinitionId)) { throw new AriesFrameworkError(`${credentialDefinitionId} is not a valid legacy indy credential definition id`) } @@ -210,10 +210,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const credOffer = attachment.getDataAsJson() - if ( - !credOffer.schema_id.match(legacyIndySchemaIdRegex) || - !credOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex) - ) { + if (!isUnqualifiedSchemaId(credOffer.schema_id) || !isUnqualifiedCredentialDefinitionId(credOffer.cred_def_id)) { throw new ProblemReportError('Invalid credential offer', { problemCode: CredentialProblemReportReason.IssuanceAbandoned, }) @@ -234,7 +231,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic const credentialOffer = offerAttachment.getDataAsJson() - if (!credentialOffer.cred_def_id.match(legacyIndyCredentialDefinitionIdRegex)) { + if (!isUnqualifiedCredentialDefinitionId(credentialOffer.cred_def_id)) { throw new AriesFrameworkError( `${credentialOffer.cred_def_id} is not a valid legacy indy credential definition id` ) diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts index ec493a8cc7..c912f5f692 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -55,11 +55,12 @@ import { checkValidCredentialValueEncoding, encodeCredentialValue, assertNoDuplicateGroupsNamesInProofRequest, - legacyIndyCredentialDefinitionIdRegex, - legacyIndySchemaIdRegex, + unqualifiedCredentialDefinitionIdRegex, + unqualifiedSchemaIdRegex, getRevocationRegistriesForRequest, getRevocationRegistriesForProof, } from '../utils' +import { isUnqualifiedCredentialDefinitionId, isUnqualifiedSchemaId } from '../utils/indyIdentifiers' const V2_INDY_PRESENTATION_PROPOSAL = 'hlindy/proof-req@v2.0' const V2_INDY_PRESENTATION_REQUEST = 'hlindy/proof-req@v2.0' @@ -468,7 +469,7 @@ export class LegacyIndyProofFormatService implements ProofFormatService { }), ] - const cd = parseCredentialDefinitionId(credentialDefinitionState.credentialDefinitionId) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.namespaceIdentifier, cd.schemaSeqNo, cd.tag) + const cd = parseIndyCredentialDefinitionId(credentialDefinitionState.credentialDefinitionId) + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( + cd.namespaceIdentifier, + cd.schemaSeqNo, + cd.tag + ) - const s = parseSchemaId(schemaState.schemaId) - const legacySchemaId = getLegacySchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion) + const s = parseIndySchemaId(schemaState.schemaId) + const legacySchemaId = getUnqualifiedSchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion) // Holder creates proposal holderCredentialRecord.credentialAttributes = credentialAttributes diff --git a/packages/anoncreds/src/index.ts b/packages/anoncreds/src/index.ts index 2ad16e028a..edc9883578 100644 --- a/packages/anoncreds/src/index.ts +++ b/packages/anoncreds/src/index.ts @@ -12,5 +12,5 @@ export { AnonCredsModuleConfig, AnonCredsModuleConfigOptions } from './AnonCreds export { AnonCredsApi } from './AnonCredsApi' export * from './AnonCredsApiOptions' export { generateLegacyProverDidLikeString } from './utils/proverDid' -export * from './utils/legacyIndyIdentifiers' +export * from './utils/indyIdentifiers' export { assertBestPracticeRevocationInterval } from './utils/revocationInterval' diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts index e6362c807b..78c65f302d 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts @@ -5,10 +5,10 @@ import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' import { - legacyIndyCredentialDefinitionIdRegex, - legacyIndyDidRegex, - legacyIndySchemaIdRegex, - legacyIndySchemaVersionRegex, + unqualifiedCredentialDefinitionIdRegex, + unqualifiedIndyDidRegex, + unqualifiedSchemaIdRegex, + unqualifiedSchemaVersionRegex, } from '../../../../utils' import { V1CredentialPreview } from './V1CredentialPreview' @@ -79,7 +79,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_issuer_did' }) @IsString() @IsOptional() - @Matches(legacyIndyDidRegex) + @Matches(unqualifiedIndyDidRegex) public schemaIssuerDid?: string /** @@ -88,7 +88,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_id' }) @IsString() @IsOptional() - @Matches(legacyIndySchemaIdRegex) + @Matches(unqualifiedSchemaIdRegex) public schemaId?: string /** @@ -105,7 +105,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'schema_version' }) @IsString() @IsOptional() - @Matches(legacyIndySchemaVersionRegex, { + @Matches(unqualifiedSchemaVersionRegex, { message: 'Version must be X.X or X.X.X', }) public schemaVersion?: string @@ -116,7 +116,7 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'cred_def_id' }) @IsString() @IsOptional() - @Matches(legacyIndyCredentialDefinitionIdRegex) + @Matches(unqualifiedCredentialDefinitionIdRegex) public credentialDefinitionId?: string /** @@ -125,6 +125,6 @@ export class V1ProposeCredentialMessage extends AgentMessage { @Expose({ name: 'issuer_did' }) @IsString() @IsOptional() - @Matches(legacyIndyDidRegex) + @Matches(unqualifiedIndyDidRegex) public issuerDid?: string } diff --git a/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts b/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts index 7e651dea57..47cbf8a636 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/models/V1PresentationPreview.ts @@ -13,7 +13,7 @@ import { } from 'class-validator' import { anonCredsPredicateType, AnonCredsPredicateType } from '../../../../models' -import { legacyIndyCredentialDefinitionIdRegex } from '../../../../utils' +import { unqualifiedCredentialDefinitionIdRegex } from '../../../../utils' export interface V1PresentationPreviewAttributeOptions { name: string @@ -39,7 +39,7 @@ export class V1PresentationPreviewAttribute { @Expose({ name: 'cred_def_id' }) @IsString() @ValidateIf((o: V1PresentationPreviewAttribute) => o.referent !== undefined) - @Matches(legacyIndyCredentialDefinitionIdRegex) + @Matches(unqualifiedCredentialDefinitionIdRegex) public credentialDefinitionId?: string @Expose({ name: 'mime-type' }) @@ -82,7 +82,7 @@ export class V1PresentationPreviewPredicate { @Expose({ name: 'cred_def_id' }) @IsString() - @Matches(legacyIndyCredentialDefinitionIdRegex) + @Matches(unqualifiedCredentialDefinitionIdRegex) public credentialDefinitionId!: string @IsIn(anonCredsPredicateType) diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts index 2986566069..fe0bcc6eea 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRecord.ts @@ -4,6 +4,12 @@ import type { TagsBase } from '@aries-framework/core' import { BaseRecord, utils } from '@aries-framework/core' +import { + getUnqualifiedCredentialDefinitionId, + isDidIndyCredentialDefinitionId, + parseIndyCredentialDefinitionId, +} from '../utils/indyIdentifiers' + export interface AnonCredsCredentialDefinitionRecordProps { id?: string credentialDefinitionId: string @@ -17,6 +23,11 @@ export type DefaultAnonCredsCredentialDefinitionTags = { issuerId: string tag: string methodName: string + + // Stores the unqualified variant of the credential definition id, which allows issuing credentials using the legacy + // credential definition id, even though the credential definition id is stored in the wallet as a qualified id. + // This is only added when the credential definition id is an did:indy identifier. + unqualifiedCredentialDefinitionId?: string } export class AnonCredsCredentialDefinitionRecord extends BaseRecord< @@ -48,6 +59,13 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord< } public getTags() { + let unqualifiedCredentialDefinitionId: string | undefined = undefined + if (isDidIndyCredentialDefinitionId(this.credentialDefinitionId)) { + const { namespaceIdentifier, schemaSeqNo, tag } = parseIndyCredentialDefinitionId(this.credentialDefinitionId) + + unqualifiedCredentialDefinitionId = getUnqualifiedCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) + } + return { ...this._tags, credentialDefinitionId: this.credentialDefinitionId, @@ -55,6 +73,7 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord< issuerId: this.credentialDefinition.issuerId, tag: this.credentialDefinition.tag, methodName: this.methodName, + unqualifiedCredentialDefinitionId, } } } diff --git a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts index 7677dd76b8..88aedef82a 100644 --- a/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts +++ b/packages/anoncreds/src/repository/AnonCredsCredentialDefinitionRepository.ts @@ -14,10 +14,28 @@ export class AnonCredsCredentialDefinitionRepository extends Repository } public async getBySchemaId(agentContext: AgentContext, schemaId: string) { - return this.getSingleByQuery(agentContext, { schemaId: schemaId }) + return this.getSingleByQuery(agentContext, { + $or: [ + { + schemaId, + }, + { + unqualifiedSchemaId: schemaId, + }, + ], + }) } public async findBySchemaId(agentContext: AgentContext, schemaId: string) { - return await this.findSingleByQuery(agentContext, { schemaId: schemaId }) + return await this.findSingleByQuery(agentContext, { + $or: [ + { + schemaId, + }, + { + unqualifiedSchemaId: schemaId, + }, + ], + }) } } diff --git a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts index 7bfe190380..a657279715 100644 --- a/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts +++ b/packages/anoncreds/src/services/AnonCredsHolderServiceOptions.ts @@ -61,13 +61,13 @@ export interface GetCredentialOptions { } export interface GetCredentialsOptions { - credentialDefinitionId: string - schemaId: string - schemaIssuerId: string - schemaName: string - schemaVersion: string - issuerId: string - methodName: string + credentialDefinitionId?: string + schemaId?: string + schemaIssuerId?: string + schemaName?: string + schemaVersion?: string + issuerId?: string + methodName?: string } // TODO: Maybe we can make this a bit more specific? diff --git a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap index d3a5f4c2a5..1aacd46acc 100644 --- a/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap +++ b/packages/anoncreds/src/updates/__tests__/__snapshots__/0.3.test.ts.snap @@ -365,6 +365,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "methodName": "indy", "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/AnotherSchema/5.12", "tag": "TAG2222", + "unqualifiedCredentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728266:TAG2222", }, "type": "AnonCredsCredentialDefinitionRecord", "value": { @@ -463,6 +464,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "methodName": "indy", "schemaId": "did:indy:bcovrin:test:A4CYPASJYRZRt98YWrac3H/anoncreds/v0/SCHEMA/Test Schema/5.0", "tag": "TAG", + "unqualifiedCredentialDefinitionId": "A4CYPASJYRZRt98YWrac3H:3:CL:728265:TAG", }, "type": "AnonCredsCredentialDefinitionRecord", "value": { @@ -699,6 +701,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "schemaIssuerDid": undefined, "schemaName": "AnotherSchema", "schemaVersion": "5.12", + "unqualifiedSchemaId": "A4CYPASJYRZRt98YWrac3H:2:AnotherSchema:5.12", }, "type": "AnonCredsSchemaRecord", "value": { @@ -797,6 +800,7 @@ exports[`UpdateAssistant | AnonCreds | v0.3.1 - v0.4 should correctly update the "schemaIssuerDid": undefined, "schemaName": "Test Schema", "schemaVersion": "5.0", + "unqualifiedSchemaId": "A4CYPASJYRZRt98YWrac3H:2:Test Schema:5.0", }, "type": "AnonCredsSchemaRecord", "value": { diff --git a/packages/anoncreds/src/utils/__tests__/indyIdentifiers.test.ts b/packages/anoncreds/src/utils/__tests__/indyIdentifiers.test.ts new file mode 100644 index 0000000000..7fba68562d --- /dev/null +++ b/packages/anoncreds/src/utils/__tests__/indyIdentifiers.test.ts @@ -0,0 +1,147 @@ +import { + getUnqualifiedCredentialDefinitionId, + getUnqualifiedRevocationRegistryId, + getUnqualifiedSchemaId, + parseIndyCredentialDefinitionId, + parseIndyRevocationRegistryId, + parseIndySchemaId, + unqualifiedCredentialDefinitionIdRegex, + unqualifiedIndyDidRegex, + unqualifiedRevocationRegistryIdRegex, + unqualifiedSchemaIdRegex, + unqualifiedSchemaVersionRegex, +} from '../indyIdentifiers' + +describe('Legacy Indy Identifier Regex', () => { + const invalidTest = 'test' + + test('test for legacyIndyCredentialDefinitionIdRegex', async () => { + const test = 'q7ATwTYbQDgiigVijUAej:3:CL:160971:1.0.0' + expect(test).toMatch(unqualifiedCredentialDefinitionIdRegex) + expect(unqualifiedCredentialDefinitionIdRegex.test(invalidTest)).toBeFalsy() + }) + + test('test for legacyIndyDidRegex', async () => { + const test = 'did:sov:q7ATwTYbQDgiigVijUAej' + expect(test).toMatch(unqualifiedIndyDidRegex) + expect(unqualifiedIndyDidRegex.test(invalidTest)).toBeFalsy() + }) + + test('test for legacyIndySchemaIdRegex', async () => { + const test = 'q7ATwTYbQDgiigVijUAej:2:test:1.0' + expect(test).toMatch(unqualifiedSchemaIdRegex) + expect(unqualifiedSchemaIdRegex.test(invalidTest)).toBeFalsy() + }) + + test('test for legacyIndySchemaIdRegex', async () => { + const test = 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + expect(test).toMatch(unqualifiedRevocationRegistryIdRegex) + expect(unqualifiedRevocationRegistryIdRegex.test(invalidTest)).toBeFalsy() + }) + + test('test for legacyIndySchemaVersionRegex', async () => { + const test = '1.0.0' + expect(test).toMatch(unqualifiedSchemaVersionRegex) + expect(unqualifiedSchemaVersionRegex.test(invalidTest)).toBeFalsy() + }) + + test('getUnqualifiedSchemaId returns a valid schema id given a did, name, and version', () => { + const did = '12345' + const name = 'backbench' + const version = '420' + + expect(getUnqualifiedSchemaId(did, name, version)).toEqual('12345:2:backbench:420') + }) + + test('getUnqualifiedCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { + const did = '12345' + const seqNo = 420 + const tag = 'someTag' + + expect(getUnqualifiedCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') + }) + + test('getUnqualifiedRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { + const did = '12345' + const seqNo = 420 + const credentialDefinitionTag = 'someTag' + const tag = 'anotherTag' + + expect(getUnqualifiedRevocationRegistryId(did, seqNo, credentialDefinitionTag, tag)).toEqual( + '12345:4:12345:3:CL:420:someTag:CL_ACCUM:anotherTag' + ) + }) + + describe('parseIndySchemaId', () => { + test('parses legacy schema id', () => { + expect(parseIndySchemaId('SDqTzbVuCowusqGBNbNDjH:2:schema-name:1.0')).toEqual({ + did: 'SDqTzbVuCowusqGBNbNDjH', + namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + schemaName: 'schema-name', + schemaVersion: '1.0', + }) + }) + + test('parses did:indy schema id', () => { + expect( + parseIndySchemaId('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0') + ).toEqual({ + namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', + did: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', + schemaName: 'schema-name', + schemaVersion: '1.0', + namespace: 'bcovrin:test', + }) + }) + }) + + describe('parseIndyCredentialDefinitionId', () => { + test('parses legacy credential definition id', () => { + expect(parseIndyCredentialDefinitionId('TL1EaPFCZ8Si5aUrqScBDt:3:CL:10:TAG')).toEqual({ + did: 'TL1EaPFCZ8Si5aUrqScBDt', + namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + schemaSeqNo: '10', + tag: 'TAG', + }) + }) + + test('parses did:indy credential definition id', () => { + expect( + parseIndyCredentialDefinitionId('did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/10/TAG') + ).toEqual({ + namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', + did: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', + namespace: 'pool:localtest', + schemaSeqNo: '10', + tag: 'TAG', + }) + }) + }) + + describe('parseIndyRevocationRegistryId', () => { + test('parses legacy revocation registry id', () => { + expect( + parseIndyRevocationRegistryId('5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1') + ).toEqual({ + did: '5nDyJVP1NrcPAttP3xwMB9', + namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + schemaSeqNo: '56495', + credentialDefinitionTag: 'npdb', + revocationRegistryTag: 'TAG1', + }) + }) + + test('parses did:indy revocation registry id', () => { + expect( + parseIndyRevocationRegistryId('did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_DEF/56495/npdb/TAG1') + ).toEqual({ + namespace: 'sovrin', + namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', + did: 'did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9', + schemaSeqNo: '56495', + credentialDefinitionTag: 'npdb', + revocationRegistryTag: 'TAG1', + }) + }) + }) +}) diff --git a/packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts b/packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts deleted file mode 100644 index 2d38390ae9..0000000000 --- a/packages/anoncreds/src/utils/__tests__/legacyIndyIdentifiers.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - legacyIndyCredentialDefinitionIdRegex, - legacyIndyDidRegex, - legacyIndySchemaIdRegex, - legacyIndySchemaVersionRegex, -} from '../legacyIndyIdentifiers' - -describe('Legacy Indy Identifier Regex', () => { - const invalidTest = 'test' - - test('test for legacyIndyCredentialDefinitionIdRegex', async () => { - const test = 'q7ATwTYbQDgiigVijUAej:3:CL:160971:1.0.0' - expect(test).toMatch(legacyIndyCredentialDefinitionIdRegex) - expect(legacyIndyCredentialDefinitionIdRegex.test(invalidTest)).toBeFalsy() - }) - - test('test for legacyIndyDidRegex', async () => { - const test = 'did:sov:q7ATwTYbQDgiigVijUAej' - expect(test).toMatch(legacyIndyDidRegex) - expect(legacyIndyDidRegex.test(invalidTest)).toBeFalsy - }) - - test('test for legacyIndySchemaIdRegex', async () => { - const test = 'q7ATwTYbQDgiigVijUAej:2:test:1.0' - expect(test).toMatch(legacyIndySchemaIdRegex) - expect(legacyIndySchemaIdRegex.test(invalidTest)).toBeFalsy - }) - - test('test for legacyIndySchemaVersionRegex', async () => { - const test = '1.0.0' - expect(test).toMatch(legacyIndySchemaVersionRegex) - expect(legacyIndySchemaVersionRegex.test(invalidTest)).toBeFalsy - }) -}) diff --git a/packages/anoncreds/src/utils/index.ts b/packages/anoncreds/src/utils/index.ts index 7fa0da87ed..7f2d7763fe 100644 --- a/packages/anoncreds/src/utils/index.ts +++ b/packages/anoncreds/src/utils/index.ts @@ -10,8 +10,8 @@ export { IsMap } from './isMap' export { composeCredentialAutoAccept, composeProofAutoAccept } from './composeAutoAccept' export { areCredentialPreviewAttributesEqual } from './credentialPreviewAttributes' export { - legacyIndyCredentialDefinitionIdRegex, - legacyIndyDidRegex, - legacyIndySchemaIdRegex, - legacyIndySchemaVersionRegex, -} from './legacyIndyIdentifiers' + unqualifiedCredentialDefinitionIdRegex, + unqualifiedIndyDidRegex, + unqualifiedSchemaIdRegex, + unqualifiedSchemaVersionRegex, +} from './indyIdentifiers' diff --git a/packages/anoncreds/src/utils/indyIdentifiers.ts b/packages/anoncreds/src/utils/indyIdentifiers.ts new file mode 100644 index 0000000000..1e20f75c55 --- /dev/null +++ b/packages/anoncreds/src/utils/indyIdentifiers.ts @@ -0,0 +1,196 @@ +import { AriesFrameworkError } from '@aries-framework/core' + +const didIndyAnonCredsBase = + /(did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22}))\/anoncreds\/v0/ + +// :2:: +export const unqualifiedSchemaIdRegex = /^([a-zA-Z0-9]{21,22}):2:(.+):([0-9.]+)$/ +// did:indy::/anoncreds/v0/SCHEMA// +export const didIndySchemaIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/SCHEMA/(.+)/([0-9.]+)$`) + +export const unqualifiedSchemaVersionRegex = /^(\d+\.)?(\d+\.)?(\*|\d+)$/ +export const unqualifiedIndyDidRegex = /^(did:sov:)?[a-zA-Z0-9]{21,22}$/ + +// :3:CL:: +export const unqualifiedCredentialDefinitionIdRegex = /^([a-zA-Z0-9]{21,22}):3:CL:([1-9][0-9]*):(.+)$/ +// did:indy::/anoncreds/v0/CLAIM_DEF// +export const didIndyCredentialDefinitionIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/CLAIM_DEF/([1-9][0-9]*)/(.+)$` +) + +// :4::3:CL::CL_ACCUM: +export const unqualifiedRevocationRegistryIdRegex = + /^([a-zA-Z0-9]{21,22}):4:[a-zA-Z0-9]{21,22}:3:CL:([1-9][0-9]*):(.+):CL_ACCUM:(.+)$/ +// did:indy::/anoncreds/v0/REV_REG_DEF/// +export const didIndyRevocationRegistryIdRegex = new RegExp( + `^${didIndyAnonCredsBase.source}/REV_REG_DEF/([1-9][0-9]*)/(.+)/(.+)$` +) + +export const didIndyRegex = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22})$/ + +export function getUnqualifiedSchemaId(unqualifiedDid: string, name: string, version: string) { + return `${unqualifiedDid}:2:${name}:${version}` +} + +export function getUnqualifiedCredentialDefinitionId(unqualifiedDid: string, seqNo: string | number, tag: string) { + return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` +} + +// TZQuLp43UcYTdtc3HewcDz:4:TZQuLp43UcYTdtc3HewcDz:3:CL:98158:BaustellenzertifikateNU1:CL_ACCUM:1-100 +export function getUnqualifiedRevocationRegistryId( + unqualifiedDid: string, + seqNo: string | number, + credentialDefinitionTag: string, + revocationRegistryTag: string +) { + return `${unqualifiedDid}:4:${unqualifiedDid}:3:CL:${seqNo}:${credentialDefinitionTag}:CL_ACCUM:${revocationRegistryTag}` +} + +export function isUnqualifiedCredentialDefinitionId(credentialDefinitionId: string) { + return unqualifiedCredentialDefinitionIdRegex.test(credentialDefinitionId) +} + +export function isUnqualifiedRevocationRegistryId(revocationRegistryId: string) { + return unqualifiedRevocationRegistryIdRegex.test(revocationRegistryId) +} + +export function isUnqualifiedSchemaId(schemaId: string) { + return unqualifiedSchemaIdRegex.test(schemaId) +} + +export function isDidIndySchemaId(schemaId: string) { + return didIndySchemaIdRegex.test(schemaId) +} + +export function isDidIndyCredentialDefinitionId(credentialDefinitionId: string) { + return didIndyCredentialDefinitionIdRegex.test(credentialDefinitionId) +} + +export function isDidIndyRevocationRegistryId(revocationRegistryId: string) { + return didIndyRevocationRegistryIdRegex.test(revocationRegistryId) +} + +export function parseIndyDid(did: string) { + const match = did.match(didIndyRegex) + if (match) { + const [, namespace, namespaceIdentifier] = match + return { namespace, namespaceIdentifier } + } else { + throw new AriesFrameworkError(`${did} is not a valid did:indy did`) + } +} + +interface ParsedIndySchemaId { + did: string + namespaceIdentifier: string + schemaName: string + schemaVersion: string + namespace?: string +} + +export function parseIndySchemaId(schemaId: string): ParsedIndySchemaId { + const didIndyMatch = schemaId.match(didIndySchemaIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaName, schemaVersion] = didIndyMatch + + return { + did, + namespaceIdentifier, + schemaName, + schemaVersion, + namespace, + } + } + + const legacyMatch = schemaId.match(unqualifiedSchemaIdRegex) + if (legacyMatch) { + const [, did, schemaName, schemaVersion] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaName, + schemaVersion, + } + } + + throw new Error(`Invalid schema id: ${schemaId}`) +} + +interface ParsedIndyCredentialDefinitionId { + did: string + namespaceIdentifier: string + schemaSeqNo: string + tag: string + namespace?: string +} + +export function parseIndyCredentialDefinitionId(credentialDefinitionId: string): ParsedIndyCredentialDefinitionId { + const didIndyMatch = credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaSeqNo, tag] = didIndyMatch + + return { + did, + namespaceIdentifier, + schemaSeqNo, + tag, + namespace, + } + } + + const legacyMatch = credentialDefinitionId.match(unqualifiedCredentialDefinitionIdRegex) + if (legacyMatch) { + const [, did, schemaSeqNo, tag] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaSeqNo, + tag, + } + } + + throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) +} + +interface ParsedIndyRevocationRegistryId { + did: string + namespaceIdentifier: string + schemaSeqNo: string + credentialDefinitionTag: string + revocationRegistryTag: string + namespace?: string +} + +export function parseIndyRevocationRegistryId(revocationRegistryId: string): ParsedIndyRevocationRegistryId { + const didIndyMatch = revocationRegistryId.match(didIndyRevocationRegistryIdRegex) + if (didIndyMatch) { + const [, did, namespace, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = + didIndyMatch + + return { + did, + namespaceIdentifier, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag, + namespace, + } + } + + const legacyMatch = revocationRegistryId.match(unqualifiedRevocationRegistryIdRegex) + if (legacyMatch) { + const [, did, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = legacyMatch + + return { + did, + namespaceIdentifier: did, + schemaSeqNo, + credentialDefinitionTag, + revocationRegistryTag, + } + } + + throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) +} diff --git a/packages/anoncreds/src/utils/legacyIndyIdentifiers.ts b/packages/anoncreds/src/utils/legacyIndyIdentifiers.ts deleted file mode 100644 index 29cc3f45d6..0000000000 --- a/packages/anoncreds/src/utils/legacyIndyIdentifiers.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const legacyIndySchemaIdRegex = /^[a-zA-Z0-9]{21,22}:2:.+:[0-9.]+$/ -export const legacyIndySchemaVersionRegex = /^(\d+\.)?(\d+\.)?(\*|\d+)$/ -export const legacyIndyCredentialDefinitionIdRegex = - /^([a-zA-Z0-9]{21,22}):3:CL:(([1-9][0-9]*)|([a-zA-Z0-9]{21,22}:2:.+:[0-9.]+)):(.+)?$/ -export const legacyIndyDidRegex = /^(did:sov:)?[a-zA-Z0-9]{21,22}$/ diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index 7ee471b4e1..9cb3a9adf3 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -19,14 +19,8 @@ import type { AgentContext } from '@aries-framework/core' import { Hasher, TypedArrayEncoder } from '@aries-framework/core' import BigNumber from 'bn.js' -import { - getDidIndyCredentialDefinitionId, - getDidIndySchemaId, - getLegacyCredentialDefinitionId, - getLegacySchemaId, - parseSchemaId, -} from '../../indy-sdk/src/anoncreds/utils/identifiers' -import { parseIndyDid } from '../../indy-sdk/src/dids/didIndyUtil' +import { getDidIndyCredentialDefinitionId, getDidIndySchemaId } from '../../indy-sdk/src/anoncreds/utils/identifiers' +import { getUnqualifiedCredentialDefinitionId, getUnqualifiedSchemaId, parseIndyDid, parseIndySchemaId } from '../src' /** * In memory implementation of the {@link AnonCredsRegistry} interface. Useful for testing. @@ -43,34 +37,30 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { private credentialDefinitions: Record private revocationRegistryDefinitions: Record private revocationStatusLists: Record> - private useLegacyIdentifiers: boolean public constructor({ existingSchemas = {}, existingCredentialDefinitions = {}, existingRevocationRegistryDefinitions = {}, existingRevocationStatusLists = {}, - useLegacyIdentifiers = false, }: { existingSchemas?: Record existingCredentialDefinitions?: Record existingRevocationRegistryDefinitions?: Record existingRevocationStatusLists?: Record> - useLegacyIdentifiers?: boolean } = {}) { this.schemas = existingSchemas this.credentialDefinitions = existingCredentialDefinitions this.revocationRegistryDefinitions = existingRevocationRegistryDefinitions this.revocationStatusLists = existingRevocationStatusLists - this.useLegacyIdentifiers = useLegacyIdentifiers } public async getSchema(agentContext: AgentContext, schemaId: string): Promise { const schema = this.schemas[schemaId] - const parsed = parseSchemaId(schemaId) + const parsed = parseIndySchemaId(schemaId) - const legacySchemaId = getLegacySchemaId(parsed.namespaceIdentifier, parsed.schemaName, parsed.schemaVersion) + const legacySchemaId = getUnqualifiedSchemaId(parsed.namespaceIdentifier, parsed.schemaName, parsed.schemaVersion) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) if (!schema) { @@ -100,18 +90,17 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { agentContext: AgentContext, options: RegisterSchemaOptions ): Promise { - let legacyIssuerId - let didIndySchemaId = '' - if (this.useLegacyIdentifiers) { - legacyIssuerId = options.schema.issuerId - } else { - const { namespace, namespaceIdentifier } = parseIndyDid(options.schema.issuerId) - legacyIssuerId = namespaceIdentifier - didIndySchemaId = getDidIndySchemaId(namespace, namespaceIdentifier, options.schema.name, options.schema.version) - this.schemas[didIndySchemaId] = options.schema - } + const { namespace, namespaceIdentifier } = parseIndyDid(options.schema.issuerId) + const legacyIssuerId = namespaceIdentifier + const didIndySchemaId = getDidIndySchemaId( + namespace, + namespaceIdentifier, + options.schema.name, + options.schema.version + ) + this.schemas[didIndySchemaId] = options.schema - const legacySchemaId = getLegacySchemaId(legacyIssuerId, options.schema.name, options.schema.version) + const legacySchemaId = getUnqualifiedSchemaId(legacyIssuerId, options.schema.name, options.schema.version) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) this.schemas[legacySchemaId] = { @@ -129,7 +118,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { schemaState: { state: 'finished', schema: options.schema, - schemaId: this.useLegacyIdentifiers ? legacySchemaId : didIndySchemaId, + schemaId: didIndySchemaId, }, } } @@ -163,32 +152,26 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { agentContext: AgentContext, options: RegisterCredentialDefinitionOptions ): Promise { - const parsedSchema = parseSchemaId(options.credentialDefinition.schemaId) - const legacySchemaId = getLegacySchemaId( + const parsedSchema = parseIndySchemaId(options.credentialDefinition.schemaId) + const legacySchemaId = getUnqualifiedSchemaId( parsedSchema.namespaceIdentifier, parsedSchema.schemaName, parsedSchema.schemaVersion ) const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId) - let legacyIssuerId - let didIndyCredentialDefinitionId = '' - if (this.useLegacyIdentifiers) { - legacyIssuerId = options.credentialDefinition.issuerId - } else { - const { namespace, namespaceIdentifier } = parseIndyDid(options.credentialDefinition.issuerId) - legacyIssuerId = namespaceIdentifier - didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( - namespace, - namespaceIdentifier, - indyLedgerSeqNo, - options.credentialDefinition.tag - ) + const { namespace, namespaceIdentifier } = parseIndyDid(options.credentialDefinition.issuerId) + const legacyIssuerId = namespaceIdentifier + const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId( + namespace, + namespaceIdentifier, + indyLedgerSeqNo, + options.credentialDefinition.tag + ) - this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition - } + this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( legacyIssuerId, indyLedgerSeqNo, options.credentialDefinition.tag @@ -206,9 +189,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { credentialDefinitionState: { state: 'finished', credentialDefinition: options.credentialDefinition, - credentialDefinitionId: this.useLegacyIdentifiers - ? legacyCredentialDefinitionId - : didIndyCredentialDefinitionId, + credentialDefinitionId: didIndyCredentialDefinitionId, }, } } diff --git a/packages/anoncreds/tests/anoncreds.test.ts b/packages/anoncreds/tests/anoncreds.test.ts index db7c6e1def..d5590ca4ba 100644 --- a/packages/anoncreds/tests/anoncreds.test.ts +++ b/packages/anoncreds/tests/anoncreds.test.ts @@ -165,6 +165,7 @@ describe('AnonCreds API', () => { schemaName: 'Employee Credential', schemaVersion: '1.0.0', methodName: 'inMemory', + unqualifiedSchemaId: '6xDN7v3AiGgusRp4bqZACZ:2:Employee Credential:1.0.0', }) }) @@ -263,6 +264,7 @@ describe('AnonCreds API', () => { schemaId: '7Cd2Yj9yEZNcmNoH54tq9i:2:Test Schema:1.0.0', issuerId: 'did:indy:pool:localhost:VsKV7grR1BUE29mG2Fm2kX', tag: 'TAG', + unqualifiedCredentialDefinitionId: 'VsKV7grR1BUE29mG2Fm2kX:3:CL:75206:TAG', }) }) diff --git a/packages/anoncreds/tests/legacyAnonCredsSetup.ts b/packages/anoncreds/tests/legacyAnonCredsSetup.ts index 571ecc74eb..7565352e10 100644 --- a/packages/anoncreds/tests/legacyAnonCredsSetup.ts +++ b/packages/anoncreds/tests/legacyAnonCredsSetup.ts @@ -51,12 +51,6 @@ import { IndySdkModule, IndySdkSovDidResolver, } from '../../indy-sdk/src' -import { - getLegacyCredentialDefinitionId, - getLegacySchemaId, - parseCredentialDefinitionId, - parseSchemaId, -} from '../../indy-sdk/src/anoncreds/utils/identifiers' import { getIndySdkModuleConfig } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrAnonCredsRegistry, @@ -67,6 +61,10 @@ import { } from '../../indy-vdr/src' import { indyVdrModuleConfig } from '../../indy-vdr/tests/helpers' import { + getUnqualifiedCredentialDefinitionId, + getUnqualifiedSchemaId, + parseIndyCredentialDefinitionId, + parseIndySchemaId, V1CredentialProtocol, V1ProofProtocol, AnonCredsModule, @@ -469,11 +467,15 @@ export async function prepareForAnonCredsIssuance(agent: Agent, { attributeNames tag: 'default', }) - const s = parseSchemaId(schema.schemaId) - const cd = parseCredentialDefinitionId(credentialDefinition.credentialDefinitionId) + const s = parseIndySchemaId(schema.schemaId) + const cd = parseIndyCredentialDefinitionId(credentialDefinition.credentialDefinitionId) - const legacySchemaId = getLegacySchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.namespaceIdentifier, cd.schemaSeqNo, cd.tag) + const legacySchemaId = getUnqualifiedSchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion) + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( + cd.namespaceIdentifier, + cd.schemaSeqNo, + cd.tag + ) // Wait some time pass to let ledger settle the object await sleep(1000) diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index 3c4dcda0ec..2174291c81 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -159,7 +159,7 @@ export class AskarStorageService implements StorageService } return instances } catch (error) { - throw new WalletError(`Error executing query`, { cause: error }) + throw new WalletError(`Error executing query. ${error.message}`, { cause: error }) } } } diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index 7f5abe619f..3a7b6dac2e 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -126,7 +126,7 @@ const getIndyJsonLdModules = () => cache: new CacheModule({ cache: new InMemoryLruCache({ limit: 100 }), }), - w3cVc: new W3cCredentialsModule({ + w3cCredentials: new W3cCredentialsModule({ documentLoader: customDocumentLoader, }), } as const) diff --git a/packages/core/src/modules/vc/W3cCredentialsModule.ts b/packages/core/src/modules/vc/W3cCredentialsModule.ts index 70b93e81b6..3c6886fdf4 100644 --- a/packages/core/src/modules/vc/W3cCredentialsModule.ts +++ b/packages/core/src/modules/vc/W3cCredentialsModule.ts @@ -1,4 +1,4 @@ -import type { W3cVcModuleConfigOptions } from './W3cCredentialsModuleConfig' +import type { W3cCredentialsModuleConfigOptions } from './W3cCredentialsModuleConfig' import type { DependencyManager, Module } from '../../plugins' import { KeyType } from '../../crypto' @@ -21,7 +21,7 @@ export class W3cCredentialsModule implements Module { public readonly config: W3cCredentialsModuleConfig public readonly api = W3cCredentialsApi - public constructor(config?: W3cVcModuleConfigOptions) { + public constructor(config?: W3cCredentialsModuleConfigOptions) { this.config = new W3cCredentialsModuleConfig(config) } diff --git a/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts b/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts index c9b6fbdd2f..ef7c5e5947 100644 --- a/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts +++ b/packages/core/src/modules/vc/W3cCredentialsModuleConfig.ts @@ -3,10 +3,10 @@ import type { DocumentLoaderWithContext } from './libraries/documentLoader' import { defaultDocumentLoader } from './libraries/documentLoader' /** - * W3cVcModuleConfigOptions defines the interface for the options of the W3cVcModuleConfig class. + * W3cCredentialsModuleConfigOptions defines the interface for the options of the W3cCredentialsModuleConfig class. * This can contain optional parameters that have default values in the config class itself. */ -export interface W3cVcModuleConfigOptions { +export interface W3cCredentialsModuleConfigOptions { /** * Document loader to use for resolving JSON-LD objects. Takes a {@link AgentContext} as parameter, * and must return a {@link DocumentLoader} function. @@ -33,13 +33,13 @@ export interface W3cVcModuleConfigOptions { } export class W3cCredentialsModuleConfig { - private options: W3cVcModuleConfigOptions + private options: W3cCredentialsModuleConfigOptions - public constructor(options?: W3cVcModuleConfigOptions) { + public constructor(options?: W3cCredentialsModuleConfigOptions) { this.options = options ?? {} } - /** See {@link W3cVcModuleConfigOptions.documentLoader} */ + /** See {@link W3cCredentialsModuleConfigOptions.documentLoader} */ public get documentLoader() { return this.options.documentLoader ?? defaultDocumentLoader } diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts index f939a40ccd..1af3b044b1 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialsApi.test.ts @@ -3,18 +3,23 @@ import { getAgentOptions, indySdk } from '../../../../tests' import { Agent } from '../../../agent/Agent' import { JsonTransformer } from '../../../utils' import { W3cCredentialService } from '../W3cCredentialService' +import { W3cCredentialsModule } from '../W3cCredentialsModule' import { W3cVerifiableCredential } from '../models' import { W3cCredentialRepository } from '../repository' +import { customDocumentLoader } from './documentLoader' import { Ed25519Signature2018Fixtures } from './fixtures' const modules = { indySdk: new IndySdkModule({ indySdk, }), + w3cCredentials: new W3cCredentialsModule({ + documentLoader: customDocumentLoader, + }), } -const agentOptions = getAgentOptions('W3cCredentialsApi', {}, modules) +const agentOptions = getAgentOptions('W3cCredentialsApi', {}, modules) const agent = new Agent(agentOptions) @@ -83,7 +88,7 @@ describe('W3cCredentialsApi', () => { expect(repoSpy).toHaveBeenCalledTimes(1) expect(serviceSpy).toHaveBeenCalledTimes(1) - expect(serviceSpy).toHaveBeenCalledWith((agent as any).agentContext, storedCredential.id) + expect(serviceSpy).toHaveBeenCalledWith(agent.context, storedCredential.id) const allCredentials = await agent.w3cCredentials.getAllCredentialRecords() expect(allCredentials).toHaveLength(0) diff --git a/packages/core/tests/jsonld.ts b/packages/core/tests/jsonld.ts index 5ac2385157..41ea8013b4 100644 --- a/packages/core/tests/jsonld.ts +++ b/packages/core/tests/jsonld.ts @@ -33,7 +33,7 @@ export const getJsonLdModules = ({ credentialProtocols: [new V2CredentialProtocol({ credentialFormats: [new JsonLdCredentialFormatService()] })], autoAcceptCredentials, }), - w3cVc: new W3cCredentialsModule({ + w3cCredentials: new W3cCredentialsModule({ documentLoader: customDocumentLoader, }), proofs: new ProofsModule({ diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts index 733e08190c..21ab95ab53 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkAnonCredsRegistry.ts @@ -14,20 +14,24 @@ import type { import type { AgentContext } from '@aries-framework/core' import type { Schema as IndySdkSchema } from 'indy-sdk' -import { parseIndyDid, verificationKeyForIndyDid } from '../../dids/didIndyUtil' +import { + getUnqualifiedCredentialDefinitionId, + getUnqualifiedRevocationRegistryId, + getUnqualifiedSchemaId, + parseIndyCredentialDefinitionId, + parseIndyDid, + parseIndyRevocationRegistryId, + parseIndySchemaId, +} from '@aries-framework/anoncreds' + +import { verificationKeyForIndyDid } from '../../dids/didIndyUtil' import { IndySdkError, isIndyError } from '../../error' import { IndySdkPoolService } from '../../ledger' import { IndySdkSymbol } from '../../types' import { getDidIndyCredentialDefinitionId, getDidIndySchemaId, - getLegacyCredentialDefinitionId, - getLegacyRevocationRegistryId, - getLegacySchemaId, indySdkAnonCredsRegistryIdentifierRegex, - parseCredentialDefinitionId, - parseRevocationRegistryId, - parseSchemaId, } from '../utils/identifiers' import { anonCredsRevocationStatusListFromIndySdk } from '../utils/transform' @@ -47,12 +51,12 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) // parse schema id (supports did:indy and legacy) - const { did, namespaceIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId) + const { did, namespaceIdentifier, schemaName, schemaVersion } = parseIndySchemaId(schemaId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`) // even though we support did:indy and legacy identifiers we always need to fetch using the legacy identifier - const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schemaName, schemaVersion) + const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, schemaName, schemaVersion) const request = await indySdk.buildGetSchemaRequest(null, legacySchemaId) agentContext.config.logger.trace( @@ -126,7 +130,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { options.schema.name, options.schema.version ) - const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version) + const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, options.schema.name, options.schema.version) const schema = { attrNames: options.schema.attrNames, @@ -193,14 +197,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) // we support did:indy and legacy identifiers - const { did, namespaceIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId) + const { did, namespaceIdentifier, schemaSeqNo, tag } = parseIndyCredentialDefinitionId(credentialDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve credential definition '${credentialDefinitionId}'` ) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) const request = await indySdk.buildGetCredDefRequest(null, legacyCredentialDefinitionId) agentContext.config.logger.trace( @@ -315,7 +319,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { } } - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( namespaceIdentifier, schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag @@ -380,14 +384,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) const { did, namespaceIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = - parseRevocationRegistryId(revocationRegistryDefinitionId) + parseIndyRevocationRegistryId(revocationRegistryDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` ) - const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryId( namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, @@ -422,7 +426,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { schemaSeqNo, credentialDefinitionTag ) - : getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag) + : getUnqualifiedCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag) return { resolutionMetadata: {}, @@ -474,14 +478,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry { const indySdk = agentContext.dependencyManager.resolve(IndySdkSymbol) const { did, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = - parseRevocationRegistryId(revocationRegistryId) + parseIndyRevocationRegistryId(revocationRegistryId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.didIndyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` ) - const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryId( namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts index 3fcb1edbb7..ef44edb2e2 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts @@ -24,13 +24,21 @@ import type { IndyProofRequest, } from 'indy-sdk' -import { AnonCredsLinkSecretRepository, generateLegacyProverDidLikeString } from '@aries-framework/anoncreds' +import { + parseIndyCredentialDefinitionId, + AnonCredsLinkSecretRepository, + generateLegacyProverDidLikeString, +} from '@aries-framework/anoncreds' import { AriesFrameworkError, injectable, inject, utils } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' -import { parseCredentialDefinitionId } from '../utils/identifiers' +import { + assertAllUnqualified, + assertUnqualifiedCredentialOffer, + assertUnqualifiedProofRequest, +} from '../utils/assertUnqualified' import { anonCredsCredentialRequestMetadataFromIndySdk, indySdkCredentialDefinitionFromAnonCreds, @@ -81,6 +89,13 @@ export class IndySdkHolderService implements AnonCredsHolderService { assertIndySdkWallet(agentContext.wallet) + // Make sure all identifiers are unqualified + assertAllUnqualified({ + schemaIds: Object.keys(options.schemas), + credentialDefinitionIds: Object.keys(options.credentialDefinitions), + revocationRegistryIds: Object.keys(options.revocationRegistries), + }) + const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository) try { @@ -106,7 +121,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { ) // Get the seqNo for the schemas so we can use it when transforming the schemas - const { schemaSeqNo } = parseCredentialDefinitionId(credentialDefinitionId) + const { schemaSeqNo } = parseIndyCredentialDefinitionId(credentialDefinitionId) seqNoMap[credentialDefinition.schemaId] = Number(schemaSeqNo) } @@ -153,6 +168,11 @@ export class IndySdkHolderService implements AnonCredsHolderService { public async storeCredential(agentContext: AgentContext, options: StoreCredentialOptions): Promise { assertIndySdkWallet(agentContext.wallet) + assertAllUnqualified({ + schemaIds: [options.credentialDefinition.schemaId, options.credential.schema_id], + credentialDefinitionIds: [options.credentialDefinitionId, options.credential.cred_def_id], + revocationRegistryIds: [options.revocationRegistry?.id, options.credential.rev_reg_id], + }) const indyRevocationRegistryDefinition = options.revocationRegistry ? indySdkRevocationRegistryDefinitionFromAnonCreds( @@ -214,6 +234,12 @@ export class IndySdkHolderService implements AnonCredsHolderService { return [] } + assertAllUnqualified({ + credentialDefinitionIds: [options.credentialDefinitionId], + schemaIds: [options.schemaId], + issuerIds: [options.issuerId, options.schemaIssuerId], + }) + const credentials = await this.indySdk.proverGetCredentials(agentContext.wallet.handle, { cred_def_id: options.credentialDefinitionId, schema_id: options.schemaId, @@ -240,6 +266,12 @@ export class IndySdkHolderService implements AnonCredsHolderService { ): Promise { assertIndySdkWallet(agentContext.wallet) + assertUnqualifiedCredentialOffer(options.credentialOffer) + assertAllUnqualified({ + schemaIds: [options.credentialDefinition.schemaId], + issuerIds: [options.credentialDefinition.issuerId], + }) + if (!options.useLegacyProverDid) { throw new AriesFrameworkError('Indy SDK only supports legacy prover did for credential requests') } @@ -307,6 +339,7 @@ export class IndySdkHolderService implements AnonCredsHolderService { options: GetCredentialsForProofRequestOptions ): Promise { assertIndySdkWallet(agentContext.wallet) + assertUnqualifiedProofRequest(options.proofRequest) try { // Open indy credential search diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts index 72abbbdea8..01973d31dd 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts @@ -12,14 +12,18 @@ import type { } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' -import { generateLegacyProverDidLikeString } from '@aries-framework/anoncreds' +import { parseIndyDid, getUnqualifiedSchemaId, generateLegacyProverDidLikeString } from '@aries-framework/anoncreds' import { injectable, AriesFrameworkError, inject } from '@aries-framework/core' -import { parseIndyDid } from '../../dids/didIndyUtil' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' import { assertIndySdkWallet } from '../../utils/assertIndySdkWallet' -import { getLegacySchemaId } from '../utils/identifiers' +import { + assertUnqualifiedCredentialDefinitionId, + assertUnqualifiedCredentialOffer, + assertUnqualifiedCredentialRequest, + assertUnqualifiedRevocationRegistryId, +} from '../utils/assertUnqualified' import { createTailsReader } from '../utils/tails' import { indySdkSchemaFromAnonCreds } from '../utils/transform' @@ -63,7 +67,7 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { const { namespaceIdentifier } = parseIndyDid(options.issuerId) // parse schema in a way that supports both unqualified and qualified identifiers - const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schema.name, schema.version) + const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, schema.name, schema.version) if (!metadata) throw new AriesFrameworkError('The metadata parameter is required when using Indy, but received undefined.') @@ -100,6 +104,8 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { options: CreateCredentialOfferOptions ): Promise { assertIndySdkWallet(agentContext.wallet) + assertUnqualifiedCredentialDefinitionId(options.credentialDefinitionId) + try { return await this.indySdk.issuerCreateCredentialOffer(agentContext.wallet.handle, options.credentialDefinitionId) } catch (error) { @@ -114,6 +120,12 @@ export class IndySdkIssuerService implements AnonCredsIssuerService { const { tailsFilePath, credentialOffer, credentialRequest, credentialValues, revocationRegistryId } = options assertIndySdkWallet(agentContext.wallet) + assertUnqualifiedCredentialOffer(options.credentialOffer) + assertUnqualifiedCredentialRequest(options.credentialRequest) + if (options.revocationRegistryId) { + assertUnqualifiedRevocationRegistryId(options.revocationRegistryId) + } + try { // Indy SDK requires tailsReaderHandle. Use null if no tailsFilePath is present const tailsReaderHandle = tailsFilePath ? await createTailsReader(agentContext, tailsFilePath) : 0 diff --git a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts index b280256229..5d03e7e18c 100644 --- a/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts +++ b/packages/indy-sdk/src/anoncreds/services/IndySdkVerifierService.ts @@ -2,11 +2,12 @@ import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framew import type { AgentContext } from '@aries-framework/core' import type { CredentialDefs, Schemas, RevocRegDefs, RevRegs, IndyProofRequest, IndyProof } from 'indy-sdk' +import { parseIndyCredentialDefinitionId } from '@aries-framework/anoncreds' import { inject, injectable } from '@aries-framework/core' import { IndySdkError, isIndyError } from '../../error' import { IndySdk, IndySdkSymbol } from '../../types' -import { parseCredentialDefinitionId } from '../utils/identifiers' +import { assertAllUnqualified } from '../utils/assertUnqualified' import { indySdkCredentialDefinitionFromAnonCreds, indySdkRevocationRegistryDefinitionFromAnonCreds, @@ -23,6 +24,12 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { } public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise { + assertAllUnqualified({ + credentialDefinitionIds: Object.keys(options.credentialDefinitions), + schemaIds: Object.keys(options.schemas), + revocationRegistryIds: Object.keys(options.revocationRegistries), + }) + try { // The AnonCredsSchema doesn't contain the seqNo anymore. However, the indy credential definition id // does contain the seqNo, so we can extract it from the credential definition id. @@ -39,7 +46,7 @@ export class IndySdkVerifierService implements AnonCredsVerifierService { ) // Get the seqNo for the schemas so we can use it when transforming the schemas - const { schemaSeqNo } = parseCredentialDefinitionId(credentialDefinitionId) + const { schemaSeqNo } = parseIndyCredentialDefinitionId(credentialDefinitionId) seqNoMap[credentialDefinition.schemaId] = Number(schemaSeqNo) } diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts new file mode 100644 index 0000000000..3475cc48bc --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/assertUnqualified.test.ts @@ -0,0 +1,152 @@ +import type { AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '@aries-framework/anoncreds' + +import { + assertUnqualifiedCredentialDefinitionId, + assertUnqualifiedCredentialOffer, + assertUnqualifiedCredentialRequest, + assertUnqualifiedIssuerId, + assertUnqualifiedProofRequest, + assertUnqualifiedRevocationRegistryId, + assertUnqualifiedSchemaId, +} from '../assertUnqualified' + +describe('assertUnqualified', () => { + describe('assertUnqualifiedCredentialDefinitionId', () => { + test('throws when a non-unqualified credential definition id is passed', () => { + expect(() => + assertUnqualifiedCredentialDefinitionId( + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID' + ) + ).toThrow() + }) + + test('does not throw when an unqualified credential definition id is passed', () => { + expect(() => + assertUnqualifiedCredentialDefinitionId('N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID') + ).not.toThrow() + }) + }) + + describe('assertUnqualifiedSchemaId', () => { + test('throws when a non-unqualified schema id is passed', () => { + expect(() => + assertUnqualifiedSchemaId('did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0') + ).toThrowError('Schema id') + }) + + test('does not throw when an unqualified schema id is passed', () => { + expect(() => assertUnqualifiedSchemaId('BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0')).not.toThrow() + }) + }) + + describe('assertUnqualifiedRevocationRegistryId', () => { + test('throws when a non-unqualified revocation registry id is passed', () => { + expect(() => + assertUnqualifiedRevocationRegistryId( + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/REV_REG_DEF/100669/SCH Employee ID/1-1024' + ) + ).toThrowError('Revocation registry id') + }) + + test('does not throw when an unqualified revocation registry id is passed', () => { + expect(() => + assertUnqualifiedRevocationRegistryId( + 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + ) + ).not.toThrow() + }) + }) + + describe('assertUnqualifiedIssuerId', () => { + test('throws when a non-unqualified issuer id is passed', () => { + expect(() => assertUnqualifiedIssuerId('did:indy:sovrin:N7baRMcyvPwWc8v85CtZ6e')).toThrowError('Issuer id') + }) + + test('does not throw when an unqualified issuer id is passed', () => { + expect(() => assertUnqualifiedIssuerId('N7baRMcyvPwWc8v85CtZ6e')).not.toThrow() + }) + }) + + describe('assertUnqualifiedCredentialOffer', () => { + test('throws when non-unqualified identifiers are passed', () => { + expect(() => + assertUnqualifiedCredentialOffer({ + cred_def_id: 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID', + schema_id: 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0', + } as AnonCredsCredentialOffer) + ).toThrowError('Credential definition id') + + expect(() => + assertUnqualifiedCredentialOffer({ + cred_def_id: 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID', + schema_id: 'did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0', + } as AnonCredsCredentialOffer) + ).toThrowError('Schema id') + }) + + test('does not throw when only unqualified identifiers are passed', () => { + expect(() => + assertUnqualifiedCredentialOffer({ + cred_def_id: 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID', + schema_id: 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0', + } as AnonCredsCredentialOffer) + ).not.toThrow() + }) + }) + + describe('assertUnqualifiedCredentialRequest', () => { + test('throws when non-unqualified identifiers are passed', () => { + expect(() => + assertUnqualifiedCredentialRequest({ + cred_def_id: 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID', + } as AnonCredsCredentialRequest) + ).toThrowError('Credential definition id') + }) + + test('does not throw when only unqualified identifiers are passed', () => { + expect(() => + assertUnqualifiedCredentialRequest({ + cred_def_id: 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID', + } as AnonCredsCredentialRequest) + ).not.toThrow() + }) + }) + + describe('assertUnqualifiedProofRequest', () => { + test('throws when non-unqualified identifiers are passed', () => { + expect(() => + assertUnqualifiedProofRequest({ + requested_attributes: { + a: { + restrictions: [ + { + cred_def_id: 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID', + }, + ], + }, + }, + requested_predicates: {}, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any) + ).toThrowError('Credential definition id') + }) + + test('does not throw when only unqualified identifiers are passed', () => { + expect(() => + assertUnqualifiedProofRequest({ + requested_attributes: { + a: { + restrictions: [ + { + schema_id: 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0', + }, + ], + }, + }, + requested_predicates: {}, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any) + ).not.toThrow() + }) + }) +}) diff --git a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts index f60ebe04a1..9b9a54ba83 100644 --- a/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts +++ b/packages/indy-sdk/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -2,13 +2,7 @@ import { getDidIndyCredentialDefinitionId, getDidIndyRevocationRegistryId, getDidIndySchemaId, - getLegacyCredentialDefinitionId, - getLegacyRevocationRegistryId, - getLegacySchemaId, indySdkAnonCredsRegistryIdentifierRegex, - parseCredentialDefinitionId, - parseRevocationRegistryId, - parseSchemaId, } from '../identifiers' describe('identifiers', () => { @@ -49,33 +43,6 @@ describe('identifiers', () => { }) }) - test('getLegacySchemaId returns a valid schema id given a did, name, and version', () => { - const did = '12345' - const name = 'backbench' - const version = '420' - - expect(getLegacySchemaId(did, name, version)).toEqual('12345:2:backbench:420') - }) - - test('getLegacyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { - const did = '12345' - const seqNo = 420 - const tag = 'someTag' - - expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') - }) - - test('getLegacyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { - const did = '12345' - const seqNo = 420 - const credentialDefinitionTag = 'someTag' - const tag = 'anotherTag' - - expect(getLegacyRevocationRegistryId(did, seqNo, credentialDefinitionTag, tag)).toEqual( - '12345:4:12345:3:CL:420:someTag:CL_ACCUM:anotherTag' - ) - }) - test('getDidIndySchemaId returns a valid schema id given a did, name, and version', () => { const namespace = 'sovrin:test' const did = '12345' @@ -109,77 +76,4 @@ describe('identifiers', () => { 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag' ) }) - - describe('parseSchemaId', () => { - test('parses legacy schema id', () => { - expect(parseSchemaId('SDqTzbVuCowusqGBNbNDjH:2:schema-name:1.0')).toEqual({ - did: 'SDqTzbVuCowusqGBNbNDjH', - namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', - schemaName: 'schema-name', - schemaVersion: '1.0', - }) - }) - - test('parses did:indy schema id', () => { - expect(parseSchemaId('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0')).toEqual( - { - namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', - did: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', - schemaName: 'schema-name', - schemaVersion: '1.0', - namespace: 'bcovrin:test', - } - ) - }) - }) - - describe('parseCredentialDefinitionId', () => { - test('parses legacy credential definition id', () => { - expect(parseCredentialDefinitionId('TL1EaPFCZ8Si5aUrqScBDt:3:CL:10:TAG')).toEqual({ - did: 'TL1EaPFCZ8Si5aUrqScBDt', - namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', - schemaSeqNo: '10', - tag: 'TAG', - }) - }) - - test('parses did:indy credential definition id', () => { - expect( - parseCredentialDefinitionId('did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/10/TAG') - ).toEqual({ - namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', - did: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', - namespace: 'pool:localtest', - schemaSeqNo: '10', - tag: 'TAG', - }) - }) - }) - - describe('parseRevocationRegistryId', () => { - test('parses legacy revocation registry id', () => { - expect( - parseRevocationRegistryId('5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1') - ).toEqual({ - did: '5nDyJVP1NrcPAttP3xwMB9', - namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', - schemaSeqNo: '56495', - credentialDefinitionTag: 'npdb', - revocationRegistryTag: 'TAG1', - }) - }) - - test('parses did:indy revocation registry id', () => { - expect( - parseRevocationRegistryId('did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_DEF/56495/npdb/TAG1') - ).toEqual({ - namespace: 'sovrin', - namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', - did: 'did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9', - schemaSeqNo: '56495', - credentialDefinitionTag: 'npdb', - revocationRegistryTag: 'TAG1', - }) - }) - }) }) diff --git a/packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts b/packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts new file mode 100644 index 0000000000..320fadcb6e --- /dev/null +++ b/packages/indy-sdk/src/anoncreds/utils/assertUnqualified.ts @@ -0,0 +1,133 @@ +import type { + AnonCredsCredentialOffer, + AnonCredsCredentialRequest, + AnonCredsProofRequest, +} from '@aries-framework/anoncreds' + +import { + unqualifiedRevocationRegistryIdRegex, + unqualifiedCredentialDefinitionIdRegex, + unqualifiedIndyDidRegex, + unqualifiedSchemaIdRegex, +} from '@aries-framework/anoncreds' +import { AriesFrameworkError } from '@aries-framework/core' + +/** + * Assert that a credential definition id is unqualified. + */ +export function assertUnqualifiedCredentialDefinitionId(credentialDefinitionId: string) { + if (!unqualifiedCredentialDefinitionIdRegex.test(credentialDefinitionId)) { + throw new AriesFrameworkError( + `Credential definition id '${credentialDefinitionId}' is not an unqualified credential definition id. Indy SDK only supports unqualified identifiers.` + ) + } +} + +/** + * Assert that a schema id is unqualified. + */ +export function assertUnqualifiedSchemaId(schemaId: string) { + if (!unqualifiedSchemaIdRegex.test(schemaId)) { + throw new AriesFrameworkError( + `Schema id '${schemaId}' is not an unqualified schema id. Indy SDK only supports unqualified identifiers.` + ) + } +} + +/** + * Assert that a revocation registry id is unqualified. + */ +export function assertUnqualifiedRevocationRegistryId(revocationRegistryId: string) { + if (!unqualifiedRevocationRegistryIdRegex.test(revocationRegistryId)) { + throw new AriesFrameworkError( + `Revocation registry id '${revocationRegistryId}' is not an unqualified revocation registry id. Indy SDK only supports unqualified identifiers.` + ) + } +} + +/** + * Assert that an issuer id is unqualified. + */ +export function assertUnqualifiedIssuerId(issuerId: string) { + if (!unqualifiedIndyDidRegex.test(issuerId)) { + throw new AriesFrameworkError( + `Issuer id '${issuerId}' is not an unqualified issuer id. Indy SDK only supports unqualified identifiers.` + ) + } +} + +/** + * Assert that a credential offer only contains unqualified identifiers. + */ +export function assertUnqualifiedCredentialOffer(credentialOffer: AnonCredsCredentialOffer) { + assertUnqualifiedCredentialDefinitionId(credentialOffer.cred_def_id) + assertUnqualifiedSchemaId(credentialOffer.schema_id) +} + +/** + * Assert that a credential request only contains unqualified identifiers. + */ +export function assertUnqualifiedCredentialRequest(credentialRequest: AnonCredsCredentialRequest) { + assertUnqualifiedCredentialDefinitionId(credentialRequest.cred_def_id) +} + +/** + * Assert that a proof request only contains unqualified identifiers. + */ +export function assertUnqualifiedProofRequest(proofRequest: AnonCredsProofRequest) { + const allRequested = [ + ...Object.values(proofRequest.requested_attributes), + ...Object.values(proofRequest.requested_predicates), + ] + + for (const requested of allRequested) { + for (const restriction of requested.restrictions ?? []) { + assertAllUnqualified({ + credentialDefinitionIds: [restriction.cred_def_id], + schemaIds: [restriction.schema_id], + revocationRegistryIds: [restriction.rev_reg_id], + issuerIds: [restriction.issuer_did, restriction.schema_issuer_did], + }) + } + } +} + +export function assertAllUnqualified({ + schemaIds = [], + credentialDefinitionIds = [], + revocationRegistryIds = [], + issuerIds = [], +}: { + schemaIds?: Array + credentialDefinitionIds?: Array + revocationRegistryIds?: Array + issuerIds?: Array +}) { + for (const schemaId of schemaIds) { + // We don't validate undefined values + if (!schemaId) continue + + assertUnqualifiedSchemaId(schemaId) + } + + for (const credentialDefinitionId of credentialDefinitionIds) { + // We don't validate undefined values + if (!credentialDefinitionId) continue + + assertUnqualifiedCredentialDefinitionId(credentialDefinitionId) + } + + for (const revocationRegistryId of revocationRegistryIds) { + // We don't validate undefined values + if (!revocationRegistryId) continue + + assertUnqualifiedRevocationRegistryId(revocationRegistryId) + } + + for (const issuerId of issuerIds) { + // We don't validate undefined values + if (!issuerId) continue + + assertUnqualifiedIssuerId(issuerId) + } +} diff --git a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts index decd85f10b..4cedb11ff4 100644 --- a/packages/indy-sdk/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-sdk/src/anoncreds/utils/identifiers.ts @@ -1,33 +1,17 @@ /** - * NOTE: this file is availalbe in both the indy-sdk and indy-vdr packages. If making changes to + * NOTE: this file is available in both the indy-sdk and indy-vdr packages. If making changes to * this file, make sure to update both files if applicable. */ -import { DID_INDY_REGEX } from '../../utils/did' - -const didIndyAnonCredsBase = - /(did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22}))\/anoncreds\/v0/ - -// did:indy::/anoncreds/v0/SCHEMA// -const didIndySchemaIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/SCHEMA/(.+)/([0-9.]+)$`) - -// :2:: -const legacyIndySchemaIdRegex = /^([a-zA-Z0-9]{21,22}):2:(.+):([0-9.]+)$/ - -// did:indy::/anoncreds/v0/CLAIM_DEF// -const didIndyCredentialDefinitionIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/CLAIM_DEF/([1-9][0-9]*)/(.+)$`) - -// :3:CL:: -const legacyIndyCredentialDefinitionIdRegex = /^([a-zA-Z0-9]{21,22}):3:CL:([1-9][0-9]*):(.+)$/ - -// did:indy::/anoncreds/v0/REV_REG_DEF/// -const didIndyRevocationRegistryIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/REV_REG_DEF/([1-9][0-9]*)/(.+)/(.+)$` -) - -// :4::3:CL::CL_ACCUM: -const legacyIndyRevocationRegistryIdRegex = - /^([a-zA-Z0-9]{21,22}):4:[a-zA-Z0-9]{21,22}:3:CL:([1-9][0-9]*):(.+):CL_ACCUM:(.+)$/ +import { + unqualifiedSchemaIdRegex, + unqualifiedCredentialDefinitionIdRegex, + unqualifiedRevocationRegistryIdRegex, + didIndyCredentialDefinitionIdRegex, + didIndyRevocationRegistryIdRegex, + didIndySchemaIdRegex, + didIndyRegex, +} from '@aries-framework/anoncreds' // combines both legacy and did:indy anoncreds identifiers and also the issuer id const indySdkAnonCredsRegexes = [ @@ -36,18 +20,18 @@ const indySdkAnonCredsRegexes = [ // As we find a matching anoncreds registry based on the issuerId only when creating an object, this will make sure // it will throw an no registry found for identifier error. // issuer id - DID_INDY_REGEX, + didIndyRegex, // schema didIndySchemaIdRegex, - legacyIndySchemaIdRegex, + unqualifiedSchemaIdRegex, // credential definition didIndyCredentialDefinitionIdRegex, - legacyIndyCredentialDefinitionIdRegex, + unqualifiedCredentialDefinitionIdRegex, // revocation registry - legacyIndyRevocationRegistryIdRegex, + unqualifiedRevocationRegistryIdRegex, didIndyRevocationRegistryIdRegex, ] @@ -59,14 +43,6 @@ export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, na return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/SCHEMA/${name}/${version}` } -export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { - return `${unqualifiedDid}:2:${name}:${version}` -} - -export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: string | number, tag: string) { - return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` -} - export function getDidIndyCredentialDefinitionId( namespace: string, unqualifiedDid: string, @@ -76,16 +52,6 @@ export function getDidIndyCredentialDefinitionId( return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/CLAIM_DEF/${seqNo}/${tag}` } -// TZQuLp43UcYTdtc3HewcDz:4:TZQuLp43UcYTdtc3HewcDz:3:CL:98158:BaustellenzertifikateNU1:CL_ACCUM:1-100 -export function getLegacyRevocationRegistryId( - unqualifiedDid: string, - seqNo: string | number, - credentialDefinitionTag: string, - revocationRegistryTag: string -) { - return `${unqualifiedDid}:4:${unqualifiedDid}:3:CL:${seqNo}:${credentialDefinitionTag}:CL_ACCUM:${revocationRegistryTag}` -} - export function getDidIndyRevocationRegistryId( namespace: string, unqualifiedDid: string, @@ -95,118 +61,3 @@ export function getDidIndyRevocationRegistryId( ) { return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_DEF/${seqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` } - -interface ParsedSchemaId { - did: string - namespaceIdentifier: string - schemaName: string - schemaVersion: string - namespace?: string -} - -export function parseSchemaId(schemaId: string): ParsedSchemaId { - const didIndyMatch = schemaId.match(didIndySchemaIdRegex) - if (didIndyMatch) { - const [, did, namespace, namespaceIdentifier, schemaName, schemaVersion] = didIndyMatch - - return { - did, - namespaceIdentifier, - schemaName, - schemaVersion, - namespace, - } - } - - const legacyMatch = schemaId.match(legacyIndySchemaIdRegex) - if (legacyMatch) { - const [, did, schemaName, schemaVersion] = legacyMatch - - return { - did, - namespaceIdentifier: did, - schemaName, - schemaVersion, - } - } - - throw new Error(`Invalid schema id: ${schemaId}`) -} - -interface ParsedCredentialDefinitionId { - did: string - namespaceIdentifier: string - schemaSeqNo: string - tag: string - namespace?: string -} - -export function parseCredentialDefinitionId(credentialDefinitionId: string): ParsedCredentialDefinitionId { - const didIndyMatch = credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) - if (didIndyMatch) { - const [, did, namespace, namespaceIdentifier, schemaSeqNo, tag] = didIndyMatch - - return { - did, - namespaceIdentifier, - schemaSeqNo, - tag, - namespace, - } - } - - const legacyMatch = credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) - if (legacyMatch) { - const [, did, schemaSeqNo, tag] = legacyMatch - - return { - did, - namespaceIdentifier: did, - schemaSeqNo, - tag, - } - } - - throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) -} - -interface ParsedRevocationRegistryId { - did: string - namespaceIdentifier: string - schemaSeqNo: string - credentialDefinitionTag: string - revocationRegistryTag: string - namespace?: string -} - -export function parseRevocationRegistryId(revocationRegistryId: string): ParsedRevocationRegistryId { - const didIndyMatch = revocationRegistryId.match(didIndyRevocationRegistryIdRegex) - if (didIndyMatch) { - const [, did, namespace, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = - didIndyMatch - - return { - did, - namespaceIdentifier, - schemaSeqNo, - credentialDefinitionTag, - revocationRegistryTag, - namespace, - } - } - - const legacyMatch = revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) - if (legacyMatch) { - const [, did, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = legacyMatch - - return { - did, - namespaceIdentifier: did, - schemaSeqNo, - credentialDefinitionTag, - revocationRegistryTag, - } - } - - throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) -} diff --git a/packages/indy-sdk/src/anoncreds/utils/transform.ts b/packages/indy-sdk/src/anoncreds/utils/transform.ts index 9ddc18f81d..73b5441c93 100644 --- a/packages/indy-sdk/src/anoncreds/utils/transform.ts +++ b/packages/indy-sdk/src/anoncreds/utils/transform.ts @@ -8,10 +8,10 @@ import type { } from '@aries-framework/anoncreds' import type { CredDef, CredReqMetadata, RevocReg, RevocRegDef, RevocRegDelta, Schema } from 'indy-sdk' -import { parseCredentialDefinitionId, parseSchemaId } from './identifiers' +import { parseIndyCredentialDefinitionId, parseIndySchemaId } from '@aries-framework/anoncreds' export function anonCredsSchemaFromIndySdk(schema: Schema): AnonCredsSchema { - const { did } = parseSchemaId(schema.id) + const { did } = parseIndySchemaId(schema.id) return { issuerId: did, name: schema.name, @@ -32,7 +32,7 @@ export function indySdkSchemaFromAnonCreds(schemaId: string, schema: AnonCredsSc } export function anonCredsCredentialDefinitionFromIndySdk(credentialDefinition: CredDef): AnonCredsCredentialDefinition { - const { did } = parseCredentialDefinitionId(credentialDefinition.id) + const { did } = parseIndyCredentialDefinitionId(credentialDefinition.id) return { issuerId: did, diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts index a7aba8eab1..2a3c6c3097 100644 --- a/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidRegistrar.ts @@ -12,6 +12,7 @@ import type { } from '@aries-framework/core' import type { NymRole } from 'indy-sdk' +import { parseIndyDid } from '@aries-framework/anoncreds' import { DidDocumentRole, DidRecord, DidRepository, KeyType, Key } from '@aries-framework/core' import { IndySdkError } from '../error' @@ -21,7 +22,7 @@ import { IndySdkSymbol } from '../types' import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' import { isLegacySelfCertifiedDid, legacyIndyDidFromPublicKeyBase58 } from '../utils/did' -import { createKeyAgreementKey, indyDidDocumentFromDid, parseIndyDid, verificationKeyForIndyDid } from './didIndyUtil' +import { createKeyAgreementKey, indyDidDocumentFromDid, verificationKeyForIndyDid } from './didIndyUtil' import { addServicesFromEndpointsAttrib } from './didSovUtil' export class IndySdkIndyDidRegistrar implements DidRegistrar { diff --git a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts index 4aa0ddf1d3..1c486eb3aa 100644 --- a/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts +++ b/packages/indy-sdk/src/dids/IndySdkIndyDidResolver.ts @@ -3,12 +3,14 @@ import type { IndySdkPool } from '../ledger' import type { IndySdk } from '../types' import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' +import { parseIndyDid } from '@aries-framework/anoncreds' + import { isIndyError, IndySdkError } from '../error' import { IndySdkPoolService } from '../ledger/IndySdkPoolService' import { IndySdkSymbol } from '../types' import { getFullVerkey } from '../utils/did' -import { createKeyAgreementKey, indyDidDocumentFromDid, parseIndyDid } from './didIndyUtil' +import { createKeyAgreementKey, indyDidDocumentFromDid } from './didIndyUtil' import { addServicesFromEndpointsAttrib } from './didSovUtil' export class IndySdkIndyDidResolver implements DidResolver { diff --git a/packages/indy-sdk/src/dids/didIndyUtil.ts b/packages/indy-sdk/src/dids/didIndyUtil.ts index 928ae1007e..7da10664b2 100644 --- a/packages/indy-sdk/src/dids/didIndyUtil.ts +++ b/packages/indy-sdk/src/dids/didIndyUtil.ts @@ -9,18 +9,6 @@ import { TypedArrayEncoder, } from '@aries-framework/core' -import { DID_INDY_REGEX } from '../utils/did' - -export function parseIndyDid(did: string) { - const match = did.match(DID_INDY_REGEX) - if (match) { - const [, namespace, namespaceIdentifier] = match - return { namespace, namespaceIdentifier } - } else { - throw new AriesFrameworkError(`${did} is not a valid did:indy did`) - } -} - // Create a base DIDDoc template according to https://hyperledger.github.io/indy-did-method/#base-diddoc-template export function indyDidDocumentFromDid(did: string, publicKeyBase58: string) { const verificationMethodId = `${did}#verkey` diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index d66251a83c..bf632152bb 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -3,6 +3,7 @@ import type { IndySdk } from '../types' import type { AgentContext, Key } from '@aries-framework/core' import type { GetNymResponse, LedgerReadReplyResponse, LedgerRequest, LedgerWriteReplyResponse } from 'indy-sdk' +import { didIndyRegex } from '@aries-framework/anoncreds' import { TypedArrayEncoder, CacheModuleConfig, @@ -17,7 +18,7 @@ import { Subject } from 'rxjs' import { IndySdkModuleConfig } from '../IndySdkModuleConfig' import { IndySdkError, isIndyError } from '../error' import { assertIndySdkWallet } from '../utils/assertIndySdkWallet' -import { DID_INDY_REGEX, isLegacySelfCertifiedDid } from '../utils/did' +import { isLegacySelfCertifiedDid } from '../utils/did' import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' import { IndySdkPool } from './IndySdkPool' @@ -62,7 +63,7 @@ export class IndySdkPoolService { * https://docs.google.com/document/d/109C_eMsuZnTnYe2OAd02jAts1vC4axwEKIq7_4dnNVA/edit * * This method will optionally return a nym response when the did has been resolved to determine the ledger - * either now or in the past. The nymResponse can be used to prevent multiple ledger quries fetching the same + * either now or in the past. The nymResponse can be used to prevent multiple ledger queries fetching the same * did */ public async getPoolForDid( @@ -70,7 +71,7 @@ export class IndySdkPoolService { did: string ): Promise<{ pool: IndySdkPool; nymResponse?: GetNymResponse }> { // Check if the did starts with did:indy - const match = did.match(DID_INDY_REGEX) + const match = did.match(didIndyRegex) if (match) { const [, namespace] = match diff --git a/packages/indy-sdk/src/utils/did.ts b/packages/indy-sdk/src/utils/did.ts index 7d78cd09e2..afb080696f 100644 --- a/packages/indy-sdk/src/utils/did.ts +++ b/packages/indy-sdk/src/utils/did.ts @@ -19,7 +19,6 @@ import { Buffer, TypedArrayEncoder } from '@aries-framework/core' export const FULL_VERKEY_REGEX = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/ export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ -export const DID_INDY_REGEX = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22})$/ /** * Check whether the did is a self certifying did. If the verkey is abbreviated this method diff --git a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts index fd33a35696..d4c2af8e38 100644 --- a/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts +++ b/packages/indy-sdk/tests/sov-did-resolver.e2e.test.ts @@ -1,9 +1,9 @@ import type { IndySdkIndyDidCreateOptions } from '../src' +import { parseIndyDid } from '@aries-framework/anoncreds' import { Agent, AriesFrameworkError, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' import { getAgentOptions, importExistingIndyDidFromPrivateKey, publicDidSeed } from '../../core/tests/helpers' -import { parseIndyDid } from '../src/dids/didIndyUtil' import { getIndySdkModules } from './setupIndySdkModule' diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 433f9d0ae5..01cdd18449 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -12,6 +12,15 @@ import type { } from '@aries-framework/anoncreds' import type { AgentContext } from '@aries-framework/core' +import { + getUnqualifiedCredentialDefinitionId, + getUnqualifiedRevocationRegistryId, + getUnqualifiedSchemaId, + parseIndyCredentialDefinitionId, + parseIndyDid, + parseIndyRevocationRegistryId, + parseIndySchemaId, +} from '@aries-framework/anoncreds' import { GetSchemaRequest, SchemaRequest, @@ -22,19 +31,13 @@ import { GetRevocationRegistryDefinitionRequest, } from '@hyperledger/indy-vdr-shared' -import { parseIndyDid, verificationKeyForIndyDid } from '../dids/didIndyUtil' +import { verificationKeyForIndyDid } from '../dids/didIndyUtil' import { IndyVdrPoolService } from '../pool' import { - getLegacySchemaId, - getLegacyCredentialDefinitionId, indyVdrAnonCredsRegistryIdentifierRegex, - parseSchemaId, getDidIndySchemaId, - parseCredentialDefinitionId, getDidIndyCredentialDefinitionId, - parseRevocationRegistryId, - getLegacyRevocationRegistryId, } from './utils/identifiers' import { anonCredsRevocationStatusListFromIndyVdr } from './utils/transform' @@ -48,12 +51,12 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) // parse schema id (supports did:indy and legacy) - const { did, namespaceIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId) + const { did, namespaceIdentifier, schemaName, schemaVersion } = parseIndySchemaId(schemaId) const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.indyNamespace}'`) // even though we support did:indy and legacy identifiers we always need to fetch using the legacy identifier - const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schemaName, schemaVersion) + const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, schemaName, schemaVersion) const request = new GetSchemaRequest({ schemaId: legacySchemaId }) agentContext.config.logger.trace( @@ -133,7 +136,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { options.schema.name, options.schema.version ) - const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version) + const legacySchemaId = getUnqualifiedSchemaId(namespaceIdentifier, options.schema.name, options.schema.version) const schemaRequest = new SchemaRequest({ submitterDid: namespaceIdentifier, @@ -198,14 +201,14 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const indyVdrPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) // we support did:indy and legacy identifiers - const { did, namespaceIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId) + const { did, namespaceIdentifier, schemaSeqNo, tag } = parseIndyCredentialDefinitionId(credentialDefinitionId) const { pool } = await indyVdrPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Getting credential definition '${credentialDefinitionId}' from ledger '${pool.indyNamespace}'` ) - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag) const request = new GetCredentialDefinitionRequest({ credentialDefinitionId: legacyCredentialDefinitionId, }) @@ -304,7 +307,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { } } - const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId( + const legacyCredentialDefinitionId = getUnqualifiedCredentialDefinitionId( options.credentialDefinition.issuerId, schemaMetadata.indyLedgerSeqNo, options.credentialDefinition.tag @@ -377,14 +380,14 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) const { did, namespaceIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } = - parseRevocationRegistryId(revocationRegistryDefinitionId) + parseIndyRevocationRegistryId(revocationRegistryDefinitionId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.indyNamespace}' to retrieve revocation registry definition '${revocationRegistryDefinitionId}'` ) - const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryId( namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, @@ -431,7 +434,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { schemaSeqNo, credentialDefinitionTag ) - : getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag) + : getUnqualifiedCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag) const revocationRegistryDefinition = { issuerId: did, @@ -488,14 +491,14 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const indySdkPoolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) const { did, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } = - parseRevocationRegistryId(revocationRegistryId) + parseIndyRevocationRegistryId(revocationRegistryId) const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did) agentContext.config.logger.debug( `Using ledger '${pool.indyNamespace}' to retrieve revocation registry deltas with revocation registry definition id '${revocationRegistryId}' until ${timestamp}` ) - const legacyRevocationRegistryId = getLegacyRevocationRegistryId( + const legacyRevocationRegistryId = getUnqualifiedRevocationRegistryId( namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, @@ -602,7 +605,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const schema = response.result.data?.txn.data as SchemaType - const schemaId = getLegacySchemaId(did, schema.data.name, schema.data.version) + const schemaId = getUnqualifiedSchemaId(did, schema.data.name, schema.data.version) return { schema: { diff --git a/packages/indy-vdr/src/anoncreds/utils/__tests__/identifiers.test.ts b/packages/indy-vdr/src/anoncreds/utils/__tests__/identifiers.test.ts new file mode 100644 index 0000000000..b96720611b --- /dev/null +++ b/packages/indy-vdr/src/anoncreds/utils/__tests__/identifiers.test.ts @@ -0,0 +1,79 @@ +import { + getDidIndyCredentialDefinitionId, + getDidIndyRevocationRegistryId, + getDidIndySchemaId, + indyVdrAnonCredsRegistryIdentifierRegex, +} from '../identifiers' + +describe('identifiers', () => { + describe('indyVdrAnonCredsRegistryIdentifierRegex', () => { + test('matches against a legacy schema id, credential definition id and revocation registry id', () => { + const did = '7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' + const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' + const revocationRegistryId = + 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' + + const anotherId = 'some:id' + + // unqualified issuerId not in regex on purpose. See note in implementation. + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(false) + + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + + test('matches against a did indy did, schema id, credential definition id and revocation registry id', () => { + const did = 'did:indy:local:7Tqg6BwSSWapxgUDm9KKgg' + const schemaId = 'did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0' + const credentialDefinitionId = + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID' + const revocationRegistryId = + 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/REV_REG_DEF/100669/SCH Employee ID/1-1024' + + const anotherId = 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/SOME_DEF' + + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) + expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) + }) + }) + + test('getDidIndySchemaId returns a valid schema id given a did, name, and version', () => { + const namespace = 'sovrin:test' + const did = '12345' + const name = 'backbench' + const version = '420' + + expect(getDidIndySchemaId(namespace, did, name, version)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/SCHEMA/backbench/420' + ) + }) + + test('getDidIndyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { + const namespace = 'sovrin:test' + const did = '12345' + const seqNo = 420 + const tag = 'someTag' + + expect(getDidIndyCredentialDefinitionId(namespace, did, seqNo, tag)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/CLAIM_DEF/420/someTag' + ) + }) + + test('getDidIndyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { + const namespace = 'sovrin:test' + const did = '12345' + const seqNo = 420 + const credentialDefinitionTag = 'someTag' + const tag = 'anotherTag' + + expect(getDidIndyRevocationRegistryId(namespace, did, seqNo, credentialDefinitionTag, tag)).toEqual( + 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag' + ) + }) +}) diff --git a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts b/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts deleted file mode 100644 index 1f01c18209..0000000000 --- a/packages/indy-vdr/src/anoncreds/utils/_tests_/identifiers.test.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { - getDidIndyCredentialDefinitionId, - getDidIndyRevocationRegistryId, - getDidIndySchemaId, - getLegacyCredentialDefinitionId, - getLegacyRevocationRegistryId, - getLegacySchemaId, - indyVdrAnonCredsRegistryIdentifierRegex, - parseCredentialDefinitionId, - parseRevocationRegistryId, - parseSchemaId, -} from '../identifiers' - -describe('identifiers', () => { - describe('indyVdrAnonCredsRegistryIdentifierRegex', () => { - test('matches against a legacy schema id, credential definition id and revocation registry id', () => { - const did = '7Tqg6BwSSWapxgUDm9KKgg' - const schemaId = 'BQ42WeE24jFHeyGg8x9XAz:2:Medical Bill:1.0' - const credentialDefinitionId = 'N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID' - const revocationRegistryId = - 'N7baRMcyvPwWc8v85CtZ6e:4:N7baRMcyvPwWc8v85CtZ6e:3:CL:100669:SCH Employee ID:CL_ACCUM:1-1024' - - const anotherId = 'some:id' - - // unqualified issuerId not in regex on purpose. See note in implementation. - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(false) - - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) - }) - - test('matches against a did indy did, schema id, credential definition id and revocation registry id', () => { - const did = 'did:indy:local:7Tqg6BwSSWapxgUDm9KKgg' - const schemaId = 'did:indy:local:BQ42WeE24jFHeyGg8x9XAz/anoncreds/v0/SCHEMA/Medical Bill/1.0' - const credentialDefinitionId = - 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/CLAIM_DEF/100669/SCH Employee ID' - const revocationRegistryId = - 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/REV_REG_DEF/100669/SCH Employee ID/1-1024' - - const anotherId = 'did:indy:local:N7baRMcyvPwWc8v85CtZ6e/anoncreds/v0/SOME_DEF' - - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(did)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(schemaId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(credentialDefinitionId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(revocationRegistryId)).toEqual(true) - expect(indyVdrAnonCredsRegistryIdentifierRegex.test(anotherId)).toEqual(false) - }) - }) - - test('getLegacySchemaId returns a valid schema id given a did, name, and version', () => { - const did = '12345' - const name = 'backbench' - const version = '420' - - expect(getLegacySchemaId(did, name, version)).toEqual('12345:2:backbench:420') - }) - - test('getLegacyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { - const did = '12345' - const seqNo = 420 - const tag = 'someTag' - - expect(getLegacyCredentialDefinitionId(did, seqNo, tag)).toEqual('12345:3:CL:420:someTag') - }) - - test('getLegacyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { - const did = '12345' - const seqNo = 420 - const credentialDefinitionTag = 'someTag' - const tag = 'anotherTag' - - expect(getLegacyRevocationRegistryId(did, seqNo, credentialDefinitionTag, tag)).toEqual( - '12345:4:12345:3:CL:420:someTag:CL_ACCUM:anotherTag' - ) - }) - - test('getDidIndySchemaId returns a valid schema id given a did, name, and version', () => { - const namespace = 'sovrin:test' - const did = '12345' - const name = 'backbench' - const version = '420' - - expect(getDidIndySchemaId(namespace, did, name, version)).toEqual( - 'did:indy:sovrin:test:12345/anoncreds/v0/SCHEMA/backbench/420' - ) - }) - - test('getDidIndyCredentialDefinitionId returns a valid credential definition id given a did, seqNo, and tag', () => { - const namespace = 'sovrin:test' - const did = '12345' - const seqNo = 420 - const tag = 'someTag' - - expect(getDidIndyCredentialDefinitionId(namespace, did, seqNo, tag)).toEqual( - 'did:indy:sovrin:test:12345/anoncreds/v0/CLAIM_DEF/420/someTag' - ) - }) - - test('getDidIndyRevocationRegistryId returns a valid credential definition id given a did, seqNo, and tag', () => { - const namespace = 'sovrin:test' - const did = '12345' - const seqNo = 420 - const credentialDefinitionTag = 'someTag' - const tag = 'anotherTag' - - expect(getDidIndyRevocationRegistryId(namespace, did, seqNo, credentialDefinitionTag, tag)).toEqual( - 'did:indy:sovrin:test:12345/anoncreds/v0/REV_REG_DEF/420/someTag/anotherTag' - ) - }) - - describe('parseSchemaId', () => { - test('parses legacy schema id', () => { - expect(parseSchemaId('SDqTzbVuCowusqGBNbNDjH:2:schema-name:1.0')).toEqual({ - did: 'SDqTzbVuCowusqGBNbNDjH', - namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', - schemaName: 'schema-name', - schemaVersion: '1.0', - }) - }) - - test('parses did:indy schema id', () => { - expect(parseSchemaId('did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH/anoncreds/v0/SCHEMA/schema-name/1.0')).toEqual( - { - namespaceIdentifier: 'SDqTzbVuCowusqGBNbNDjH', - did: 'did:indy:bcovrin:test:SDqTzbVuCowusqGBNbNDjH', - schemaName: 'schema-name', - schemaVersion: '1.0', - namespace: 'bcovrin:test', - } - ) - }) - }) - - describe('parseCredentialDefinitionId', () => { - test('parses legacy credential definition id', () => { - expect(parseCredentialDefinitionId('TL1EaPFCZ8Si5aUrqScBDt:3:CL:10:TAG')).toEqual({ - did: 'TL1EaPFCZ8Si5aUrqScBDt', - namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', - schemaSeqNo: '10', - tag: 'TAG', - }) - }) - - test('parses did:indy credential definition id', () => { - expect( - parseCredentialDefinitionId('did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt/anoncreds/v0/CLAIM_DEF/10/TAG') - ).toEqual({ - namespaceIdentifier: 'TL1EaPFCZ8Si5aUrqScBDt', - did: 'did:indy:pool:localtest:TL1EaPFCZ8Si5aUrqScBDt', - namespace: 'pool:localtest', - schemaSeqNo: '10', - tag: 'TAG', - }) - }) - }) - - describe('parseRevocationRegistryId', () => { - test('parses legacy revocation registry id', () => { - expect( - parseRevocationRegistryId('5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1') - ).toEqual({ - did: '5nDyJVP1NrcPAttP3xwMB9', - namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', - schemaSeqNo: '56495', - credentialDefinitionTag: 'npdb', - revocationRegistryTag: 'TAG1', - }) - }) - - test('parses did:indy revocation registry id', () => { - expect( - parseRevocationRegistryId('did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/anoncreds/v0/REV_REG_DEF/56495/npdb/TAG1') - ).toEqual({ - namespace: 'sovrin', - namespaceIdentifier: '5nDyJVP1NrcPAttP3xwMB9', - did: 'did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9', - schemaSeqNo: '56495', - credentialDefinitionTag: 'npdb', - revocationRegistryTag: 'TAG1', - }) - }) - }) -}) diff --git a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts index cc05d2b3bb..1e9d6a8fd3 100644 --- a/packages/indy-vdr/src/anoncreds/utils/identifiers.ts +++ b/packages/indy-vdr/src/anoncreds/utils/identifiers.ts @@ -3,31 +3,15 @@ * this file, make sure to update both files if applicable. */ -import { DID_INDY_REGEX } from '../../utils/did' - -const didIndyAnonCredsBase = - /(did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22}))\/anoncreds\/v0/ - -// did:indy::/anoncreds/v0/SCHEMA// -const didIndySchemaIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/SCHEMA/(.+)/([0-9.]+)$`) - -// :2:: -const legacyIndySchemaIdRegex = /^([a-zA-Z0-9]{21,22}):2:(.+):([0-9.]+)$/ - -// did:indy::/anoncreds/v0/CLAIM_DEF// -const didIndyCredentialDefinitionIdRegex = new RegExp(`^${didIndyAnonCredsBase.source}/CLAIM_DEF/([1-9][0-9]*)/(.+)$`) - -// :3:CL:: -const legacyIndyCredentialDefinitionIdRegex = /^([a-zA-Z0-9]{21,22}):3:CL:([1-9][0-9]*):(.+)$/ - -// did:indy::/anoncreds/v0/REV_REG_DEF/// -const didIndyRevocationRegistryIdRegex = new RegExp( - `^${didIndyAnonCredsBase.source}/REV_REG_DEF/([1-9][0-9]*)/(.+)/(.+)$` -) - -// :4::3:CL::CL_ACCUM: -const legacyIndyRevocationRegistryIdRegex = - /^([a-zA-Z0-9]{21,22}):4:[a-zA-Z0-9]{21,22}:3:CL:([1-9][0-9]*):(.+):CL_ACCUM:(.+)$/ +import { + unqualifiedSchemaIdRegex, + unqualifiedCredentialDefinitionIdRegex, + unqualifiedRevocationRegistryIdRegex, + didIndyCredentialDefinitionIdRegex, + didIndyRevocationRegistryIdRegex, + didIndySchemaIdRegex, + didIndyRegex, +} from '@aries-framework/anoncreds' // combines both legacy and did:indy anoncreds identifiers and also the issuer id const indyVdrAnonCredsRegexes = [ @@ -36,18 +20,18 @@ const indyVdrAnonCredsRegexes = [ // As we find a matching anoncreds registry based on the issuerId only when creating an object, this will make sure // it will throw an no registry found for identifier error. // issuer id - DID_INDY_REGEX, + didIndyRegex, // schema didIndySchemaIdRegex, - legacyIndySchemaIdRegex, + unqualifiedSchemaIdRegex, // credential definition didIndyCredentialDefinitionIdRegex, - legacyIndyCredentialDefinitionIdRegex, + unqualifiedCredentialDefinitionIdRegex, // revocation registry - legacyIndyRevocationRegistryIdRegex, + unqualifiedRevocationRegistryIdRegex, didIndyRevocationRegistryIdRegex, ] @@ -59,14 +43,6 @@ export function getDidIndySchemaId(namespace: string, unqualifiedDid: string, na return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/SCHEMA/${name}/${version}` } -export function getLegacySchemaId(unqualifiedDid: string, name: string, version: string) { - return `${unqualifiedDid}:2:${name}:${version}` -} - -export function getLegacyCredentialDefinitionId(unqualifiedDid: string, seqNo: string | number, tag: string) { - return `${unqualifiedDid}:3:CL:${seqNo}:${tag}` -} - export function getDidIndyCredentialDefinitionId( namespace: string, unqualifiedDid: string, @@ -76,16 +52,6 @@ export function getDidIndyCredentialDefinitionId( return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/CLAIM_DEF/${seqNo}/${tag}` } -// TZQuLp43UcYTdtc3HewcDz:4:TZQuLp43UcYTdtc3HewcDz:3:CL:98158:BaustellenzertifikateNU1:CL_ACCUM:1-100 -export function getLegacyRevocationRegistryId( - unqualifiedDid: string, - seqNo: string | number, - credentialDefinitionTag: string, - revocationRegistryTag: string -) { - return `${unqualifiedDid}:4:${unqualifiedDid}:3:CL:${seqNo}:${credentialDefinitionTag}:CL_ACCUM:${revocationRegistryTag}` -} - export function getDidIndyRevocationRegistryId( namespace: string, unqualifiedDid: string, @@ -95,118 +61,3 @@ export function getDidIndyRevocationRegistryId( ) { return `did:indy:${namespace}:${unqualifiedDid}/anoncreds/v0/REV_REG_DEF/${seqNo}/${credentialDefinitionTag}/${revocationRegistryTag}` } - -interface ParsedSchemaId { - did: string - namespaceIdentifier: string - schemaName: string - schemaVersion: string - namespace?: string -} - -export function parseSchemaId(schemaId: string): ParsedSchemaId { - const didIndyMatch = schemaId.match(didIndySchemaIdRegex) - if (didIndyMatch) { - const [, did, namespace, namespaceIdentifier, schemaName, schemaVersion] = didIndyMatch - - return { - did, - namespaceIdentifier, - schemaName, - schemaVersion, - namespace, - } - } - - const legacyMatch = schemaId.match(legacyIndySchemaIdRegex) - if (legacyMatch) { - const [, did, schemaName, schemaVersion] = legacyMatch - - return { - did, - namespaceIdentifier: did, - schemaName, - schemaVersion, - } - } - - throw new Error(`Invalid schema id: ${schemaId}`) -} - -interface ParsedCredentialDefinitionId { - did: string - namespaceIdentifier: string - schemaSeqNo: string - tag: string - namespace?: string -} - -export function parseCredentialDefinitionId(credentialDefinitionId: string): ParsedCredentialDefinitionId { - const didIndyMatch = credentialDefinitionId.match(didIndyCredentialDefinitionIdRegex) - if (didIndyMatch) { - const [, did, namespace, namespaceIdentifier, schemaSeqNo, tag] = didIndyMatch - - return { - did, - namespaceIdentifier, - schemaSeqNo, - tag, - namespace, - } - } - - const legacyMatch = credentialDefinitionId.match(legacyIndyCredentialDefinitionIdRegex) - if (legacyMatch) { - const [, did, schemaSeqNo, tag] = legacyMatch - - return { - did, - namespaceIdentifier: did, - schemaSeqNo, - tag, - } - } - - throw new Error(`Invalid credential definition id: ${credentialDefinitionId}`) -} - -interface ParsedRevocationRegistryId { - did: string - namespaceIdentifier: string - schemaSeqNo: string - credentialDefinitionTag: string - revocationRegistryTag: string - namespace?: string -} - -export function parseRevocationRegistryId(revocationRegistryId: string): ParsedRevocationRegistryId { - const didIndyMatch = revocationRegistryId.match(didIndyRevocationRegistryIdRegex) - if (didIndyMatch) { - const [, did, namespace, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = - didIndyMatch - - return { - did, - namespaceIdentifier, - schemaSeqNo, - credentialDefinitionTag, - revocationRegistryTag, - namespace, - } - } - - const legacyMatch = revocationRegistryId.match(legacyIndyRevocationRegistryIdRegex) - if (legacyMatch) { - const [, did, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag] = legacyMatch - - return { - did, - namespaceIdentifier: did, - schemaSeqNo, - credentialDefinitionTag, - revocationRegistryTag, - } - } - - throw new Error(`Invalid revocation registry id: ${revocationRegistryId}`) -} diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index ffcc98b929..33a0e088a9 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -11,6 +11,7 @@ import type { DidDocumentService, } from '@aries-framework/core' +import { parseIndyDid } from '@aries-framework/anoncreds' import { IndyAgentService, DidCommV1Service, @@ -32,7 +33,6 @@ import { createKeyAgreementKey, didDocDiff, indyDidDocumentFromDid, - parseIndyDid, isSelfCertifiedIndyDid, verificationKeyForIndyDid, } from './didIndyUtil' diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts index 124e5da88e..ddc339c745 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts @@ -2,12 +2,13 @@ import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' import type { IndyVdrPool } from '../pool' import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' +import { parseIndyDid } from '@aries-framework/anoncreds' import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrError, IndyVdrNotFoundError } from '../error' import { IndyVdrPoolService } from '../pool' -import { combineDidDocumentWithJson, createKeyAgreementKey, indyDidDocumentFromDid, parseIndyDid } from './didIndyUtil' +import { combineDidDocumentWithJson, createKeyAgreementKey, indyDidDocumentFromDid } from './didIndyUtil' import { getFullVerkey, addServicesFromEndpointsAttrib } from './didSovUtil' export class IndyVdrIndyDidResolver implements DidResolver { diff --git a/packages/indy-vdr/src/dids/didIndyUtil.ts b/packages/indy-vdr/src/dids/didIndyUtil.ts index e91f5ebd2b..50adce5226 100644 --- a/packages/indy-vdr/src/dids/didIndyUtil.ts +++ b/packages/indy-vdr/src/dids/didIndyUtil.ts @@ -1,5 +1,6 @@ import type { AgentContext } from '@aries-framework/core' +import { parseIndyDid } from '@aries-framework/anoncreds' import { getKeyFromVerificationMethod, AriesFrameworkError, @@ -14,8 +15,6 @@ import { TypedArrayEncoder, } from '@aries-framework/core' -import { DID_INDY_REGEX } from '../utils/did' - // Create a base DIDDoc template according to https://hyperledger.github.io/indy-did-method/#base-diddoc-template export function indyDidDocumentFromDid(did: string, verKeyBase58: string) { const verificationMethodId = `${did}#verkey` @@ -39,16 +38,6 @@ export function createKeyAgreementKey(verkey: string) { return TypedArrayEncoder.toBase58(convertPublicKeyToX25519(TypedArrayEncoder.fromBase58(verkey))) } -export function parseIndyDid(did: string) { - const match = did.match(DID_INDY_REGEX) - if (match) { - const [, namespace, namespaceIdentifier] = match - return { namespace, namespaceIdentifier } - } else { - throw new AriesFrameworkError(`${did} is not a valid did:indy did`) - } -} - const deepMerge = (a: Record, b: Record) => { const output: Record = {} diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index 69ee1026a0..fc05f63672 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -1,12 +1,13 @@ import type { AgentContext } from '@aries-framework/core' import type { GetNymResponse } from '@hyperledger/indy-vdr-shared' +import { didIndyRegex } from '@aries-framework/anoncreds' import { Logger, InjectionSymbols, injectable, inject, CacheModuleConfig } from '@aries-framework/core' import { GetNymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrModuleConfig } from '../IndyVdrModuleConfig' import { IndyVdrError, IndyVdrNotFoundError, IndyVdrNotConfiguredError } from '../error' -import { isSelfCertifiedDid, DID_INDY_REGEX } from '../utils/did' +import { isSelfCertifiedDid } from '../utils/did' import { allSettled, onlyFulfilled, onlyRejected } from '../utils/promises' import { IndyVdrPool } from './IndyVdrPool' @@ -46,7 +47,7 @@ export class IndyVdrPoolService { did: string ): Promise<{ pool: IndyVdrPool; nymResponse?: CachedDidResponse['nymResponse'] }> { // Check if the did starts with did:indy - const match = did.match(DID_INDY_REGEX) + const match = did.match(didIndyRegex) if (match) { const [, namespace] = match diff --git a/packages/indy-vdr/src/utils/did.ts b/packages/indy-vdr/src/utils/did.ts index 44632246bb..f3f346f070 100644 --- a/packages/indy-vdr/src/utils/did.ts +++ b/packages/indy-vdr/src/utils/did.ts @@ -17,7 +17,6 @@ import { TypedArrayEncoder } from '@aries-framework/core' -export const DID_INDY_REGEX = /^did:indy:((?:[a-z][_a-z0-9-]*)(?::[a-z][_a-z0-9-]*)?):([1-9A-HJ-NP-Za-km-z]{21,22})$/ export const ABBREVIATED_VERKEY_REGEX = /^~[1-9A-HJ-NP-Za-km-z]{21,22}$/ /** diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index dbf68311d0..2e8035fed0 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -1,5 +1,6 @@ import type { IndyVdrDidCreateOptions } from '../src/dids/IndyVdrIndyDidRegistrar' +import { didIndyRegex } from '@aries-framework/anoncreds' import { Key, JsonTransformer, @@ -22,7 +23,6 @@ import { IndyVdrModule, IndyVdrSovDidResolver } from '../src' import { IndyVdrIndyDidRegistrar } from '../src/dids/IndyVdrIndyDidRegistrar' import { IndyVdrIndyDidResolver } from '../src/dids/IndyVdrIndyDidResolver' import { indyDidFromNamespaceAndInitialKey } from '../src/dids/didIndyUtil' -import { DID_INDY_REGEX } from '../src/utils/did' import { indyVdrModuleConfig } from './helpers' @@ -76,16 +76,16 @@ describe('Indy VDR Indy Did Registrar', () => { didRegistrationMetadata: {}, didState: { state: 'finished', - did: expect.stringMatching(DID_INDY_REGEX), + did: expect.stringMatching(didIndyRegex), didDocument: { '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], - id: expect.stringMatching(DID_INDY_REGEX), + id: expect.stringMatching(didIndyRegex), alsoKnownAs: undefined, controller: undefined, verificationMethod: [ { type: 'Ed25519VerificationKey2018', - controller: expect.stringMatching(DID_INDY_REGEX), + controller: expect.stringMatching(didIndyRegex), id: expect.stringContaining('#verkey'), publicKeyBase58: expect.any(String), }, diff --git a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts index 14b2a7e202..cc987e7888 100644 --- a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts @@ -1,3 +1,4 @@ +import { parseIndyDid } from '@aries-framework/anoncreds' import { DidsModule, Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' @@ -6,7 +7,6 @@ import { IndySdkModule } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' import { IndyVdrModule } from '../src' import { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from '../src/dids' -import { parseIndyDid } from '../src/dids/didIndyUtil' import { createDidOnLedger, indyVdrModuleConfig } from './helpers' From 66afda2fe7311977047928e0b1c857ed2c5602c7 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 14 Apr 2023 20:05:14 -0300 Subject: [PATCH 125/139] fix(connections): store imageUrl when using DIDExchange (#1433) Signed-off-by: Ariel Gentile --- packages/core/src/modules/connections/DidExchangeProtocol.ts | 1 + packages/core/tests/oob.test.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 531b133e56..e7f56e23ec 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -98,6 +98,7 @@ export class DidExchangeProtocol { autoAcceptConnection: outOfBandRecord.autoAcceptConnection, outOfBandId: outOfBandRecord.id, invitationDid, + imageUrl: outOfBandInvitation.imageUrl, }) DidExchangeStateMachine.assertCreateMessageState(DidExchangeRequestMessage.type, connectionRecord) diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index 0ee9e59a51..c05cb5c0a2 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -50,6 +50,7 @@ describe('out of band', () => { goalCode: 'p2p-messaging', label: 'Faber College', alias: `Faber's connection with Alice`, + imageUrl: 'http://faber.image.url', } const issueCredentialConfig = { @@ -186,6 +187,7 @@ describe('out of band', () => { expect(outOfBandRecord.outOfBandInvitation.goal).toBe(makeConnectionConfig.goal) expect(outOfBandRecord.outOfBandInvitation.goalCode).toBe(makeConnectionConfig.goalCode) expect(outOfBandRecord.outOfBandInvitation.label).toBe(makeConnectionConfig.label) + expect(outOfBandRecord.outOfBandInvitation.imageUrl).toBe(makeConnectionConfig.imageUrl) }) test('create OOB message only with handshake', async () => { @@ -316,6 +318,7 @@ describe('out of band', () => { expect(faberAliceConnection?.state).toBe(DidExchangeState.Completed) expect(aliceFaberConnection).toBeConnectedWith(faberAliceConnection!) + expect(aliceFaberConnection.imageUrl).toBe(makeConnectionConfig.imageUrl) expect(faberAliceConnection).toBeConnectedWith(aliceFaberConnection) expect(faberAliceConnection.alias).toBe(makeConnectionConfig.alias) }) From 8a933c057e0c88870779bf8eb98b4684de4745de Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Sun, 16 Apr 2023 17:05:36 -0300 Subject: [PATCH 126/139] fix(indy-vdr): do not force indy-vdr version (#1434) Signed-off-by: Ariel Gentile --- demo/package.json | 2 +- packages/indy-vdr/package.json | 4 ++-- yarn.lock | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/demo/package.json b/demo/package.json index 5b5eb93d5e..2bafc0c67c 100644 --- a/demo/package.json +++ b/demo/package.json @@ -14,7 +14,7 @@ "refresh": "rm -rf ./node_modules ./yarn.lock && yarn" }, "dependencies": { - "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.13", + "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.14", "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", "inquirer": "^8.2.5" diff --git a/packages/indy-vdr/package.json b/packages/indy-vdr/package.json index a4cf2dc94b..86239fd7e7 100644 --- a/packages/indy-vdr/package.json +++ b/packages/indy-vdr/package.json @@ -26,10 +26,10 @@ "dependencies": { "@aries-framework/anoncreds": "0.3.3", "@aries-framework/core": "0.3.3", - "@hyperledger/indy-vdr-shared": "0.1.0-dev.13" + "@hyperledger/indy-vdr-shared": "^0.1.0-dev.14" }, "devDependencies": { - "@hyperledger/indy-vdr-nodejs": "0.1.0-dev.13", + "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.14", "@stablelib/ed25519": "^1.0.2", "rimraf": "^4.4.0", "rxjs": "^7.2.0", diff --git a/yarn.lock b/yarn.lock index 165f1ed715..202590163b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1072,12 +1072,12 @@ dependencies: fast-text-encoding "^1.0.3" -"@hyperledger/indy-vdr-nodejs@0.1.0-dev.13": - version "0.1.0-dev.13" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.13.tgz#1630d7e00366160d3ef6ae0237528faa749ca831" - integrity sha512-r5s5GuKjTP1ALkW4sG6oGdSGPX407nCEHB0lTapVuDF7aUHRsYQkcGUkyMESH2LmCSoxAvwM05RVKJOHYnK7zg== +"@hyperledger/indy-vdr-nodejs@^0.1.0-dev.14": + version "0.1.0-dev.14" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-nodejs/-/indy-vdr-nodejs-0.1.0-dev.14.tgz#d21590d5464341fb701b654c132c75c96e1cba8f" + integrity sha512-iuR4At4Vs2tQhctZH84KlWKFL1JI6BXa+2dnBauQXhMEQj8q2l5kTH8TjJeXPD1PyOnbLB9ry4tfx+/a0A2AKg== dependencies: - "@hyperledger/indy-vdr-shared" "0.1.0-dev.13" + "@hyperledger/indy-vdr-shared" "0.1.0-dev.14" "@mapbox/node-pre-gyp" "^1.0.10" "@types/ref-array-di" "^1.2.5" ffi-napi "^4.0.3" @@ -1085,10 +1085,10 @@ ref-napi "^3.0.3" ref-struct-di "^1.1.1" -"@hyperledger/indy-vdr-shared@0.1.0-dev.13": - version "0.1.0-dev.13" - resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.13.tgz#ccecd11b706253e61be16c1e8098ba045a5a42d1" - integrity sha512-MxSlxZSy9lpfFB6/APHclxOqFwWbyEGJtg+8u9CgAVtyHA8AqdM5MQc7A485VzUFYqV95f1FmE+8W5qpPSLTZw== +"@hyperledger/indy-vdr-shared@0.1.0-dev.14", "@hyperledger/indy-vdr-shared@^0.1.0-dev.14": + version "0.1.0-dev.14" + resolved "https://registry.yarnpkg.com/@hyperledger/indy-vdr-shared/-/indy-vdr-shared-0.1.0-dev.14.tgz#7dc3d61f997c1bcf737b23133ed0d08fec2a901d" + integrity sha512-CsZUcqybgtvVEVD1uHHgCGY3tddYcePJKjkEar14pss4w2IxbhRYnzkfp0+lWYUQnAY7rGX8A0MxRxwd7K1Yhw== "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" From ccc7f28581acf495d3b3d55c684425ffa34df2a4 Mon Sep 17 00:00:00 2001 From: Ry Jones Date: Wed, 19 Apr 2023 08:12:29 -0700 Subject: [PATCH 127/139] chore(ci): switch to dedicated Aries runners (#1436) Signed-off-by: Ry Jones --- .github/workflows/continuous-integration.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 250bec536c..64ffe6ca14 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -26,7 +26,7 @@ jobs: # validation scripts. To still be able to run the CI we can manually trigger it by adding the 'ci-test' # label to the pull request ci-trigger: - runs-on: ubuntu-latest-m + runs-on: aries-ubuntu-latest outputs: triggered: ${{ steps.check.outputs.triggered }} steps: @@ -45,7 +45,7 @@ jobs: echo "::set-output name=triggered::${SHOULD_RUN}" validate: - runs-on: ubuntu-latest-m + runs-on: aries-ubuntu-latest name: Validate steps: - name: Checkout aries-framework-javascript @@ -79,7 +79,7 @@ jobs: run: yarn build integration-test: - runs-on: ubuntu-latest-m + runs-on: aries-ubuntu-latest name: Integration Tests strategy: @@ -126,7 +126,7 @@ jobs: if: always() version-stable: - runs-on: ubuntu-latest-m + runs-on: aries-ubuntu-latest name: Release stable needs: [integration-test, validate] if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' From cf98f76d7d12f83bfdbac6375856f3b45c299780 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Wed, 19 Apr 2023 20:57:11 +0200 Subject: [PATCH 128/139] chore: export askar types (#1437) Signed-off-by: martin auer --- packages/askar/src/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/askar/src/index.ts b/packages/askar/src/index.ts index ed7baf9247..438ae1d7f9 100644 --- a/packages/askar/src/index.ts +++ b/packages/askar/src/index.ts @@ -1,5 +1,10 @@ // Wallet -export { AskarWallet } from './wallet' +export { + AskarWallet, + AskarWalletPostgresStorageConfig, + AskarWalletPostgresConfig, + AskarWalletPostgresCredentials, +} from './wallet' // Storage export { AskarStorageService } from './storage' From 1ffb0111fc3db170e5623d350cb912b22027387a Mon Sep 17 00:00:00 2001 From: DaevMithran <61043607+DaevMithran@users.noreply.github.com> Date: Wed, 19 Apr 2023 15:04:45 -0700 Subject: [PATCH 129/139] feat: Add cheqd demo and localnet for tests (#1435) --- .github/actions/setup-cheqd/action.yml | 14 ++++++++ .github/workflows/continuous-integration.yml | 3 ++ README.md | 8 +++++ demo/package.json | 1 + demo/src/BaseAgent.ts | 23 ++++++++++-- demo/src/Faber.ts | 34 ++++++++++++------ demo/src/FaberInquirer.ts | 4 ++- packages/cheqd/README.md | 35 +++++++++++++++++++ .../tests/cheqd-did-resolver.e2e.test.ts | 15 ++++---- .../cheqd-sdk-anoncreds-registry.e2e.test.ts | 11 +++--- packages/cheqd/tests/setupCheqdModule.ts | 7 ++-- 11 files changed, 129 insertions(+), 26 deletions(-) create mode 100644 .github/actions/setup-cheqd/action.yml create mode 100644 packages/cheqd/README.md diff --git a/.github/actions/setup-cheqd/action.yml b/.github/actions/setup-cheqd/action.yml new file mode 100644 index 0000000000..e8a64207e7 --- /dev/null +++ b/.github/actions/setup-cheqd/action.yml @@ -0,0 +1,14 @@ +name: Setup cheqd +description: Setup a cheqd network to perform tests +author: 'daev@cheqd.io' + +runs: + using: composite + steps: + - name: Start cheqd localnet + run: docker run --rm -d -p 26657:26657 ghcr.io/cheqd/cheqd-testnet:latest + shell: bash + +branding: + icon: scissors + color: purple diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 64ffe6ca14..b68c37f1cb 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -101,6 +101,9 @@ jobs: with: seed: ${TEST_AGENT_PUBLIC_DID_SEED} + - name: Setup Cheqd + uses: ./.github/actions/setup-cheqd + - name: Setup Postgres uses: ./.github/actions/setup-postgres diff --git a/README.md b/README.md index 45725071db..26794e54ba 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,14 @@ Aries Framework JavaScript is a framework written in TypeScript for building **S + + @aries-framework/cheqd + + + @aries-framework/cheqd version + + + @aries-framework/askar diff --git a/demo/package.json b/demo/package.json index 2bafc0c67c..85b1951908 100644 --- a/demo/package.json +++ b/demo/package.json @@ -26,6 +26,7 @@ "@aries-framework/core": "*", "@aries-framework/indy-sdk": "*", "@aries-framework/indy-vdr": "*", + "@aries-framework/cheqd": "*", "@aries-framework/node": "*", "@types/figlet": "^1.5.4", "@types/indy-sdk": "^1.16.26", diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index f7b252c36a..ac0281fbff 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -11,6 +11,13 @@ import { } from '@aries-framework/anoncreds' import { AnonCredsRsModule } from '@aries-framework/anoncreds-rs' import { AskarModule } from '@aries-framework/askar' +import { + CheqdAnonCredsRegistry, + CheqdDidRegistrar, + CheqdDidResolver, + CheqdModule, + CheqdModuleConfig, +} from '@aries-framework/cheqd' import { ConnectionsModule, DidsModule, @@ -129,7 +136,7 @@ function getAskarAnonCredsIndyModules() { ], }), anoncreds: new AnonCredsModule({ - registries: [new IndyVdrAnonCredsRegistry()], + registries: [new IndyVdrAnonCredsRegistry(), new CheqdAnonCredsRegistry()], }), anoncredsRs: new AnonCredsRsModule({ anoncreds, @@ -138,8 +145,20 @@ function getAskarAnonCredsIndyModules() { indyVdr, networks: [indyNetworkConfig], }), + cheqd: new CheqdModule( + new CheqdModuleConfig({ + networks: [ + { + network: 'testnet', + cosmosPayerSeed: + 'robust across amount corn curve panther opera wish toe ring bleak empower wreck party abstract glad average muffin picnic jar squeeze annual long aunt', + }, + ], + }) + ), dids: new DidsModule({ - resolvers: [new IndyVdrSovDidResolver()], + resolvers: [new IndyVdrSovDidResolver(), new CheqdDidResolver()], + registrars: [new CheqdDidRegistrar()], }), askar: new AskarModule({ ariesAskar, diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index fdaca44b66..0aae2e702f 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -8,6 +8,11 @@ import { ui } from 'inquirer' import { BaseAgent } from './BaseAgent' import { Color, greenText, Output, purpleText, redText } from './OutputClass' +export enum RegistryOptions { + indy = 'did:indy', + cheqd = 'did:cheqd', +} + export class Faber extends BaseAgent { public outOfBandId?: string public credentialDefinition?: RegisterCredentialDefinitionReturnStateFinished @@ -22,25 +27,34 @@ export class Faber extends BaseAgent { public static async build(): Promise { const faber = new Faber(9001, 'faber') await faber.initializeAgent() + return faber + } + public async importDid(registry: string) { // NOTE: we assume the did is already registered on the ledger, we just store the private key in the wallet // and store the existing did in the wallet const privateKey = TypedArrayEncoder.fromString('afjdemoverysercure00000000000000') - - const key = await faber.agent.wallet.createKey({ + const key = await this.agent.wallet.createKey({ keyType: KeyType.Ed25519, privateKey, }) - // did is first 16 bytes of public key encoded as base58 const unqualifiedIndyDid = TypedArrayEncoder.toBase58(key.publicKey.slice(0, 16)) - await faber.agent.dids.import({ - did: `did:sov:${unqualifiedIndyDid}`, - }) - - faber.anonCredsIssuerId = unqualifiedIndyDid - - return faber + const cheqdDid = 'did:cheqd:testnet:2d6841a0-8614-44c0-95c5-d54c61e420f2' + switch (registry) { + case RegistryOptions.indy: + await this.agent.dids.import({ + did: `did:sov:${unqualifiedIndyDid}`, + }) + this.anonCredsIssuerId = unqualifiedIndyDid + break + case RegistryOptions.cheqd: + await this.agent.dids.import({ + did: cheqdDid, + }) + this.anonCredsIssuerId = cheqdDid + break + } } private async getConnectionRecord() { diff --git a/demo/src/FaberInquirer.ts b/demo/src/FaberInquirer.ts index 6324c5a1d6..7eb4ac7785 100644 --- a/demo/src/FaberInquirer.ts +++ b/demo/src/FaberInquirer.ts @@ -3,7 +3,7 @@ import { textSync } from 'figlet' import { prompt } from 'inquirer' import { BaseInquirer, ConfirmOptions } from './BaseInquirer' -import { Faber } from './Faber' +import { Faber, RegistryOptions } from './Faber' import { Listener } from './Listener' import { Title } from './OutputClass' @@ -89,6 +89,8 @@ export class FaberInquirer extends BaseInquirer { } public async credential() { + const registry = await prompt([this.inquireOptions([RegistryOptions.indy, RegistryOptions.cheqd])]) + await this.faber.importDid(registry.options) await this.faber.issueCredential() const title = 'Is the credential offer accepted?' await this.listener.newAcceptedPrompt(title, this) diff --git a/packages/cheqd/README.md b/packages/cheqd/README.md new file mode 100644 index 0000000000..732f6e3fdd --- /dev/null +++ b/packages/cheqd/README.md @@ -0,0 +1,35 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript - Cheqd

+

+ License + typescript + @aries-framework/cheqd version + +

+
+ +### Installation + +### Quick start + +### Example of usage diff --git a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts index b7778ec41c..4dd7302da0 100644 --- a/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-resolver.e2e.test.ts @@ -2,23 +2,26 @@ import { Agent, JsonTransformer } from '@aries-framework/core' import { getAgentOptions } from '../../core/tests/helpers' import { getClosestResourceVersion } from '../src/dids/didCheqdUtil' +import { DefaultRPCUrl } from '../src/ledger/CheqdLedgerService' import { getCheqdModules } from './setupCheqdModule' -const agent = new Agent(getAgentOptions('Indy SDK Sov DID resolver', {}, getCheqdModules())) +export const resolverAgent = new Agent( + getAgentOptions('Cheqd resolver', {}, getCheqdModules(undefined, DefaultRPCUrl.Testnet)) +) describe('Cheqd DID resolver', () => { beforeAll(async () => { - await agent.initialize() + await resolverAgent.initialize() }) afterAll(async () => { - await agent.shutdown() - await agent.wallet.delete() + await resolverAgent.shutdown() + await resolverAgent.wallet.delete() }) it('should resolve a did:cheqd:testnet did', async () => { - const did = await agent.dids.resolve('did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8') + const did = await resolverAgent.dids.resolve('did:cheqd:testnet:3053e034-8faa-458d-9f01-2e3e1e8b2ab8') expect(JsonTransformer.toJSON(did)).toMatchObject({ didDocument: { '@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/ed25519-2020/v1'], @@ -46,7 +49,7 @@ describe('Cheqd DID resolver', () => { }) it('should getClosestResourceVersion', async () => { - const did = await agent.dids.resolve('did:cheqd:testnet:SiVQgrFZ7jFZFrTGstT4ZD') + const did = await resolverAgent.dids.resolve('did:cheqd:testnet:SiVQgrFZ7jFZFrTGstT4ZD') let resource = getClosestResourceVersion(did.didDocumentMetadata.linkedResourceMetadata, new Date()) expect(resource).toMatchObject({ id: '0b02ebf4-07c4-4df7-9015-e93c21108240', diff --git a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts index 1f872e344b..32d99d9918 100644 --- a/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-sdk-anoncreds-registry.e2e.test.ts @@ -5,6 +5,7 @@ import { Agent, JsonTransformer, TypedArrayEncoder } from '@aries-framework/core import { agentDependencies, getAgentConfig } from '../../core/tests/helpers' import { CheqdAnonCredsRegistry } from '../src/anoncreds' +import { resolverAgent } from './cheqd-did-resolver.e2e.test' import { getCheqdModules } from './setupCheqdModule' const agentConfig = getAgentConfig('cheqdAnonCredsRegistry') @@ -12,7 +13,9 @@ const agentConfig = getAgentConfig('cheqdAnonCredsRegistry') const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, - modules: getCheqdModules('000000000000000000000000000cheqd'), + modules: getCheqdModules( + 'ugly dirt sorry girl prepare argue door man that manual glow scout bomb pigeon matter library transfer flower clown cat miss pluck drama dizzy' + ), }) const cheqdAnonCredsRegistry = new CheqdAnonCredsRegistry() @@ -189,7 +192,7 @@ describe('cheqdAnonCredsRegistry', () => { test('resolve query based url', async () => { const schemaResourceId = 'did:cheqd:testnet:d8ac0372-0d4b-413e-8ef5-8e8f07822b2c?resourceName=test - 11&resourceType=anonCredsSchema' - const schemaResponse = await cheqdAnonCredsRegistry.getSchema(agent.context, `${schemaResourceId}`) + const schemaResponse = await cheqdAnonCredsRegistry.getSchema(resolverAgent.context, `${schemaResourceId}`) expect(schemaResponse).toMatchObject({ schema: { @@ -203,7 +206,7 @@ describe('cheqdAnonCredsRegistry', () => { test('resolve revocation registry definition and statusList', async () => { const revocationRegistryId = 'did:cheqd:testnet:e42ccb8b-78e8-4e54-9d11-f375153d63f8?resourceName=universityDegree' const revocationDefinitionResponse = await cheqdAnonCredsRegistry.getRevocationRegistryDefinition( - agent.context, + resolverAgent.context, revocationRegistryId ) @@ -224,7 +227,7 @@ describe('cheqdAnonCredsRegistry', () => { }) const revocationStatusListResponse = await cheqdAnonCredsRegistry.getRevocationStatusList( - agent.context, + resolverAgent.context, revocationRegistryId, 1680789403 ) diff --git a/packages/cheqd/tests/setupCheqdModule.ts b/packages/cheqd/tests/setupCheqdModule.ts index 0e095b3aa7..17881fdf26 100644 --- a/packages/cheqd/tests/setupCheqdModule.ts +++ b/packages/cheqd/tests/setupCheqdModule.ts @@ -11,10 +11,11 @@ export const getIndySdkModuleConfig = () => indySdk, }) -export const getCheqdModuleConfig = (seed?: string) => +export const getCheqdModuleConfig = (seed?: string, rpcUrl?: string) => ({ networks: [ { + rpcUrl: rpcUrl || 'http://localhost:26657', network: 'testnet', cosmosPayerSeed: seed || @@ -23,8 +24,8 @@ export const getCheqdModuleConfig = (seed?: string) => ], } satisfies CheqdModuleConfigOptions) -export const getCheqdModules = (seed?: string) => ({ - cheqdSdk: new CheqdModule(getCheqdModuleConfig(seed)), +export const getCheqdModules = (seed?: string, rpcUrl?: string) => ({ + cheqdSdk: new CheqdModule(getCheqdModuleConfig(seed, rpcUrl)), dids: new DidsModule({ registrars: [new CheqdDidRegistrar(), new KeyDidRegistrar()], resolvers: [new CheqdDidResolver(), new KeyDidResolver()], From d247b53d99629e4ba768a55b617a217936fcef89 Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Mon, 24 Apr 2023 14:03:15 +0200 Subject: [PATCH 130/139] feat(indy-vdr)!: did transaction endorsement (#1417) Signed-off-by: martin auer --- .github/actions/setup-indy-pool/action.yml | 9 +- .github/workflows/continuous-integration.yml | 7 +- DEVREADME.md | 12 +- packages/core/package.json | 4 +- packages/core/src/modules/dids/types.ts | 23 +- packages/indy-vdr/src/IndyVdrApi.ts | 79 + packages/indy-vdr/src/IndyVdrModule.ts | 5 + .../src/__tests__/IndyVdrModule.test.ts | 1 + .../src/anoncreds/IndyVdrAnonCredsRegistry.ts | 17 +- .../src/dids/IndyVdrIndyDidRegistrar.ts | 632 +++-- .../src/dids/IndyVdrIndyDidResolver.ts | 104 +- .../src/dids/IndyVdrSovDidResolver.ts | 4 +- .../__tests__/IndyVdrIndyDidRegistrar.test.ts | 219 +- .../__tests__/IndyVdrIndyDidResolver.test.ts | 16 +- .../__tests__/IndyVdrSovDidResolver.test.ts | 10 +- packages/indy-vdr/src/dids/didIndyUtil.ts | 98 +- packages/indy-vdr/src/dids/index.ts | 2 +- packages/indy-vdr/src/index.ts | 2 +- packages/indy-vdr/src/pool/IndyVdrPool.ts | 31 +- .../indy-vdr/src/pool/IndyVdrPoolService.ts | 2 +- packages/indy-vdr/tests/helpers.ts | 5 +- .../indy-vdr-anoncreds-registry.e2e.test.ts | 11 +- .../tests/indy-vdr-did-registrar.e2e.test.ts | 392 ++- .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 11 +- packages/node/package.json | 2 +- yarn.lock | 2130 ++++++++++------- 26 files changed, 2542 insertions(+), 1286 deletions(-) create mode 100644 packages/indy-vdr/src/IndyVdrApi.ts diff --git a/.github/actions/setup-indy-pool/action.yml b/.github/actions/setup-indy-pool/action.yml index 791548b809..23e1ebd8cf 100644 --- a/.github/actions/setup-indy-pool/action.yml +++ b/.github/actions/setup-indy-pool/action.yml @@ -6,6 +6,9 @@ inputs: seed: description: Seed to register on the ledger required: true + endorserSeed: + description: Endorser seed to register on the ledger + required: true runs: using: composite @@ -20,10 +23,14 @@ runs: run: docker exec indy-pool indy-cli-setup shell: bash - - name: Register DID on ledger + - name: Register Trustee DID on ledger run: docker exec indy-pool add-did-from-seed ${{ inputs.seed }} TRUSTEE shell: bash + - name: Register Endorser DID on ledger + run: docker exec indy-pool add-did-from-seed ${{ inputs.endorserSeed }} ENDORSER + shell: bash + branding: icon: scissors color: purple diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index b68c37f1cb..7ac7f26bb5 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -10,6 +10,7 @@ on: env: TEST_AGENT_PUBLIC_DID_SEED: 000000000000000000000000Trustee9 + ENDORSER_AGENT_PUBLIC_DID_SEED: 00000000000000000000000Endorser9 GENESIS_TXN_PATH: network/genesis/local-genesis.txn LIB_INDY_STRG_POSTGRES: /home/runner/work/aries-framework-javascript/indy-sdk/experimental/plugins/postgres_storage/target/release # for Linux NODE_OPTIONS: --max_old_space_size=4096 @@ -37,7 +38,7 @@ jobs: export SHOULD_RUN='true' elif [[ "${{ github.event.action }}" == "labeled" && "${{ github.event.label.name }}" != "ci-test" ]]; then export SHOULD_RUN='false' - else + else export SHOULD_RUN='true' fi @@ -96,10 +97,12 @@ jobs: - name: Setup Libindy uses: ./.github/actions/setup-libindy + - name: Setup Indy Pool uses: ./.github/actions/setup-indy-pool with: seed: ${TEST_AGENT_PUBLIC_DID_SEED} + endorserSeed: ${ENDORSER_AGENT_PUBLIC_DID_SEED} - name: Setup Cheqd uses: ./.github/actions/setup-cheqd @@ -123,7 +126,7 @@ jobs: run: yarn install - name: Run tests - run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage --forceExit --bail + run: TEST_AGENT_PUBLIC_DID_SEED=${TEST_AGENT_PUBLIC_DID_SEED} ENDORSER_AGENT_PUBLIC_DID_SEED=${ENDORSER_AGENT_PUBLIC_DID_SEED} GENESIS_TXN_PATH=${GENESIS_TXN_PATH} yarn test --coverage --forceExit --bail - uses: codecov/codecov-action@v1 if: always() diff --git a/DEVREADME.md b/DEVREADME.md index 98849e0997..e6bf748ab4 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -18,7 +18,7 @@ GENESIS_TXN_PATH=/work/network/genesis/local-genesis.txn ## Running tests -Test are executed using jest. Some test require either the **mediator agents** or the **ledger** to be running. When running tests that require a connection to the ledger pool, you need to set the `TEST_AGENT_PUBLIC_DID_SEED` and `GENESIS_TXN_PATH` environment variables. +Test are executed using jest. Some test require either the **mediator agents** or the **ledger** to be running. When running tests that require a connection to the ledger pool, you need to set the `TEST_AGENT_PUBLIC_DID_SEED`, `ENDORSER_AGENT_PUBLIC_DID_SEED` and `GENESIS_TXN_PATH` environment variables. ### Setting environment variables @@ -31,6 +31,9 @@ If you're using the setup as described in this document, you don't need to provi - `TEST_AGENT_PUBLIC_DID_SEED`: The seed to use for the public DID. This will be used to do public write operations to the ledger. You should use a seed for a DID that is already registered on the ledger. - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-ledger) in the previous step. (default is `000000000000000000000000Trustee9`) - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. +- `ENDORSER_AGENT_PUBLIC_DID_SEED`: The seed to use for the public Endorser DID. This will be used to endorse transactions. You should use a seed for a DID that is already registered on the ledger. + - If using the local or default genesis, use the same seed you used for the `add-did-from-seed` command from the [ledger setup](#setup-ledger) in the previous step. (default is `00000000000000000000000Endorser9`) + - If using the BuilderNet genesis, make sure your seed is registered on the BuilderNet using [selfserve.sovrin.org](https://selfserve.sovrin.org/) and you have read and accepted the associated [Transaction Author Agreement](https://github.com/sovrin-foundation/sovrin/blob/master/TAA/TAA.md). We are not responsible for any unwanted consequences of using the BuilderNet. ### Setup Postgres @@ -61,7 +64,10 @@ docker run -d --rm --name indy-pool -p 9701-9708:9701-9708 indy-pool # Setup CLI. This creates a wallet, connects to the ledger and sets the Transaction Author Agreement docker exec indy-pool indy-cli-setup -# DID and Verkey from seed. Set 'Trustee' role in order to be able to register public DIDs +# DID and Verkey from seed. Set 'ENDORSER' role in order to be able to register public DIDs +docker exec indy-pool add-did-from-seed 00000000000000000000000Endorser9 ENDORSER + +# DID and Verkey from seed. Set 'Trustee' docker exec indy-pool add-did-from-seed 000000000000000000000000Trustee9 TRUSTEE # If you want to register using the DID/Verkey you can use @@ -79,7 +85,7 @@ yarn test If you're not using the ledger setup from above, make sure you pass the correct environment variables from [Setting environment variables](#setting-environment-variables) for connecting to the indy **ledger** pool. ```sh -GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=000000000000000000000000Trustee9 yarn test +GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=000000000000000000000000Trustee9 ENDORSER_AGENT_PUBLIC_DID_SEED=00000000000000000000000Endorser9 yarn test ``` Locally, you might want to run the tests without postgres tests. You can do that by ignoring the tests: diff --git a/packages/core/package.json b/packages/core/package.json index 5e6ebb6d28..be6bdb7498 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,7 +30,7 @@ "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", "@stablelib/sha256": "^1.0.1", - "@types/node-fetch": "^2.5.10", + "node-fetch": "^2.6.1", "@types/ws": "^8.5.4", "abort-controller": "^3.0.0", "borc": "^3.0.0", @@ -58,7 +58,7 @@ "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", "nock": "^13.3.0", - "node-fetch": "^2.0", + "@types/node-fetch": "2.6.2", "rimraf": "^4.4.0", "tslog": "^4.8.2", "typescript": "~4.9.5" diff --git a/packages/core/src/modules/dids/types.ts b/packages/core/src/modules/dids/types.ts index 257260f2e8..359616a818 100644 --- a/packages/core/src/modules/dids/types.ts +++ b/packages/core/src/modules/dids/types.ts @@ -1,5 +1,5 @@ import type { DidDocument } from './domain' -import type { DIDResolutionOptions, ParsedDID, DIDDocumentMetadata, DIDResolutionMetadata } from 'did-resolver' +import type { DIDDocumentMetadata, DIDResolutionMetadata, DIDResolutionOptions, ParsedDID } from 'did-resolver' export type ParsedDid = ParsedDID export type DidResolutionOptions = DIDResolutionOptions @@ -36,13 +36,20 @@ export interface DidOperationStateFailed { reason: string } -export interface DidOperationState { - state: 'action' | 'wait' +export interface DidOperationStateWait { + state: 'wait' did?: string secret?: DidRegistrationSecretOptions didDocument?: DidDocument } +export interface DidOperationStateActionBase { + state: 'action' + action: string + did?: string + secret?: DidRegistrationSecretOptions + didDocument?: DidDocument +} export interface DidCreateOptions { method?: string did?: string @@ -51,9 +58,11 @@ export interface DidCreateOptions { didDocument?: DidDocument } -export interface DidCreateResult { +export interface DidCreateResult< + DidOperationStateAction extends DidOperationStateActionBase = DidOperationStateActionBase +> { jobId?: string - didState: DidOperationState | DidOperationStateFinished | DidOperationStateFailed + didState: DidOperationStateWait | DidOperationStateAction | DidOperationStateFinished | DidOperationStateFailed didRegistrationMetadata: DidRegistrationMetadata didDocumentMetadata: DidResolutionMetadata } @@ -68,7 +77,7 @@ export interface DidUpdateOptions { export interface DidUpdateResult { jobId?: string - didState: DidOperationState | DidOperationStateFinished | DidOperationStateFailed + didState: DidOperationStateWait | DidOperationStateActionBase | DidOperationStateFinished | DidOperationStateFailed didRegistrationMetadata: DidRegistrationMetadata didDocumentMetadata: DidResolutionMetadata } @@ -81,7 +90,7 @@ export interface DidDeactivateOptions { export interface DidDeactivateResult { jobId?: string - didState: DidOperationState | DidOperationStateFinished | DidOperationStateFailed + didState: DidOperationStateWait | DidOperationStateActionBase | DidOperationStateFinished | DidOperationStateFailed didRegistrationMetadata: DidRegistrationMetadata didDocumentMetadata: DidResolutionMetadata } diff --git a/packages/indy-vdr/src/IndyVdrApi.ts b/packages/indy-vdr/src/IndyVdrApi.ts new file mode 100644 index 0000000000..4cd0ff3154 --- /dev/null +++ b/packages/indy-vdr/src/IndyVdrApi.ts @@ -0,0 +1,79 @@ +import type { Key } from '@aries-framework/core' +import type { IndyVdrRequest } from '@hyperledger/indy-vdr-shared' + +import { parseIndyDid } from '@aries-framework/anoncreds' +import { AgentContext, injectable, TypedArrayEncoder } from '@aries-framework/core' +import { CustomRequest } from '@hyperledger/indy-vdr-shared' + +import { verificationKeyForIndyDid } from './dids/didIndyUtil' +import { IndyVdrError } from './error' +import { IndyVdrPoolService } from './pool' + +@injectable() +export class IndyVdrApi { + private agentContext: AgentContext + private indyVdrPoolService: IndyVdrPoolService + + public constructor(agentContext: AgentContext, indyVdrPoolService: IndyVdrPoolService) { + this.agentContext = agentContext + this.indyVdrPoolService = indyVdrPoolService + } + + private async multiSignRequest( + request: Request, + signingKey: Key, + identifier: string + ) { + const signature = await this.agentContext.wallet.sign({ + data: TypedArrayEncoder.fromString(request.signatureInput), + key: signingKey, + }) + + request.setMultiSignature({ + signature, + identifier, + }) + + return request + } + + private async signRequest(request: Request, submitterDid: string) { + const signingKey = await verificationKeyForIndyDid(this.agentContext, submitterDid) + + const { pool } = await this.indyVdrPoolService.getPoolForDid(this.agentContext, submitterDid) + const signedRequest = await pool.prepareWriteRequest(this.agentContext, request, signingKey) + + return signedRequest + } + + /** + * This method endorses a transaction. The transaction can be either a string or a JSON object. + * If the transaction has a signature, it means the transaction was created by another author and will be endorsed. + * This requires the `endorser` on the transaction to be equal to the unqualified variant of the `endorserDid`. + * + * If the transaction is not signed, we have a special case where the endorser will author the transaction. + * This is required when a new did is created, as the author and the endorser did must already exist on the ledger. + * In this case, the author did (`identifier`) must be equal to the unqualified identifier of the `endorserDid`. + * @param transaction the transaction body to be endorsed + * @param endorserDid the did of the endorser + * @returns An endorsed transaction + */ + public async endorseTransaction(transaction: string | Record, endorserDid: string) { + const endorserSigningKey = await verificationKeyForIndyDid(this.agentContext, endorserDid) + const { namespaceIdentifier } = parseIndyDid(endorserDid) + + const request = new CustomRequest({ customRequest: transaction }) + let endorsedTransaction: CustomRequest + + // the request is not parsed correctly due to too large numbers. The reqId overflows. + const txBody = typeof transaction === 'string' ? JSON.parse(transaction) : transaction + if (txBody.signature) { + if (txBody.endorser !== namespaceIdentifier) throw new IndyVdrError('Submitter does not match Endorser') + endorsedTransaction = await this.multiSignRequest(request, endorserSigningKey, namespaceIdentifier) + } else { + if (txBody.identifier !== namespaceIdentifier) throw new IndyVdrError('Submitter does not match identifier') + endorsedTransaction = await this.signRequest(request, endorserDid) + } + return endorsedTransaction.body + } +} diff --git a/packages/indy-vdr/src/IndyVdrModule.ts b/packages/indy-vdr/src/IndyVdrModule.ts index d03db3c08d..ee337fdcaf 100644 --- a/packages/indy-vdr/src/IndyVdrModule.ts +++ b/packages/indy-vdr/src/IndyVdrModule.ts @@ -1,6 +1,7 @@ import type { IndyVdrModuleConfigOptions } from './IndyVdrModuleConfig' import type { AgentContext, DependencyManager, Module } from '@aries-framework/core' +import { IndyVdrApi } from './IndyVdrApi' import { IndyVdrModuleConfig } from './IndyVdrModuleConfig' import { IndyVdrPoolService } from './pool/IndyVdrPoolService' @@ -9,6 +10,7 @@ import { IndyVdrPoolService } from './pool/IndyVdrPoolService' * */ export class IndyVdrModule implements Module { public readonly config: IndyVdrModuleConfig + public readonly api = IndyVdrApi public constructor(config: IndyVdrModuleConfigOptions) { this.config = new IndyVdrModuleConfig(config) @@ -20,6 +22,9 @@ export class IndyVdrModule implements Module { // Services dependencyManager.registerSingleton(IndyVdrPoolService) + + // Api + dependencyManager.registerContextScoped(IndyVdrApi) } public async initialize(agentContext: AgentContext): Promise { diff --git a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts index 1b9b55c8e4..ecc5c5d14f 100644 --- a/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts +++ b/packages/indy-vdr/src/__tests__/IndyVdrModule.test.ts @@ -9,6 +9,7 @@ import { IndyVdrPoolService } from '../pool' const dependencyManager = { registerInstance: jest.fn(), registerSingleton: jest.fn(), + registerContextScoped: jest.fn(), } as unknown as DependencyManager describe('IndyVdrModule', () => { diff --git a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts index 01cdd18449..412d7f91c0 100644 --- a/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts +++ b/packages/indy-vdr/src/anoncreds/IndyVdrAnonCredsRegistry.ts @@ -62,7 +62,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { agentContext.config.logger.trace( `Submitting get schema request for schema '${schemaId}' to ledger '${pool.indyNamespace}'` ) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) agentContext.config.logger.trace(`Got un-parsed schema '${schemaId}' from ledger '${pool.indyNamespace}'`, { response, @@ -150,7 +150,9 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { }) const submitterKey = await verificationKeyForIndyDid(agentContext, options.schema.issuerId) - const response = await pool.submitWriteRequest(agentContext, schemaRequest, submitterKey) + const writeRequest = await pool.prepareWriteRequest(agentContext, schemaRequest, submitterKey) + const response = await pool.submitRequest(writeRequest) + agentContext.config.logger.debug(`Registered schema '${didIndySchemaId}' on ledger '${pool.indyNamespace}'`, { response, schemaRequest, @@ -216,7 +218,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { agentContext.config.logger.trace( `Submitting get credential definition request for credential definition '${credentialDefinitionId}' to ledger '${pool.indyNamespace}'` ) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) // We need to fetch the schema to determine the schemaId (we only have the seqNo) const schema = await this.fetchIndySchemaWithSeqNo(agentContext, response.result.ref, namespaceIdentifier) @@ -332,7 +334,8 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { }) const submitterKey = await verificationKeyForIndyDid(agentContext, options.credentialDefinition.issuerId) - const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, submitterKey) + const writeRequest = await pool.prepareWriteRequest(agentContext, credentialDefinitionRequest, submitterKey) + const response = await pool.submitRequest(writeRequest) agentContext.config.logger.debug( `Registered credential definition '${didIndyCredentialDefinitionId}' on ledger '${pool.indyNamespace}'`, { @@ -400,7 +403,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { agentContext.config.logger.trace( `Submitting get revocation registry definition request for revocation registry definition '${revocationRegistryDefinitionId}' to ledger` ) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) if (!response.result.data) { agentContext.config.logger.error( @@ -512,7 +515,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { agentContext.config.logger.trace( `Submitting get revocation registry delta request for revocation registry '${revocationRegistryId}' to ledger` ) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) agentContext.config.logger.debug( `Got revocation registry deltas '${revocationRegistryId}' until timestamp ${timestamp} from ledger` @@ -596,7 +599,7 @@ export class IndyVdrAnonCredsRegistry implements AnonCredsRegistry { const request = new GetTransactionRequest({ ledgerType: 1, seqNo }) agentContext.config.logger.trace(`Submitting get transaction request to ledger '${pool.indyNamespace}'`) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) if (response.result.data?.txn.type !== '101') { agentContext.config.logger.error(`Could not get schema from ledger for seq no ${seqNo}'`) diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index 33a0e088a9..7f998e247d 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -3,33 +3,37 @@ import type { IndyVdrPool } from '../pool' import type { AgentContext, Buffer, - DidRegistrar, DidCreateOptions, DidCreateResult, DidDeactivateResult, - DidUpdateResult, + DidDocument, DidDocumentService, + DidOperationStateActionBase, + DidRegistrar, + DidUpdateResult, } from '@aries-framework/core' +import type { IndyVdrRequest } from '@hyperledger/indy-vdr-shared' import { parseIndyDid } from '@aries-framework/anoncreds' import { - IndyAgentService, DidCommV1Service, DidCommV2Service, - Hasher, - TypedArrayEncoder, - Key, - KeyType, DidDocumentRole, DidRecord, DidRepository, + Hasher, + IndyAgentService, + Key, + KeyType, + TypedArrayEncoder, } from '@aries-framework/core' -import { AttribRequest, NymRequest } from '@hyperledger/indy-vdr-shared' +import { AttribRequest, CustomRequest, NymRequest } from '@hyperledger/indy-vdr-shared' import { IndyVdrError } from '../error' import { IndyVdrPoolService } from '../pool/IndyVdrPoolService' import { + buildDidDocument, createKeyAgreementKey, didDocDiff, indyDidDocumentFromDid, @@ -41,205 +45,315 @@ import { endpointsAttribFromServices } from './didSovUtil' export class IndyVdrIndyDidRegistrar implements DidRegistrar { public readonly supportedMethods = ['indy'] - public async create(agentContext: AgentContext, options: IndyVdrDidCreateOptions): Promise { - const seed = options.secret?.seed - const privateKey = options.secret?.privateKey + private didCreateActionResult({ + namespace, + didAction, + did, + }: { + namespace: string + didAction: EndorseDidTxAction + did: string + }): IndyVdrDidCreateResult { + return { + jobId: did, + didDocumentMetadata: {}, + didRegistrationMetadata: { + didIndyNamespace: namespace, + }, + didState: didAction, + } + } + + private didCreateFailedResult({ reason }: { reason: string }): IndyVdrDidCreateResult { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: reason, + }, + } + } + + private didCreateFinishedResult({ + seed, + privateKey, + did, + didDocument, + namespace, + }: { + seed: Buffer | undefined + privateKey: Buffer | undefined + did: string + didDocument: DidDocument + namespace: string + }): IndyVdrDidCreateResult { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: { + didIndyNamespace: namespace, + }, + didState: { + state: 'finished', + did, + didDocument, + secret: { + // FIXME: the uni-registrar creates the seed in the registrar method + // if it doesn't exist so the seed can always be returned. Currently + // we can only return it if the seed was passed in by the user. Once + // we have a secure method for generating seeds we should use the same + // approach + seed: seed, + privateKey: privateKey, + }, + }, + } + } - const { alias, role, submitterDid, services, useEndpointAttrib } = options.options + public async parseInput(agentContext: AgentContext, options: IndyVdrDidCreateOptions): Promise { let did = options.did let namespaceIdentifier: string let verificationKey: Key + const seed = options.secret?.seed + const privateKey = options.secret?.privateKey + + if (options.options.endorsedTransaction) { + const _did = did as string + const { namespace } = parseIndyDid(_did) + // endorser did from the transaction + const endorserNamespaceIdentifier = JSON.parse(options.options.endorsedTransaction.nymRequest).identifier + + return { + status: 'ok', + did: _did, + namespace: namespace, + namespaceIdentifier: parseIndyDid(_did).namespaceIdentifier, + endorserNamespaceIdentifier, + seed, + privateKey, + } + } + + const endorserDid = options.options.endorserDid + const { namespace: endorserNamespace, namespaceIdentifier: endorserNamespaceIdentifier } = parseIndyDid(endorserDid) const allowOne = [privateKey, seed, did].filter((e) => e !== undefined) if (allowOne.length > 1) { return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `Only one of 'seed', 'privateKey' and 'did' must be provided`, - }, + status: 'error', + reason: `Only one of 'seed', 'privateKey' and 'did' must be provided`, } } - try { - // Parse submitterDid and extract namespace based on the submitter did - const { namespace: submitterNamespace, namespaceIdentifier: submitterNamespaceIdentifier } = - parseIndyDid(submitterDid) - const submitterSigningKey = await verificationKeyForIndyDid(agentContext, submitterDid) - - if (did) { - if (!options.options.verkey) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'If a did is defined, a matching verkey must be provided', - }, - } + if (did) { + if (!options.options.verkey) { + return { + status: 'error', + reason: 'If a did is defined, a matching verkey must be provided', } + } - const { namespace, namespaceIdentifier: _namespaceIdentifier } = parseIndyDid(did) - namespaceIdentifier = _namespaceIdentifier - verificationKey = Key.fromPublicKeyBase58(options.options.verkey, KeyType.Ed25519) - - if (!isSelfCertifiedIndyDid(did, options.options.verkey)) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `Initial verkey ${options.options.verkey} does not match did ${did}`, - }, - } - } + const { namespace: didNamespace, namespaceIdentifier: didNamespaceIdentifier } = parseIndyDid(did) + namespaceIdentifier = didNamespaceIdentifier + verificationKey = Key.fromPublicKeyBase58(options.options.verkey, KeyType.Ed25519) - if (submitterNamespace !== namespace) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `The submitter did uses namespace ${submitterNamespace} and the did to register uses namespace ${namespace}. Namespaces must match.`, - }, - } + if (!isSelfCertifiedIndyDid(did, options.options.verkey)) { + return { + status: 'error', + reason: `Initial verkey ${options.options.verkey} does not match did ${did}`, } - } else { - // Create a new key and calculate did according to the rules for indy did method - verificationKey = await agentContext.wallet.createKey({ privateKey, seed, keyType: KeyType.Ed25519 }) - const buffer = Hasher.hash(verificationKey.publicKey, 'sha2-256') - - namespaceIdentifier = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) - did = `did:indy:${submitterNamespace}:${namespaceIdentifier}` } - // Create base did document - const didDocumentBuilder = indyDidDocumentFromDid(did, verificationKey.publicKeyBase58) - let diddocContent + if (didNamespace !== endorserNamespace) { + return { + status: 'error', + reason: `The endorser did uses namespace: '${endorserNamespace}' and the did to register uses namespace: '${didNamespace}'. Namespaces must match.`, + } + } + } else { + // Create a new key and calculate did according to the rules for indy did method + verificationKey = await agentContext.wallet.createKey({ privateKey, seed, keyType: KeyType.Ed25519 }) + const buffer = Hasher.hash(verificationKey.publicKey, 'sha2-256') - // Add services if object was passed - if (services) { - services.forEach((item) => { - const prependDidIfNotPresent = (id: string) => { - return id.startsWith('#') ? `${did}${id}` : id - } + namespaceIdentifier = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) + did = `did:indy:${endorserNamespace}:${namespaceIdentifier}` + } - // Prepend the did to the service id if it is not already there - item.id = prependDidIfNotPresent(item.id) + return { + status: 'ok', + did, + verificationKey, + namespaceIdentifier, + namespace: endorserNamespace, + endorserNamespaceIdentifier, + seed, + privateKey, + } + } - // TODO: should we also prepend the did to routingKeys? - if (item instanceof DidCommV1Service) { - item.recipientKeys = item.recipientKeys.map(prependDidIfNotPresent) - } + public async saveDidRecord(agentContext: AgentContext, did: string, didDocument: DidDocument): Promise { + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + did, + role: DidDocumentRole.Created, + tags: { + recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), + }, + }) - didDocumentBuilder.addService(item) - }) + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + await didRepository.save(agentContext, didRecord) + } - const commTypes = [IndyAgentService.type, DidCommV1Service.type, DidCommV2Service.type] - const serviceTypes = new Set(services.map((item) => item.type)) - - const keyAgreementId = `${did}#key-agreement-1` - - // If there is at least a communication service, add the key agreement key - if (commTypes.some((type) => serviceTypes.has(type))) { - didDocumentBuilder - .addContext('https://w3id.org/security/suites/x25519-2019/v1') - .addVerificationMethod({ - controller: did, - id: keyAgreementId, - publicKeyBase58: createKeyAgreementKey(verificationKey.publicKeyBase58), - type: 'X25519KeyAgreementKey2019', - }) - .addKeyAgreement(keyAgreementId) + private createDidDocument( + did: string, + verificationKey: Key, + services: DidDocumentService[] | undefined, + useEndpointAttrib: boolean | undefined + ) { + // Create base did document + const didDocumentBuilder = indyDidDocumentFromDid(did, verificationKey.publicKeyBase58) + let diddocContent + + // Add services if object was passed + if (services) { + services.forEach((item) => { + const prependDidIfNotPresent = (id: string) => { + return id.startsWith('#') ? `${did}${id}` : id } - // If there is a DIDComm V2 service, add context - if (serviceTypes.has(DidCommV2Service.type)) { - didDocumentBuilder.addContext('https://didcomm.org/messaging/contexts/v2') - } + // Prepend the did to the service id if it is not already there + item.id = prependDidIfNotPresent(item.id) - if (!useEndpointAttrib) { - // create diddocContent parameter based on the diff between the base and the resulting DID Document - diddocContent = didDocDiff( - didDocumentBuilder.build().toJSON(), - indyDidDocumentFromDid(did, verificationKey.publicKeyBase58).build().toJSON() - ) + // TODO: should we also prepend the did to routingKeys? + if (item instanceof DidCommV1Service) { + item.recipientKeys = item.recipientKeys.map(prependDidIfNotPresent) } + + didDocumentBuilder.addService(item) + }) + + const commTypes = [IndyAgentService.type, DidCommV1Service.type, DidCommV2Service.type] + const serviceTypes = new Set(services.map((item) => item.type)) + + const keyAgreementId = `${did}#key-agreement-1` + + // If there is at least a communication service, add the key agreement key + if (commTypes.some((type) => serviceTypes.has(type))) { + didDocumentBuilder + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(verificationKey.publicKeyBase58), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) } - // Build did document - const didDocument = didDocumentBuilder.build() - const pool = agentContext.dependencyManager.resolve(IndyVdrPoolService).getPoolForNamespace(submitterNamespace) - // If there are services and we are using legacy indy endpoint attrib, make sure they are suitable before registering the DID - if (services && useEndpointAttrib) { - const endpoints = endpointsAttribFromServices(services) - await this.registerPublicDid( - agentContext, - pool, - submitterNamespaceIdentifier, - submitterSigningKey, - namespaceIdentifier, - verificationKey, - alias, - role + // If there is a DIDComm V2 service, add context + if (serviceTypes.has(DidCommV2Service.type)) { + didDocumentBuilder.addContext('https://didcomm.org/messaging/contexts/v2') + } + + if (!useEndpointAttrib) { + // create diddocContent parameter based on the diff between the base and the resulting DID Document + diddocContent = didDocDiff( + didDocumentBuilder.build().toJSON(), + indyDidDocumentFromDid(did, verificationKey.publicKeyBase58).build().toJSON() ) - await this.setEndpointsForDid(agentContext, pool, namespaceIdentifier, verificationKey, endpoints) + } + } + + // Build did document + const didDocument = didDocumentBuilder.build() + + return { + diddocContent, + didDocument, + } + } + + public async create(agentContext: AgentContext, options: IndyVdrDidCreateOptions): Promise { + try { + const res = await this.parseInput(agentContext, options) + if (res.status === 'error') return this.didCreateFailedResult({ reason: res.reason }) + + const { did, namespaceIdentifier, endorserNamespaceIdentifier, verificationKey, namespace, seed, privateKey } = + res + + const pool = agentContext.dependencyManager.resolve(IndyVdrPoolService).getPoolForNamespace(namespace) + + let nymRequest: NymRequest | CustomRequest + let didDocument: DidDocument | undefined + let attribRequest: AttribRequest | CustomRequest | undefined + let alias: string | undefined + + if (options.options.endorsedTransaction) { + const { nymRequest: _nymRequest, attribRequest: _attribRequest } = options.options.endorsedTransaction + nymRequest = new CustomRequest({ customRequest: _nymRequest }) + attribRequest = _attribRequest ? new CustomRequest({ customRequest: _attribRequest }) : undefined } else { - await this.registerPublicDid( + const { services, useEndpointAttrib } = options.options + alias = options.options.alias + if (!verificationKey) throw new Error('VerificationKey not defined') + + const { didDocument: _didDocument, diddocContent } = this.createDidDocument( + did, + verificationKey, + services, + useEndpointAttrib + ) + didDocument = _didDocument + + let didRegisterSigningKey: Key | undefined = undefined + if (options.options.endorserMode === 'internal') + didRegisterSigningKey = await verificationKeyForIndyDid(agentContext, options.options.endorserDid) + + nymRequest = await this.createRegisterDidWriteRequest({ agentContext, pool, - submitterNamespaceIdentifier, - submitterSigningKey, + signingKey: didRegisterSigningKey, + submitterNamespaceIdentifier: endorserNamespaceIdentifier, namespaceIdentifier, verificationKey, alias, - role, - diddocContent - ) - } + diddocContent, + }) - // Save the did so we know we created it and can issue with it - const didRecord = new DidRecord({ - did, - role: DidDocumentRole.Created, - tags: { - recipientKeyFingerprints: didDocument.recipientKeys.map((key: Key) => key.fingerprint), - }, - }) + if (services && useEndpointAttrib) { + const endpoints = endpointsAttribFromServices(services) + attribRequest = await this.createSetDidEndpointsRequest({ + agentContext, + pool, + signingKey: verificationKey, + endorserDid: options.options.endorserMode === 'external' ? options.options.endorserDid : undefined, + unqualifiedDid: namespaceIdentifier, + endpoints, + }) + } - const didRepository = agentContext.dependencyManager.resolve(DidRepository) - await didRepository.save(agentContext, didRecord) + if (options.options.endorserMode === 'external') { + const didAction: EndorseDidTxAction = { + state: 'action', + action: 'endorseIndyTransaction', + endorserDid: options.options.endorserDid, + nymRequest: nymRequest.body, + attribRequest: attribRequest?.body, + did: did, + secret: { seed, privateKey }, + } - return { - didDocumentMetadata: {}, - didRegistrationMetadata: { - didIndyNamespace: submitterNamespace, - }, - didState: { - state: 'finished', - did, - didDocument, - secret: { - // FIXME: the uni-registrar creates the seed in the registrar method - // if it doesn't exist so the seed can always be returned. Currently - // we can only return it if the seed was passed in by the user. Once - // we have a secure method for generating seeds we should use the same - // approach - seed: options.secret?.seed, - privateKey: options.secret?.privateKey, - }, - }, + return this.didCreateActionResult({ namespace, didAction, did }) + } } + await this.registerPublicDid(agentContext, pool, nymRequest) + if (attribRequest) await this.setEndpointsForDid(agentContext, pool, attribRequest) + didDocument = didDocument ?? (await buildDidDocument(agentContext, pool, did)) + await this.saveDidRecord(agentContext, did, didDocument) + return this.didCreateFinishedResult({ did, didDocument, namespace, seed, privateKey }) } catch (error) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `unknownError: ${error.message}`, - }, - } + return this.didCreateFailedResult({ reason: `unknownError: ${error.message}` }) } } @@ -265,92 +379,103 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { } } - private async registerPublicDid( - agentContext: AgentContext, - pool: IndyVdrPool, - unqualifiedSubmitterDid: string, - submitterSigningKey: Key, - unqualifiedDid: string, - signingKey: Key, - alias?: string, - role?: string, + private async createRegisterDidWriteRequest(options: { + agentContext: AgentContext + pool: IndyVdrPool + submitterNamespaceIdentifier: string + namespaceIdentifier: string + verificationKey: Key + signingKey?: Key + alias: string | undefined diddocContent?: Record - ) { - try { - agentContext.config.logger.debug(`Register public did '${unqualifiedDid}' on ledger '${pool}'`) + }) { + const { + agentContext, + pool, + submitterNamespaceIdentifier, + namespaceIdentifier, + verificationKey, + alias, + signingKey, + } = options + + // FIXME: Add diddocContent when supported by indy-vdr + if (options.diddocContent) { + throw new IndyVdrError('diddocContent is not yet supported') + } - // FIXME: Add diddocContent when supported by indy-vdr - if (diddocContent) { - throw new IndyVdrError('diddocContent is not yet supported') - } + const request = new NymRequest({ + submitterDid: submitterNamespaceIdentifier, + dest: namespaceIdentifier, + verkey: verificationKey.publicKeyBase58, + alias: alias, + }) - const request = new NymRequest({ - submitterDid: unqualifiedSubmitterDid, - dest: unqualifiedDid, - verkey: signingKey.publicKeyBase58, - alias, - }) + if (!signingKey) return request + const writeRequest = await pool.prepareWriteRequest(agentContext, request, signingKey, undefined) + return writeRequest + } - const response = await pool.submitWriteRequest(agentContext, request, submitterSigningKey) + private async registerPublicDid( + agentContext: AgentContext, + pool: IndyVdrPool, + writeRequest: Request + ) { + const body = writeRequest.body + try { + const response = await pool.submitRequest(writeRequest) - agentContext.config.logger.debug(`Registered public did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, { + agentContext.config.logger.debug(`Register public did on ledger '${pool.indyNamespace}'\nRequest: ${body}}`, { response, }) return } catch (error) { agentContext.config.logger.error( - `Error registering public did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, - { - error, - unqualifiedSubmitterDid, - unqualifiedDid, - signingKey, - alias, - role, - pool: pool.indyNamespace, - } + `Error Registering public did on ledger '${pool.indyNamespace}'\nRequest: ${body}}` ) throw error } } - private async setEndpointsForDid( + private async createSetDidEndpointsRequest(options: { + agentContext: AgentContext + pool: IndyVdrPool + signingKey: Key + endorserDid?: string + unqualifiedDid: string + endpoints: IndyEndpointAttrib + }): Promise { + const { agentContext, pool, endpoints, unqualifiedDid, signingKey, endorserDid } = options + const request = new AttribRequest({ + submitterDid: unqualifiedDid, + targetDid: unqualifiedDid, + raw: JSON.stringify({ endpoint: endpoints }), + }) + + const writeRequest = await pool.prepareWriteRequest(agentContext, request, signingKey, endorserDid) + return writeRequest + } + + private async setEndpointsForDid( agentContext: AgentContext, pool: IndyVdrPool, - unqualifiedDid: string, - signingKey: Key, - endpoints: IndyEndpointAttrib + writeRequest: Request ): Promise { + const body = writeRequest.body try { - agentContext.config.logger.debug( - `Set endpoints for did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, - endpoints - ) - - const request = new AttribRequest({ - submitterDid: unqualifiedDid, - targetDid: unqualifiedDid, - raw: JSON.stringify({ endpoint: endpoints }), - }) + const response = await pool.submitRequest(writeRequest) - const response = await pool.submitWriteRequest(agentContext, request, signingKey) agentContext.config.logger.debug( - `Successfully set endpoints for did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, + `Successfully set endpoints for did on ledger '${pool.indyNamespace}'.\nRequest: ${body}}`, { response, - endpoints, } ) } catch (error) { agentContext.config.logger.error( - `Error setting endpoints for did '${unqualifiedDid}' on ledger '${pool.indyNamespace}'`, - { - error, - unqualifiedDid, - endpoints, - } + `Error setting endpoints for did on ledger '${pool.indyNamespace}'.\nRequest: ${body}}` ) throw new IndyVdrError(error) @@ -367,7 +492,12 @@ interface IndyVdrDidCreateOptionsBase extends DidCreateOptions { useEndpointAttrib?: boolean verkey?: string - submitterDid: string + // endorserDid is always required. We just have internal or external mode + endorserDid: string + // if endorserMode is 'internal', the endorserDid MUST be present in the wallet + // if endorserMode is 'external', the endorserDid doesn't have to be present in the wallet + endorserMode: 'internal' | 'external' + endorsedTransaction?: never } secret?: { seed?: Buffer @@ -385,4 +515,54 @@ interface IndyVdrDidCreateOptionsWithoutDid extends IndyVdrDidCreateOptionsBase did?: never } -export type IndyVdrDidCreateOptions = IndyVdrDidCreateOptionsWithDid | IndyVdrDidCreateOptionsWithoutDid +// When transactions have been endorsed. Only supported for external mode +// this is a separate interface so we can remove all the properties we don't need anymore. +interface IndyVdrDidCreateOptionsForSubmission extends DidCreateOptions { + didDocument?: never + did: string // for submission MUST always have a did, so we know which did we're submitting the transaction for. We MUST check whether the did passed here, matches with the + method?: never + options: { + endorserMode: 'external' + + // provide the endorsed transactions. If these are provided + // we will submit the transactions to the ledger + endorsedTransaction: { + nymRequest: string + attribRequest?: string + } + } + secret?: { + seed?: Buffer + privateKey?: Buffer + } +} + +export type IndyVdrDidCreateOptions = + | IndyVdrDidCreateOptionsWithDid + | IndyVdrDidCreateOptionsWithoutDid + | IndyVdrDidCreateOptionsForSubmission + +type ParseInputOk = { + status: 'ok' + did: string + verificationKey?: Key + namespaceIdentifier: string + namespace: string + endorserNamespaceIdentifier: string + seed: Buffer | undefined + privateKey: Buffer | undefined +} + +type parseInputError = { status: 'error'; reason: string } + +type ParseInputResult = ParseInputOk | parseInputError + +export interface EndorseDidTxAction extends DidOperationStateActionBase { + action: 'endorseIndyTransaction' + endorserDid: string + nymRequest: string + attribRequest?: string + did: string +} + +export type IndyVdrDidCreateResult = DidCreateResult diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts index ddc339c745..89271f5fdd 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidResolver.ts @@ -1,15 +1,10 @@ -import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' -import type { IndyVdrPool } from '../pool' -import type { DidResolutionResult, DidResolver, AgentContext } from '@aries-framework/core' +import type { AgentContext, DidResolutionResult, DidResolver } from '@aries-framework/core' import { parseIndyDid } from '@aries-framework/anoncreds' -import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' -import { IndyVdrError, IndyVdrNotFoundError } from '../error' import { IndyVdrPoolService } from '../pool' -import { combineDidDocumentWithJson, createKeyAgreementKey, indyDidDocumentFromDid } from './didIndyUtil' -import { getFullVerkey, addServicesFromEndpointsAttrib } from './didSovUtil' +import { buildDidDocument } from './didIndyUtil' export class IndyVdrIndyDidResolver implements DidResolver { public readonly supportedMethods = ['indy'] @@ -17,8 +12,11 @@ export class IndyVdrIndyDidResolver implements DidResolver { public async resolve(agentContext: AgentContext, did: string): Promise { const didDocumentMetadata = {} try { + const poolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) + const pool = poolService.getPoolForNamespace(parseIndyDid(did).namespace) + // Get DID Document from Get NYM response - const didDocument = await this.buildDidDocument(agentContext, did) + const didDocument = await buildDidDocument(agentContext, pool, did) return { didDocument, @@ -36,94 +34,4 @@ export class IndyVdrIndyDidResolver implements DidResolver { } } } - - private async buildDidDocument(agentContext: AgentContext, did: string) { - const { namespaceIdentifier, namespace } = parseIndyDid(did) - - const poolService = agentContext.dependencyManager.resolve(IndyVdrPoolService) - const pool = poolService.getPoolForNamespace(namespace) - - const nym = await this.getPublicDid(pool, namespaceIdentifier) - - // Create base Did Document - - // For modern did:indy DIDs, we assume that GET_NYM is always a full verkey in base58. - // For backwards compatibility, we accept a shortened verkey and convert it using previous convention - const verkey = getFullVerkey(namespaceIdentifier, nym.verkey) - - const builder = indyDidDocumentFromDid(did, verkey) - - // If GET_NYM does not return any diddocContent, fallback to legacy GET_ATTRIB endpoint - if (!nym.diddocContent) { - const keyAgreementId = `${did}#key-agreement-1` - const endpoints = await this.getEndpointsForDid(agentContext, pool, namespaceIdentifier) - - if (endpoints) { - builder - .addContext('https://w3id.org/security/suites/x25519-2019/v1') - .addVerificationMethod({ - controller: did, - id: keyAgreementId, - publicKeyBase58: createKeyAgreementKey(verkey), - type: 'X25519KeyAgreementKey2019', - }) - .addKeyAgreement(keyAgreementId) - - // Process endpoint attrib following the same rules as for did:sov - addServicesFromEndpointsAttrib(builder, did, endpoints, keyAgreementId) - } - return builder.build() - } else { - // Combine it with didDoc (TODO: Check if diddocContent is returned as a JSON object or a string) - return combineDidDocumentWithJson(builder.build(), nym.diddocContent) - } - } - - private async getPublicDid(pool: IndyVdrPool, unqualifiedDid: string) { - const request = new GetNymRequest({ dest: unqualifiedDid }) - - const didResponse = await pool.submitReadRequest(request) - - if (!didResponse.result.data) { - throw new IndyVdrNotFoundError(`DID ${unqualifiedDid} not found in indy namespace ${pool.indyNamespace}`) - } - return JSON.parse(didResponse.result.data) as GetNymResponseData - } - - private async getEndpointsForDid(agentContext: AgentContext, pool: IndyVdrPool, unqualifiedDid: string) { - try { - agentContext.config.logger.debug(`Get endpoints for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`) - - const request = new GetAttribRequest({ targetDid: unqualifiedDid, raw: 'endpoint' }) - - agentContext.config.logger.debug( - `Submitting get endpoint ATTRIB request for did '${unqualifiedDid}' to ledger '${pool.indyNamespace}'` - ) - const response = await pool.submitReadRequest(request) - - if (!response.result.data) { - return null - } - - const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib - agentContext.config.logger.debug( - `Got endpoints '${JSON.stringify(endpoints)}' for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`, - { - response, - endpoints, - } - ) - - return endpoints - } catch (error) { - agentContext.config.logger.error( - `Error retrieving endpoints for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`, - { - error, - } - ) - - throw new IndyVdrError(error) - } - } } diff --git a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts index dd4ecab222..4484586078 100644 --- a/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts +++ b/packages/indy-vdr/src/dids/IndyVdrSovDidResolver.ts @@ -51,7 +51,7 @@ export class IndyVdrSovDidResolver implements DidResolver { private async getPublicDid(pool: IndyVdrPool, unqualifiedDid: string) { const request = new GetNymRequest({ dest: unqualifiedDid }) - const didResponse = await pool.submitReadRequest(request) + const didResponse = await pool.submitRequest(request) if (!didResponse.result.data) { throw new IndyVdrNotFoundError(`DID ${unqualifiedDid} not found`) @@ -68,7 +68,7 @@ export class IndyVdrSovDidResolver implements DidResolver { agentContext.config.logger.debug( `Submitting get endpoint ATTRIB request for did '${did}' to ledger '${pool.indyNamespace}'` ) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) if (!response.result.data) { return null diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts index 308947062b..1783f13ee3 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts @@ -80,8 +80,9 @@ describe('IndyVdrIndyDidRegistrar', () => { const result = await indyVdrIndyDidRegistrar.create(agentContext, { did: 'did:indy:pool1:did-value', options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', alias: 'Hello', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', }, secret: { privateKey: TypedArrayEncoder.fromString('key'), @@ -98,11 +99,12 @@ describe('IndyVdrIndyDidRegistrar', () => { }) }) - test('returns an error state if the submitter did is not a valid did:indy did', async () => { + test('returns an error state if the endorser did is not a valid did:indy did', async () => { const result = await indyVdrIndyDidRegistrar.create(agentContext, { method: 'indy', options: { - submitterDid: 'BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'BzCbsNYhMrjHiqZDTUASHg', alias: 'Hello', }, }) @@ -121,7 +123,8 @@ describe('IndyVdrIndyDidRegistrar', () => { const result = await indyVdrIndyDidRegistrar.create(agentContext, { did: 'BzCbsNYhMrjHiqZDTUASHg', options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', verkey: 'verkey', alias: 'Hello', }, @@ -141,7 +144,8 @@ describe('IndyVdrIndyDidRegistrar', () => { const result = await indyVdrIndyDidRegistrar.create(agentContext, { did: 'BzCbsNYhMrjHiqZDTUASHg', options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', alias: 'Hello', }, }) @@ -160,7 +164,8 @@ describe('IndyVdrIndyDidRegistrar', () => { const result = await indyVdrIndyDidRegistrar.create(agentContext, { did: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', verkey: 'verkey', alias: 'Hello', }, @@ -176,11 +181,12 @@ describe('IndyVdrIndyDidRegistrar', () => { }) }) - test('returns an error state if did is provided, but does not match with the namespace from the submitterDid', async () => { + test('returns an error state if did is provided, but does not match with the namespace from the endorserDid', async () => { const result = await indyVdrIndyDidRegistrar.create(agentContext, { did: 'did:indy:pool2:B6xaJg1c2xU3D9ppCtt1CZ', options: { - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', alias: 'Hello', }, @@ -192,7 +198,7 @@ describe('IndyVdrIndyDidRegistrar', () => { didState: { state: 'failed', reason: - 'The submitter did uses namespace pool1 and the did to register uses namespace pool2. Namespaces must match.', + "The endorser did uses namespace: 'pool1' and the did to register uses namespace: 'pool2'. Namespaces must match.", }, }) }) @@ -200,6 +206,14 @@ describe('IndyVdrIndyDidRegistrar', () => { test('creates a did:indy document without services', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + // @ts-ignore - method is private + const createRegisterDidWriteRequest = jest.spyOn( + indyVdrIndyDidRegistrar, + 'createRegisterDidWriteRequest' + ) + // @ts-ignore type check fails because method is private + createRegisterDidWriteRequest.mockImplementationOnce(() => Promise.resolve()) + // @ts-ignore - method is private const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') // @ts-ignore type check fails because method is private @@ -209,30 +223,27 @@ describe('IndyVdrIndyDidRegistrar', () => { method: 'indy', options: { alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', role: 'STEWARD', }, secret: { privateKey, }, }) - expect(registerPublicDidSpy).toHaveBeenCalledWith( + + expect(createRegisterDidWriteRequest).toHaveBeenCalledWith({ agentContext, - poolMock, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'B6xaJg1c2xU3D9ppCtt1CZ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Role - 'STEWARD', - undefined - ) + pool: poolMock, + signingKey: expect.any(Key), + submitterNamespaceIdentifier: 'BzCbsNYhMrjHiqZDTUASHg', + namespaceIdentifier: 'B6xaJg1c2xU3D9ppCtt1CZ', + verificationKey: expect.any(Key), + alias: 'Hello', + diddocContent: undefined, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith(agentContext, poolMock, undefined) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -263,7 +274,15 @@ describe('IndyVdrIndyDidRegistrar', () => { test('creates a did:indy document by passing did', async () => { // @ts-ignore - method is private - const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + const createRegisterDidWriteRequest = jest.spyOn( + indyVdrIndyDidRegistrar, + 'createRegisterDidWriteRequest' + ) + // @ts-ignore type check fails because method is private + createRegisterDidWriteRequest.mockImplementationOnce(() => Promise.resolve()) + + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) @@ -272,26 +291,28 @@ describe('IndyVdrIndyDidRegistrar', () => { options: { verkey: 'E6D1m3eERqCueX4ZgMCY14B4NceAr6XP2HyVqt55gDhu', alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', role: 'STEWARD', }, secret: {}, }) + + expect(createRegisterDidWriteRequest).toHaveBeenCalledWith({ + agentContext, + pool: poolMock, + signingKey: expect.any(Key), + submitterNamespaceIdentifier: 'BzCbsNYhMrjHiqZDTUASHg', + namespaceIdentifier: 'B6xaJg1c2xU3D9ppCtt1CZ', + verificationKey: expect.any(Key), + alias: 'Hello', + diddocContent: undefined, + }) + expect(registerPublicDidSpy).toHaveBeenCalledWith( agentContext, poolMock, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'B6xaJg1c2xU3D9ppCtt1CZ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Role - 'STEWARD', + // writeRequest undefined ) expect(JsonTransformer.toJSON(result)).toMatchObject({ @@ -324,11 +345,18 @@ describe('IndyVdrIndyDidRegistrar', () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') // @ts-ignore - method is private - const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') + const createRegisterDidWriteRequestSpy = jest.spyOn( + indyVdrIndyDidRegistrar, + 'createRegisterDidWriteRequest' + ) + // @ts-ignore type check fails because method is private + createRegisterDidWriteRequestSpy.mockImplementationOnce(() => Promise.resolve()) + + // @ts-ignore - method is private + const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - method is private const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') @@ -336,7 +364,8 @@ describe('IndyVdrIndyDidRegistrar', () => { method: 'indy', options: { alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', role: 'STEWARD', services: [ new DidDocumentService({ @@ -365,22 +394,15 @@ describe('IndyVdrIndyDidRegistrar', () => { }, }) - expect(registerPublicDidSpy).toHaveBeenCalledWith( + expect(createRegisterDidWriteRequestSpy).toHaveBeenCalledWith({ agentContext, - poolMock, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'B6xaJg1c2xU3D9ppCtt1CZ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Role - 'STEWARD', - { + pool: poolMock, + signingKey: expect.any(Key), + submitterNamespaceIdentifier: 'BzCbsNYhMrjHiqZDTUASHg', + namespaceIdentifier: 'B6xaJg1c2xU3D9ppCtt1CZ', + verificationKey: expect.any(Key), + alias: 'Hello', + diddocContent: { '@context': [], authentication: [], id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ', @@ -416,7 +438,14 @@ describe('IndyVdrIndyDidRegistrar', () => { type: 'X25519KeyAgreementKey2019', }, ], - } + }, + }) + + expect(registerPublicDidSpy).toHaveBeenCalledWith( + agentContext, + poolMock, + // writeRequest + undefined ) expect(setEndpointsForDidSpy).not.toHaveBeenCalled() expect(JsonTransformer.toJSON(result)).toMatchObject({ @@ -484,11 +513,27 @@ describe('IndyVdrIndyDidRegistrar', () => { test('creates a did:indy document with services using attrib', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + // @ts-ignore - method is private + const createRegisterDidWriteRequestSpy = jest.spyOn( + indyVdrIndyDidRegistrar, + 'createRegisterDidWriteRequest' + ) + // @ts-ignore type check fails because method is private + createRegisterDidWriteRequestSpy.mockImplementationOnce(() => Promise.resolve()) + // @ts-ignore - method is private const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') // @ts-ignore type check fails because method is private registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve()) + // @ts-ignore - method is private + const createSetDidEndpointsRequestSpy = jest.spyOn( + indyVdrIndyDidRegistrar, + 'createSetDidEndpointsRequest' + ) + // @ts-ignore type check fails because method is private + createSetDidEndpointsRequestSpy.mockImplementationOnce(() => Promise.resolve(undefined)) + // @ts-ignore - method is private const setEndpointsForDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'setEndpointsForDid') // @ts-ignore type check fails because method is private @@ -498,7 +543,8 @@ describe('IndyVdrIndyDidRegistrar', () => { method: 'indy', options: { alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', role: 'STEWARD', useEndpointAttrib: true, services: [ @@ -528,33 +574,37 @@ describe('IndyVdrIndyDidRegistrar', () => { }, }) + expect(createRegisterDidWriteRequestSpy).toHaveBeenCalledWith({ + agentContext, + pool: poolMock, + signingKey: expect.any(Key), + submitterNamespaceIdentifier: 'BzCbsNYhMrjHiqZDTUASHg', + namespaceIdentifier: 'B6xaJg1c2xU3D9ppCtt1CZ', + verificationKey: expect.any(Key), + alias: 'Hello', + diddocContent: undefined, + }) + expect(registerPublicDidSpy).toHaveBeenCalledWith( agentContext, poolMock, - // Unqualified submitter did - 'BzCbsNYhMrjHiqZDTUASHg', - // submitter signing key, - expect.any(Key), - // Unqualified created indy did - 'B6xaJg1c2xU3D9ppCtt1CZ', - // Verkey - expect.any(Key), - // Alias - 'Hello', - // Role - 'STEWARD' + // writeRequest + undefined ) - expect(setEndpointsForDidSpy).toHaveBeenCalledWith( + expect(createSetDidEndpointsRequestSpy).toHaveBeenCalledWith({ agentContext, - poolMock, - 'B6xaJg1c2xU3D9ppCtt1CZ', - expect.any(Key), - { + pool: poolMock, + signingKey: expect.any(Key), + endorserDid: undefined, + // Unqualified created indy did + unqualifiedDid: 'B6xaJg1c2xU3D9ppCtt1CZ', + endpoints: { endpoint: 'https://example.com/endpoint', routingKeys: ['key-1'], types: ['endpoint', 'did-communication', 'DIDComm'], - } - ) + }, + }) + expect(setEndpointsForDidSpy).not.toHaveBeenCalled() expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -620,6 +670,14 @@ describe('IndyVdrIndyDidRegistrar', () => { test('stores the did document', async () => { const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + // @ts-ignore - method is private + const createRegisterDidWriteRequestSpy = jest.spyOn( + indyVdrIndyDidRegistrar, + 'createRegisterDidWriteRequest' + ) + // @ts-ignore type check fails because method is private + createRegisterDidWriteRequestSpy.mockImplementationOnce(() => Promise.resolve()) + // @ts-ignore - method is private const registerPublicDidSpy = jest.spyOn(indyVdrIndyDidRegistrar, 'registerPublicDid') // @ts-ignore type check fails because method is private @@ -637,7 +695,8 @@ describe('IndyVdrIndyDidRegistrar', () => { method: 'indy', options: { alias: 'Hello', - submitterDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', + endorserMode: 'internal', + endorserDid: 'did:indy:pool1:BzCbsNYhMrjHiqZDTUASHg', role: 'STEWARD', services: [ new DidDocumentService({ diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts index e37266f5c7..1e652a9b5f 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts @@ -39,12 +39,12 @@ describe('IndyVdrIndyDidResolver', () => { }, } - const poolMockSubmitReadRequest = jest.spyOn(poolMock, 'submitReadRequest') - poolMockSubmitReadRequest.mockResolvedValueOnce(nymResponse) + const poolMockSubmitRequest = jest.spyOn(poolMock, 'submitRequest') + poolMockSubmitRequest.mockResolvedValueOnce(nymResponse) const result = await resolver.resolve(agentContext, did) - expect(poolMockSubmitReadRequest).toHaveBeenCalledTimes(1) + expect(poolMockSubmitRequest).toHaveBeenCalledTimes(1) expect(JsonTransformer.toJSON(result)).toMatchObject({ didDocument: didIndyLjgpST2rjsoxYegQDRm7EL, didDocumentMetadata: {}, @@ -75,8 +75,8 @@ describe('IndyVdrIndyDidResolver', () => { }, } - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(attribResponse) const result = await resolver.resolve(agentContext, did) @@ -114,8 +114,8 @@ describe('IndyVdrIndyDidResolver', () => { }, } - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(attribResponse) const result = await resolver.resolve(agentContext, did) @@ -131,7 +131,7 @@ describe('IndyVdrIndyDidResolver', () => { it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { const did = 'did:indy:ns1:R1xKJw17sUoXhejEpugMYJ' - jest.spyOn(poolMock, 'submitReadRequest').mockRejectedValue(new Error('Error submitting read request')) + jest.spyOn(poolMock, 'submitRequest').mockRejectedValue(new Error('Error submitting read request')) const result = await resolver.resolve(agentContext, did) diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts index 0ed14f5856..84ac0006df 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts @@ -50,8 +50,8 @@ describe('DidResolver', () => { }, } - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(attribResponse) const result = await resolver.resolve(agentContext, did, parseDid(did)) @@ -89,8 +89,8 @@ describe('DidResolver', () => { }, } - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(nymResponse) - jest.spyOn(poolMock, 'submitReadRequest').mockResolvedValueOnce(attribResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(nymResponse) + jest.spyOn(poolMock, 'submitRequest').mockResolvedValueOnce(attribResponse) const result = await resolver.resolve(agentContext, did, parseDid(did)) @@ -106,7 +106,7 @@ describe('DidResolver', () => { it('should return did resolution metadata with error if the indy ledger service throws an error', async () => { const did = 'did:sov:R1xKJw17sUoXhejEpugMYJ' - jest.spyOn(poolMock, 'submitReadRequest').mockRejectedValue(new Error('Error submitting read request')) + jest.spyOn(poolMock, 'submitRequest').mockRejectedValue(new Error('Error submitting read request')) const result = await resolver.resolve(agentContext, did, parseDid(did)) diff --git a/packages/indy-vdr/src/dids/didIndyUtil.ts b/packages/indy-vdr/src/dids/didIndyUtil.ts index 50adce5226..8fd8fcdf9a 100644 --- a/packages/indy-vdr/src/dids/didIndyUtil.ts +++ b/packages/indy-vdr/src/dids/didIndyUtil.ts @@ -1,10 +1,10 @@ +import type { GetNymResponseData, IndyEndpointAttrib } from './didSovUtil' +import type { IndyVdrPool } from '../pool' import type { AgentContext } from '@aries-framework/core' import { parseIndyDid } from '@aries-framework/anoncreds' import { - getKeyFromVerificationMethod, AriesFrameworkError, - convertPublicKeyToX25519, DidDocument, DidDocumentBuilder, DidsApi, @@ -13,7 +13,14 @@ import { Key, KeyType, TypedArrayEncoder, + convertPublicKeyToX25519, + getKeyFromVerificationMethod, } from '@aries-framework/core' +import { GetAttribRequest, GetNymRequest } from '@hyperledger/indy-vdr-shared' + +import { IndyVdrError, IndyVdrNotFoundError } from '../error' + +import { addServicesFromEndpointsAttrib, getFullVerkey } from './didSovUtil' // Create a base DIDDoc template according to https://hyperledger.github.io/indy-did-method/#base-diddoc-template export function indyDidDocumentFromDid(did: string, verKeyBase58: string) { @@ -181,3 +188,90 @@ export async function verificationKeyForIndyDid(agentContext: AgentContext, did: return key } + +export async function getPublicDid(pool: IndyVdrPool, unqualifiedDid: string) { + const request = new GetNymRequest({ dest: unqualifiedDid }) + + const didResponse = await pool.submitRequest(request) + + if (!didResponse.result.data) { + throw new IndyVdrNotFoundError(`DID ${unqualifiedDid} not found in indy namespace ${pool.indyNamespace}`) + } + return JSON.parse(didResponse.result.data) as GetNymResponseData +} + +export async function getEndpointsForDid(agentContext: AgentContext, pool: IndyVdrPool, unqualifiedDid: string) { + try { + agentContext.config.logger.debug(`Get endpoints for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`) + + const request = new GetAttribRequest({ targetDid: unqualifiedDid, raw: 'endpoint' }) + + agentContext.config.logger.debug( + `Submitting get endpoint ATTRIB request for did '${unqualifiedDid}' to ledger '${pool.indyNamespace}'` + ) + const response = await pool.submitRequest(request) + + if (!response.result.data) { + return null + } + + const endpoints = JSON.parse(response.result.data as string)?.endpoint as IndyEndpointAttrib + agentContext.config.logger.debug( + `Got endpoints '${JSON.stringify(endpoints)}' for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`, + { + response, + endpoints, + } + ) + + return endpoints + } catch (error) { + agentContext.config.logger.error( + `Error retrieving endpoints for did '${unqualifiedDid}' from ledger '${pool.indyNamespace}'`, + { + error, + } + ) + + throw new IndyVdrError(error) + } +} + +export async function buildDidDocument(agentContext: AgentContext, pool: IndyVdrPool, did: string) { + const { namespaceIdentifier } = parseIndyDid(did) + + const nym = await getPublicDid(pool, namespaceIdentifier) + + // Create base Did Document + + // For modern did:indy DIDs, we assume that GET_NYM is always a full verkey in base58. + // For backwards compatibility, we accept a shortened verkey and convert it using previous convention + const verkey = getFullVerkey(namespaceIdentifier, nym.verkey) + + const builder = indyDidDocumentFromDid(did, verkey) + + // If GET_NYM does not return any diddocContent, fallback to legacy GET_ATTRIB endpoint + if (!nym.diddocContent) { + const keyAgreementId = `${did}#key-agreement-1` + const endpoints = await getEndpointsForDid(agentContext, pool, namespaceIdentifier) + + if (endpoints) { + builder + .addContext('https://w3id.org/security/suites/x25519-2019/v1') + .addVerificationMethod({ + controller: did, + id: keyAgreementId, + publicKeyBase58: createKeyAgreementKey(verkey), + type: 'X25519KeyAgreementKey2019', + }) + .addKeyAgreement(keyAgreementId) + + // Process endpoint attrib following the same rules as for did:sov + addServicesFromEndpointsAttrib(builder, did, endpoints, keyAgreementId) + } + return builder.build() + } else { + // Combine it with didDoc (TODO: Check if diddocContent is returned as a JSON object or a string) + return combineDidDocumentWithJson(builder.build(), nym.diddocContent) + } +} diff --git a/packages/indy-vdr/src/dids/index.ts b/packages/indy-vdr/src/dids/index.ts index 224dcb9d13..24a7a4ae9f 100644 --- a/packages/indy-vdr/src/dids/index.ts +++ b/packages/indy-vdr/src/dids/index.ts @@ -1,3 +1,3 @@ -export { IndyVdrIndyDidRegistrar } from './IndyVdrIndyDidRegistrar' +export { IndyVdrIndyDidRegistrar, IndyVdrDidCreateResult } from './IndyVdrIndyDidRegistrar' export { IndyVdrIndyDidResolver } from './IndyVdrIndyDidResolver' export { IndyVdrSovDidResolver } from './IndyVdrSovDidResolver' diff --git a/packages/indy-vdr/src/index.ts b/packages/indy-vdr/src/index.ts index 44f4dd3e24..19fdf8dad2 100644 --- a/packages/indy-vdr/src/index.ts +++ b/packages/indy-vdr/src/index.ts @@ -1,4 +1,4 @@ -export { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver } from './dids' +export { IndyVdrIndyDidRegistrar, IndyVdrIndyDidResolver, IndyVdrSovDidResolver, IndyVdrDidCreateResult } from './dids' export { IndyVdrPoolConfig } from './pool' export * from './IndyVdrModule' export * from './IndyVdrModuleConfig' diff --git a/packages/indy-vdr/src/pool/IndyVdrPool.ts b/packages/indy-vdr/src/pool/IndyVdrPool.ts index e5dfaade1a..db3959f070 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPool.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPool.ts @@ -1,6 +1,7 @@ import type { AgentContext, Key } from '@aries-framework/core' -import type { IndyVdrRequest, IndyVdrPool as indyVdrPool } from '@hyperledger/indy-vdr-shared' +import type { IndyVdrRequest, RequestResponseType, IndyVdrPool as indyVdrPool } from '@hyperledger/indy-vdr-shared' +import { parseIndyDid } from '@aries-framework/anoncreds' import { TypedArrayEncoder } from '@aries-framework/core' import { GetTransactionAuthorAgreementRequest, @@ -83,13 +84,18 @@ export class IndyVdrPool { // this.pool.close() } - public async submitWriteRequest( + public async prepareWriteRequest( agentContext: AgentContext, request: Request, - signingKey: Key + signingKey: Key, + endorserDid?: string ) { await this.appendTaa(request) + if (endorserDid) { + request.setEndorser({ endorser: parseIndyDid(endorserDid).namespaceIdentifier }) + } + const signature = await agentContext.wallet.sign({ data: TypedArrayEncoder.fromString(request.signatureInput), key: signingKey, @@ -99,11 +105,20 @@ export class IndyVdrPool { signature, }) - return await this.pool.submitRequest(request) + return request } - public async submitReadRequest(request: Request) { - return await this.pool.submitRequest(request) + /** + * This method submits a request to the ledger. + * It does only submit the request. It does not modify it in any way. + * To create the request, use the `prepareWriteRequest` method. + * @param writeRequest + */ + + public async submitRequest( + writeRequest: Request + ): Promise> { + return await this.pool.submitRequest(writeRequest) } private async appendTaa(request: IndyVdrRequest) { @@ -158,10 +173,10 @@ export class IndyVdrPool { } const taaRequest = new GetTransactionAuthorAgreementRequest({}) - const taaResponse = await this.submitReadRequest(taaRequest) + const taaResponse = await this.submitRequest(taaRequest) const acceptanceMechanismRequest = new GetAcceptanceMechanismsRequest({}) - const acceptanceMechanismResponse = await this.submitReadRequest(acceptanceMechanismRequest) + const acceptanceMechanismResponse = await this.submitRequest(acceptanceMechanismRequest) const taaData = taaResponse.result.data diff --git a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts index fc05f63672..aa0e15b518 100644 --- a/packages/indy-vdr/src/pool/IndyVdrPoolService.ts +++ b/packages/indy-vdr/src/pool/IndyVdrPoolService.ts @@ -174,7 +174,7 @@ export class IndyVdrPoolService { const request = new GetNymRequest({ dest: did }) this.logger.trace(`Submitting get did request for did '${did}' to ledger '${pool.indyNamespace}'`) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) if (!response.result.data) { throw new IndyVdrNotFoundError(`Did ${did} not found on indy pool with namespace ${pool.indyNamespace}`) diff --git a/packages/indy-vdr/tests/helpers.ts b/packages/indy-vdr/tests/helpers.ts index 304118e2a8..eb9a94b8cf 100644 --- a/packages/indy-vdr/tests/helpers.ts +++ b/packages/indy-vdr/tests/helpers.ts @@ -20,13 +20,14 @@ export const indyVdrModuleConfig = new IndyVdrModuleConfig({ ], }) -export async function createDidOnLedger(agent: Agent, submitterDid: string) { +export async function createDidOnLedger(agent: Agent, endorserDid: string) { const key = await agent.wallet.createKey({ keyType: KeyType.Ed25519 }) const createResult = await agent.dids.create({ method: 'indy', options: { - submitterDid, + endorserMode: 'internal', + endorserDid: endorserDid, alias: 'Alias', role: 'TRUSTEE', verkey: key.publicKeyBase58, diff --git a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts index b96f5ec8bf..f71251b948 100644 --- a/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-anoncreds-registry.e2e.test.ts @@ -231,7 +231,8 @@ describe('IndyVdrAnonCredsRegistry', () => { }) // After this call, the revocation registry should now be resolvable - await pool.submitWriteRequest(agent.context, revocationRegistryRequest, signingKey) + const writeRequest = await pool.prepareWriteRequest(agent.context, revocationRegistryRequest, signingKey) + await pool.submitRequest(writeRequest) // Also create a revocation registry entry const revocationEntryRequest = new RevocationRegistryEntryRequest({ @@ -247,7 +248,13 @@ describe('IndyVdrAnonCredsRegistry', () => { }) // After this call we can query the revocation registry entries (using timestamp now) - const entryResponse = await pool.submitWriteRequest(agent.context, revocationEntryRequest, signingKey) + + const revocationEntryWriteRequest = await pool.prepareWriteRequest( + agent.context, + revocationEntryRequest, + signingKey + ) + const entryResponse = await pool.submitRequest(revocationEntryWriteRequest) const legacyRevocationRegistryDefinition = await indyVdrAnonCredsRegistry.getRevocationRegistryDefinition( agent.context, diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index 2e8035fed0..95e8742dd6 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -1,4 +1,4 @@ -import type { IndyVdrDidCreateOptions } from '../src/dids/IndyVdrIndyDidRegistrar' +import type { IndyVdrDidCreateOptions, IndyVdrDidCreateResult } from '../src/dids/IndyVdrIndyDidRegistrar' import { didIndyRegex } from '@aries-framework/anoncreds' import { @@ -26,6 +26,25 @@ import { indyDidFromNamespaceAndInitialKey } from '../src/dids/didIndyUtil' import { indyVdrModuleConfig } from './helpers' +const endorser = new Agent( + getAgentOptions( + 'Indy VDR Indy DID Registrar', + {}, + { + indyVdr: new IndyVdrModule({ + networks: indyVdrModuleConfig.networks, + indyVdr, + }), + indySdk: new IndySdkModule({ + indySdk, + }), + dids: new DidsModule({ + registrars: [new IndyVdrIndyDidRegistrar()], + resolvers: [new IndyVdrIndyDidResolver(), new IndyVdrSovDidResolver()], + }), + } + ) +) const agent = new Agent( getAgentOptions( 'Indy VDR Indy DID Registrar', @@ -47,27 +66,32 @@ const agent = new Agent( ) describe('Indy VDR Indy Did Registrar', () => { - let submitterDid: string + let endorserDid: string beforeAll(async () => { - await agent.initialize() + await endorser.initialize() const unqualifiedSubmitterDid = await importExistingIndyDidFromPrivateKey( - agent, - TypedArrayEncoder.fromString('000000000000000000000000Trustee9') + endorser, + TypedArrayEncoder.fromString('00000000000000000000000Endorser9') ) - submitterDid = `did:indy:pool:localtest:${unqualifiedSubmitterDid}` + endorserDid = `did:indy:pool:localtest:${unqualifiedSubmitterDid}` + + await agent.initialize() }) afterAll(async () => { + await endorser.shutdown() + await endorser.wallet.delete() await agent.shutdown() await agent.wallet.delete() }) test('can register a did:indy without services', async () => { - const didRegistrationResult = await agent.dids.create({ + const didRegistrationResult = await endorser.dids.create({ method: 'indy', options: { - submitterDid, + endorserDid, + endorserMode: 'internal', }, }) @@ -104,7 +128,7 @@ describe('Indy VDR Indy Did Registrar', () => { // Wait some time pass to let ledger settle the object await sleep(1000) - const didResolutionResult = await agent.dids.resolve(did) + const didResolutionResult = await endorser.dids.resolve(did) expect(JsonTransformer.toJSON(didResolutionResult)).toMatchObject({ didDocument: { '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], @@ -131,7 +155,94 @@ describe('Indy VDR Indy Did Registrar', () => { }) }) - test('can register a did:indy without services - did and verkey specified', async () => { + test('can register a did:indy without services with endorser', async () => { + const didCreateTobeEndorsedResult = (await agent.dids.create({ + method: 'indy', + options: { + endorserMode: 'external', + endorserDid, + }, + })) as IndyVdrDidCreateResult + + const didState = didCreateTobeEndorsedResult.didState + if (didState.state !== 'action' || didState.action !== 'endorseIndyTransaction') throw Error('unexpected did state') + + const signedNymRequest = await endorser.modules.indyVdr.endorseTransaction( + didState.nymRequest, + didState.endorserDid + ) + const didCreateSubmitResult = await agent.dids.create({ + did: didState.did, + options: { + endorserMode: 'external', + endorsedTransaction: { + nymRequest: signedNymRequest, + }, + }, + secret: didState.secret, + }) + + expect(JsonTransformer.toJSON(didCreateSubmitResult)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: expect.stringMatching(didIndyRegex), + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: expect.stringMatching(didIndyRegex), + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: expect.stringMatching(didIndyRegex), + id: expect.stringContaining('#verkey'), + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [expect.stringContaining('#verkey')], + service: undefined, + }, + }, + }) + + const did = didCreateSubmitResult.didState.did + if (!did) throw Error('did not defined') + + // Wait some time pass to let ledger settle the object + await sleep(1000) + + const didResolutionResult = await endorser.dids.resolve(did) + expect(JsonTransformer.toJSON(didResolutionResult)).toMatchObject({ + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: expect.any(String), + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('can register an endorsed did:indy without services - did and verkey specified', async () => { // Generate a seed and the indy did. This allows us to create a new did every time // but still check if the created output document is as expected. const seed = Array(32 + 1) @@ -145,17 +256,114 @@ describe('Indy VDR Indy Did Registrar', () => { 'pool:localtest', Key.fromPublicKey(keyPair.publicKey, KeyType.Ed25519) ) - const didRegistrationResult = await agent.dids.create({ + + const didCreateTobeEndorsedResult = (await agent.dids.create({ did, options: { - submitterDid, + endorserDid, + endorserMode: 'external', verkey, }, + })) as IndyVdrDidCreateResult + + const didState = didCreateTobeEndorsedResult.didState + if (didState.state !== 'action' || didState.action !== 'endorseIndyTransaction') throw Error('unexpected did state') + + const signedNymRequest = await endorser.modules.indyVdr.endorseTransaction( + didState.nymRequest, + didState.endorserDid + ) + const didCreateSubmitResult = await agent.dids.create({ + did: didState.did, + options: { + endorserMode: 'external', + endorsedTransaction: { + nymRequest: signedNymRequest, + }, + }, + secret: didState.secret, + }) + + expect(JsonTransformer.toJSON(didCreateSubmitResult)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did, + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, + }, + }, }) // Wait some time pass to let ledger settle the object await sleep(1000) + const didResult = await endorser.dids.resolve(did) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: undefined, + }, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('can register a did:indy without services - did and verkey specified', async () => { + // Generate a seed and the indy did. This allows us to create a new did every time + // but still check if the created output document is as expected. + const seed = Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + + const keyPair = generateKeyPairFromSeed(TypedArrayEncoder.fromString(seed)) + const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(keyPair.publicKey) + + const { did, verkey } = indyDidFromNamespaceAndInitialKey( + 'pool:localtest', + Key.fromPublicKey(keyPair.publicKey, KeyType.Ed25519) + ) + const didRegistrationResult = await endorser.dids.create({ + did, + options: { + endorserDid, + endorserMode: 'internal', + verkey, + }, + }) + expect(JsonTransformer.toJSON(didRegistrationResult)).toMatchObject({ didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -186,7 +394,7 @@ describe('Indy VDR Indy Did Registrar', () => { // Wait some time pass to let ledger settle the object await sleep(1000) - const didResult = await agent.dids.resolve(did) + const didResult = await endorser.dids.resolve(did) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: { '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/ed25519-2018/v1'], @@ -222,7 +430,7 @@ describe('Indy VDR Indy Did Registrar', () => { .slice(0, 32) ) - const key = await agent.wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) + const key = await endorser.wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(key.publicKey)) const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(key.publicKey) @@ -231,10 +439,11 @@ describe('Indy VDR Indy Did Registrar', () => { Key.fromPublicKey(key.publicKey, KeyType.Ed25519) ) - const didRegistrationResult = await agent.dids.create({ + const didRegistrationResult = await endorser.dids.create({ did, options: { - submitterDid, + endorserDid, + endorserMode: 'internal', useEndpointAttrib: true, verkey, services: [ @@ -326,7 +535,156 @@ describe('Indy VDR Indy Did Registrar', () => { // Wait some time pass to let ledger settle the object await sleep(1000) - const didResult = await agent.dids.resolve(did) + const didResult = await endorser.dids.resolve(did) + expect(JsonTransformer.toJSON(didResult)).toMatchObject({ + didDocument: expectedDidDocument, + didDocumentMetadata: {}, + didResolutionMetadata: { + contentType: 'application/did+ld+json', + }, + }) + }) + + test('can register an endorsed did:indy with services - did and verkey specified - using attrib endpoint', async () => { + // Generate a private key and the indy did. This allows us to create a new did every time + // but still check if the created output document is as expected. + const privateKey = TypedArrayEncoder.fromString( + Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + ) + + const key = await endorser.wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) + const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(key.publicKey)) + const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(key.publicKey) + + const { did, verkey } = indyDidFromNamespaceAndInitialKey( + 'pool:localtest', + Key.fromPublicKey(key.publicKey, KeyType.Ed25519) + ) + + const didCreateTobeEndorsedResult = (await endorser.dids.create({ + did, + options: { + endorserMode: 'external', + endorserDid: endorserDid, + useEndpointAttrib: true, + verkey, + services: [ + new DidDocumentService({ + id: `${did}#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }), + new DidCommV1Service({ + id: `${did}#did-communication`, + priority: 0, + recipientKeys: [`${did}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + accept: ['didcomm/aip2;env=rfc19'], + }), + new DidCommV2Service({ + accept: ['didcomm/v2'], + id: `${did}#didcomm-1`, + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + }), + ], + }, + })) as IndyVdrDidCreateResult + + const didState = didCreateTobeEndorsedResult.didState + if (didState.state !== 'action' || didState.action !== 'endorseIndyTransaction') throw Error('unexpected did state') + + const signedNymRequest = await endorser.modules.indyVdr.endorseTransaction( + didState.nymRequest, + didState.endorserDid + ) + + if (!didState.attribRequest) throw Error('attrib request not found') + const endorsedAttribRequest = await endorser.modules.indyVdr.endorseTransaction( + didState.attribRequest, + didState.endorserDid + ) + + const didCreateSubmitResult = await agent.dids.create({ + did: didState.did, + options: { + endorserMode: 'external', + endorsedTransaction: { + nymRequest: signedNymRequest, + attribRequest: endorsedAttribRequest, + }, + }, + secret: didState.secret, + }) + + const expectedDidDocument = { + '@context': [ + 'https://w3id.org/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/x25519-2019/v1', + 'https://didcomm.org/messaging/contexts/v2', + ], + id: did, + alsoKnownAs: undefined, + controller: undefined, + verificationMethod: [ + { + type: 'Ed25519VerificationKey2018', + controller: did, + id: `${did}#verkey`, + publicKeyBase58: ed25519PublicKeyBase58, + }, + { + type: 'X25519KeyAgreementKey2019', + controller: did, + id: `${did}#key-agreement-1`, + publicKeyBase58: x25519PublicKeyBase58, + }, + ], + capabilityDelegation: undefined, + capabilityInvocation: undefined, + authentication: [`${did}#verkey`], + service: [ + { + id: `${did}#endpoint`, + serviceEndpoint: 'https://example.com/endpoint', + type: 'endpoint', + }, + { + accept: ['didcomm/aip2;env=rfc19'], + id: `${did}#did-communication`, + priority: 0, + recipientKeys: [`${did}#key-agreement-1`], + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'did-communication', + }, + { + accept: ['didcomm/v2'], + id: `${did}#didcomm-1`, + routingKeys: ['a-routing-key'], + serviceEndpoint: 'https://example.com/endpoint', + type: 'DIDComm', + }, + ], + } + + expect(JsonTransformer.toJSON(didCreateSubmitResult)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did, + didDocument: expectedDidDocument, + }, + }) + // Wait some time pass to let ledger settle the object + await sleep(1000) + + const didResult = await endorser.dids.resolve(did) expect(JsonTransformer.toJSON(didResult)).toMatchObject({ didDocument: expectedDidDocument, didDocumentMetadata: {}, diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index 151b226a70..e3dab42a4b 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -59,7 +59,7 @@ describe('IndyVdrPoolService', () => { dest: 'TL1EaPFCZ8Si5aUrqScBDt', }) - const response = await pool.submitReadRequest(request) + const response = await pool.submitRequest(request) expect(response).toMatchObject({ op: 'REPLY', @@ -98,7 +98,8 @@ describe('IndyVdrPoolService', () => { verkey: key.publicKeyBase58, }) - const response = await pool.submitWriteRequest(agentContext, request, signerKey) + const writeRequest = await pool.prepareWriteRequest(agentContext, request, signerKey) + const response = await pool.submitRequest(writeRequest) expect(response).toMatchObject({ op: 'REPLY', @@ -133,7 +134,8 @@ describe('IndyVdrPoolService', () => { }, }) - const schemaResponse = await pool.submitWriteRequest(agentContext, schemaRequest, signerKey) + const writeRequest = await pool.prepareWriteRequest(agentContext, schemaRequest, signerKey) + const schemaResponse = await pool.submitRequest(writeRequest) expect(schemaResponse).toMatchObject({ op: 'REPLY', @@ -183,7 +185,8 @@ describe('IndyVdrPoolService', () => { }, }) - const response = await pool.submitWriteRequest(agentContext, credentialDefinitionRequest, signerKey) + const credDefWriteRequest = await pool.prepareWriteRequest(agentContext, credentialDefinitionRequest, signerKey) + const response = await pool.submitRequest(credDefWriteRequest) expect(response).toMatchObject({ op: 'REPLY', diff --git a/packages/node/package.json b/packages/node/package.json index f365568e32..751b312f00 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -35,7 +35,7 @@ "devDependencies": { "@types/ffi-napi": "^4.0.5", "@types/node": "^16.11.7", - "@types/node-fetch": "^2.5.10", + "@types/node-fetch": "2.6.2", "@types/ref-napi": "^3.0.4", "@types/ws": "^8.5.4", "nock": "^13.3.0", diff --git a/yarn.lock b/yarn.lock index 202590163b..b6a201b761 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,11 +3,11 @@ "@ampproject/remapping@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" "@azure/core-asynciterator-polyfill@^1.0.2": @@ -15,45 +15,45 @@ resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.2.tgz#0dd3849fb8d97f062a39db0e5cadc9ffaf861fec" integrity sha512-3rkP4LnnlWawl0LZptJOdXNrT/fHp2eQMadoasa6afspXdpGrtPZuAQc2PD0cpgyuoXtUWyC3tv7xfntjGS5Dw== -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298" - integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" + integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.0": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e" - integrity sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" + integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.21.3" - "@babel/helper-compilation-targets" "^7.20.7" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" "@babel/helper-module-transforms" "^7.21.2" "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.3" + "@babel/parser" "^7.21.4" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.3" - "@babel/types" "^7.21.3" + "@babel/traverse" "^7.21.4" + "@babel/types" "^7.21.4" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.20.0", "@babel/generator@^7.21.3", "@babel/generator@^7.7.2": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce" - integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA== +"@babel/generator@^7.20.0", "@babel/generator@^7.21.4", "@babel/generator@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" + integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== dependencies: - "@babel/types" "^7.21.3" + "@babel/types" "^7.21.4" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -65,21 +65,21 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" + integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" + "@babel/compat-data" "^7.21.4" + "@babel/helper-validator-option" "^7.21.0" browserslist "^4.21.3" lru-cache "^5.1.1" semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9" - integrity sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz#3a017163dc3c2ba7deb9a7950849a9586ea24c18" + integrity sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-environment-visitor" "^7.18.9" @@ -91,9 +91,9 @@ "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz#53ff78472e5ce10a52664272a239787107603ebb" - integrity sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz#40411a8ab134258ad2cf3a3d987ec6aa0723cee5" + integrity sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" regexpu-core "^5.3.1" @@ -137,12 +137,12 @@ dependencies: "@babel/types" "^7.21.0" -"@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== +"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.21.4" "@babel/helper-module-transforms@^7.21.2": version "7.21.2" @@ -223,7 +223,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": +"@babel/helper-validator-option@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== @@ -256,10 +256,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3" - integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" + integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== "@babel/plugin-proposal-async-generator-functions@^7.0.0": version "7.20.7" @@ -359,11 +359,11 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.0", "@babel/plugin-syntax-flow@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz#774d825256f2379d06139be0c723c4dd444f3ca1" - integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz#3e37fca4f06d93567c1cd9b75156422e90a67107" + integrity sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -379,12 +379,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.7.2": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" - integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" + integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" @@ -436,11 +436,11 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.20.0", "@babel/plugin-syntax-typescript@^7.7.2": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7" - integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz#2751948e9b7c6d771a8efa59340c15d4a2891ff8" + integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-arrow-functions@^7.0.0": version "7.20.7" @@ -502,7 +502,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.18.6": +"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz#6aeca0adcb81dc627c8986e770bfaa4d9812aff5" integrity sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w== @@ -540,7 +540,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8": +"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.21.2": version "7.21.2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== @@ -612,11 +612,11 @@ "@babel/types" "^7.21.0" "@babel/plugin-transform-runtime@^7.0.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz#2a884f29556d0a68cd3d152dcc9e6c71dfb6eee8" - integrity sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz#2e1da21ca597a7d01fc96b699b21d8d2023191aa" + integrity sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA== dependencies: - "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-module-imports" "^7.21.4" "@babel/helper-plugin-utils" "^7.20.2" babel-plugin-polyfill-corejs2 "^0.3.3" babel-plugin-polyfill-corejs3 "^0.6.0" @@ -652,7 +652,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-typescript@^7.21.0", "@babel/plugin-transform-typescript@^7.5.0": +"@babel/plugin-transform-typescript@^7.21.3", "@babel/plugin-transform-typescript@^7.5.0": version "7.21.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz#316c5be579856ea890a57ebc5116c5d064658f2b" integrity sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw== @@ -671,22 +671,24 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/preset-flow@^7.13.13": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.18.6.tgz#83f7602ba566e72a9918beefafef8ef16d2810cb" - integrity sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.21.4.tgz#a5de2a1cafa61f0e0b3af9b30ff0295d38d3608f" + integrity sha512-F24cSq4DIBmhq4OzK3dE63NHagb27OPE3eWR+HLekt4Z3Y5MzIIUGF3LlLgV0gN8vzbDViSY7HnrReNVCJXTeA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-validator-option" "^7.18.6" - "@babel/plugin-transform-flow-strip-types" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-transform-flow-strip-types" "^7.21.0" "@babel/preset-typescript@^7.13.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz#bcbbca513e8213691fe5d4b23d9251e01f00ebff" - integrity sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz#b913ac8e6aa8932e47c21b01b4368d8aa239a529" + integrity sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-validator-option" "^7.21.0" - "@babel/plugin-transform-typescript" "^7.21.0" + "@babel/plugin-syntax-jsx" "^7.21.4" + "@babel/plugin-transform-modules-commonjs" "^7.21.2" + "@babel/plugin-transform-typescript" "^7.21.3" "@babel/register@^7.13.16": version "7.21.0" @@ -720,26 +722,26 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3", "@babel/traverse@^7.7.2": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67" - integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ== +"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" + integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.21.3" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.3" - "@babel/types" "^7.21.3" + "@babel/parser" "^7.21.4" + "@babel/types" "^7.21.4" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05" - integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" + integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -751,11 +753,11 @@ integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@cheqd/sdk@cjs": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-3.3.1.tgz#6ca44df56b28e3f8a4918b49115de0f10c01eed7" - integrity sha512-8qPWGaD8Mc/pEFdJh/Tz9/YFbOvxFMpFKOI8xGalQAcv+KCIs/qKqPCkcxBNquqR4MAWe2ovCWOXGPmx0IrNxQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.2.0.tgz#0b07c0e2337fbe2bd7d62a49ce9fb4515711b52a" + integrity sha512-HOhm6tqtX/h1ccoPuNEpF9R8tACbwDAshhjtBeV77FdbTfKN891xpjrbpnmlHQ6D8DlWEQwWIYPwDXZ0EmZWBQ== dependencies: - "@cheqd/ts-proto" cjs + "@cheqd/ts-proto" "^2.2.0" "@cosmjs/amino" "^0.29.5" "@cosmjs/crypto" "^0.29.5" "@cosmjs/encoding" "^0.29.5" @@ -771,7 +773,7 @@ multiformats "^9.9.0" uuid "^9.0.0" -"@cheqd/ts-proto@cjs": +"@cheqd/ts-proto@^2.2.0", "@cheqd/ts-proto@cjs": version "2.2.0" resolved "https://registry.yarnpkg.com/@cheqd/ts-proto/-/ts-proto-2.2.0.tgz#c296a9fff23f47fba84f9c3354439b8fc91129f4" integrity sha512-COTDndE/haSUPndVYaJGAVT4OMIrVSibGfLrKol9CXZBasmUUJx5rVFOpL34wYq6VcOrfF2TN+63TRePRUBWpA== @@ -962,25 +964,25 @@ credentials-context "^2.0.0" "@eslint-community/eslint-utils@^4.2.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz#a556790523a351b4e47e9d385f47265eaaf9780a" - integrity sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA== + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== dependencies: eslint-visitor-keys "^3.3.0" "@eslint-community/regexpp@^4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.4.0.tgz#3e61c564fcd6b921cb789838631c5ee44df09403" - integrity sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ== + version "4.5.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724" + integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ== -"@eslint/eslintrc@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.1.tgz#7888fe7ec8f21bc26d646dbd2c11cd776e21192d" - integrity sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw== +"@eslint/eslintrc@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.2.tgz#01575e38707add677cf73ca1589abba8da899a02" + integrity sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.5.0" + espree "^9.5.1" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -988,10 +990,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.36.0": - version "8.36.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe" - integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg== +"@eslint/js@8.38.0": + version "8.38.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.38.0.tgz#73a8a0d8aa8a8e6fe270431c5e72ae91b5337892" + integrity sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g== "@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": version "1.1.3" @@ -1332,46 +1334,48 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": +"@jridgewell/resolve-uri@3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== "@jridgewell/source-map@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" - integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda" + integrity sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg== dependencies: "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@1.4.14": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -1381,28 +1385,28 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== dependencies: "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@lerna/child-process@6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-6.5.1.tgz#da9161ba00e8d67fa7241a709703e5cc5e4a5e5e" - integrity sha512-QfyleXSD9slh4qM54wDaqKVPvtUH1NJMgsFc9BabqSHO1Ttpandv1EAvTCN9Lu73RbCX3LJpn+BfJmnjHbjCyw== +"@lerna/child-process@6.6.1": + version "6.6.1" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-6.6.1.tgz#e31bc411ad6d474cf7b676904da6f77f58fd64eb" + integrity sha512-yUCDCcRNNbI9UUsUB6FYEmDHpo5Tn/f0q5D7vhDP4i6Or8kBj82y7+e31hwfLvK2ykOYlDVs2MxAluH/+QUBOQ== dependencies: chalk "^4.1.0" execa "^5.0.0" strong-log-transformer "^2.1.0" -"@lerna/create@6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-6.5.1.tgz#326b5d26c247bfc9e2d8728aa1f69419840cec8c" - integrity sha512-ejERJnfA36jEuKrfM+94feLiyf2/hF2NoG923N0rE4rsmvRFPr1XLVPvAKleXW+Gdi/t1p410lJ7NKaLRMYCYw== +"@lerna/create@6.6.1": + version "6.6.1" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-6.6.1.tgz#fc20f09e10b612d424a576775ad6eefe6aa96517" + integrity sha512-GDmHFhQ0mr0RcXWXrsLyfMV6ch/dZV/Ped1e6sFVQhsLL9P+FFXX1ZWxa/dQQ90VWF2qWcmK0+S/L3kUz2xvTA== dependencies: - "@lerna/child-process" "6.5.1" + "@lerna/child-process" "6.6.1" dedent "^0.7.0" fs-extra "^9.1.0" init-package-json "^3.0.2" @@ -1416,6 +1420,74 @@ validate-npm-package-name "^4.0.0" yargs-parser "20.2.4" +"@lerna/legacy-package-management@6.6.1": + version "6.6.1" + resolved "https://registry.yarnpkg.com/@lerna/legacy-package-management/-/legacy-package-management-6.6.1.tgz#1f44af40098b9396a4f698514ff2b87016b1ee3d" + integrity sha512-0EYxSFr34VgeudA5rvjGJSY7s4seITMVB7AJ9LRFv9QDUk6jpvapV13ZAaKnhDTxX5vNCfnJuWHXXWq0KyPF/Q== + dependencies: + "@npmcli/arborist" "6.2.3" + "@npmcli/run-script" "4.1.7" + "@nrwl/devkit" ">=15.5.2 < 16" + "@octokit/rest" "19.0.3" + byte-size "7.0.0" + chalk "4.1.0" + clone-deep "4.0.1" + cmd-shim "5.0.0" + columnify "1.6.0" + config-chain "1.1.12" + conventional-changelog-core "4.2.4" + conventional-recommended-bump "6.1.0" + cosmiconfig "7.0.0" + dedent "0.7.0" + dot-prop "6.0.1" + execa "5.0.0" + file-url "3.0.0" + find-up "5.0.0" + fs-extra "9.1.0" + get-port "5.1.1" + get-stream "6.0.0" + git-url-parse "13.1.0" + glob-parent "5.1.2" + globby "11.1.0" + graceful-fs "4.2.10" + has-unicode "2.0.1" + inquirer "8.2.4" + is-ci "2.0.0" + is-stream "2.0.0" + libnpmpublish "6.0.4" + load-json-file "6.2.0" + make-dir "3.1.0" + minimatch "3.0.5" + multimatch "5.0.0" + node-fetch "2.6.7" + npm-package-arg "8.1.1" + npm-packlist "5.1.1" + npm-registry-fetch "14.0.3" + npmlog "6.0.2" + p-map "4.0.0" + p-map-series "2.1.0" + p-queue "6.6.2" + p-waterfall "2.1.1" + pacote "13.6.2" + pify "5.0.0" + pretty-format "29.4.3" + read-cmd-shim "3.0.0" + read-package-json "5.0.1" + resolve-from "5.0.0" + semver "7.3.8" + signal-exit "3.0.7" + slash "3.0.0" + ssri "9.0.1" + strong-log-transformer "2.1.0" + tar "6.1.11" + temp-dir "1.0.0" + tempy "1.0.0" + upath "2.0.1" + uuid "8.3.2" + write-file-atomic "4.0.1" + write-pkg "4.0.0" + yargs "16.2.0" + "@mapbox/node-pre-gyp@1.0.9": version "1.0.9" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc" @@ -1520,44 +1592,43 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@npmcli/arborist@5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-5.3.0.tgz#321d9424677bfc08569e98a5ac445ee781f32053" - integrity sha512-+rZ9zgL1lnbl8Xbb1NQdMjveOMwj4lIYfcDtyJHHi5x4X8jtR6m8SXooJMZy5vmFVZ8w7A2Bnd/oX9eTuU8w5A== +"@npmcli/arborist@6.2.3": + version "6.2.3" + resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-6.2.3.tgz#31f8aed2588341864d3811151d929c01308f8e71" + integrity sha512-lpGOC2ilSJXcc2zfW9QtukcCTcMbl3fVI0z4wvFB2AFIl0C+Q6Wv7ccrpdrQa8rvJ1ZVuc6qkX7HVTyKlzGqKA== dependencies: "@isaacs/string-locale-compare" "^1.1.0" - "@npmcli/installed-package-contents" "^1.0.7" - "@npmcli/map-workspaces" "^2.0.3" - "@npmcli/metavuln-calculator" "^3.0.1" - "@npmcli/move-file" "^2.0.0" - "@npmcli/name-from-folder" "^1.0.1" - "@npmcli/node-gyp" "^2.0.0" - "@npmcli/package-json" "^2.0.0" - "@npmcli/run-script" "^4.1.3" - bin-links "^3.0.0" - cacache "^16.0.6" + "@npmcli/fs" "^3.1.0" + "@npmcli/installed-package-contents" "^2.0.0" + "@npmcli/map-workspaces" "^3.0.2" + "@npmcli/metavuln-calculator" "^5.0.0" + "@npmcli/name-from-folder" "^2.0.0" + "@npmcli/node-gyp" "^3.0.0" + "@npmcli/package-json" "^3.0.0" + "@npmcli/query" "^3.0.0" + "@npmcli/run-script" "^6.0.0" + bin-links "^4.0.1" + cacache "^17.0.4" common-ancestor-path "^1.0.1" - json-parse-even-better-errors "^2.3.1" + hosted-git-info "^6.1.1" + json-parse-even-better-errors "^3.0.0" json-stringify-nice "^1.1.4" - mkdirp "^1.0.4" - mkdirp-infer-owner "^2.0.0" - nopt "^5.0.0" - npm-install-checks "^5.0.0" - npm-package-arg "^9.0.0" - npm-pick-manifest "^7.0.0" - npm-registry-fetch "^13.0.0" - npmlog "^6.0.2" - pacote "^13.6.1" - parse-conflict-json "^2.0.1" - proc-log "^2.0.0" + minimatch "^6.1.6" + nopt "^7.0.0" + npm-install-checks "^6.0.0" + npm-package-arg "^10.1.0" + npm-pick-manifest "^8.0.1" + npm-registry-fetch "^14.0.3" + npmlog "^7.0.1" + pacote "^15.0.8" + parse-conflict-json "^3.0.0" + proc-log "^3.0.0" promise-all-reject-late "^1.0.0" promise-call-limit "^1.0.1" - read-package-json-fast "^2.0.2" - readdir-scoped-modules "^1.1.0" - rimraf "^3.0.2" + read-package-json-fast "^3.0.2" semver "^7.3.7" - ssri "^9.0.0" - treeverse "^2.0.0" + ssri "^10.0.1" + treeverse "^3.0.0" walk-up-path "^1.0.0" "@npmcli/fs@^1.0.0": @@ -1576,6 +1647,13 @@ "@gar/promisify" "^1.1.3" semver "^7.3.5" +"@npmcli/fs@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e" + integrity sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w== + dependencies: + semver "^7.3.5" + "@npmcli/git@^3.0.0": version "3.0.2" resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-3.0.2.tgz#5c5de6b4d70474cf2d09af149ce42e4e1dacb931" @@ -1591,6 +1669,20 @@ semver "^7.3.5" which "^2.0.2" +"@npmcli/git@^4.0.0": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-4.0.4.tgz#cdf74f21b1d440c0756fb28159d935129d9daa33" + integrity sha512-5yZghx+u5M47LghaybLCkdSyFzV/w4OuH12d96HO389Ik9CDsLaDZJVynSGGVJOLn6gy/k7Dz5XYcplM3uxXRg== + dependencies: + "@npmcli/promise-spawn" "^6.0.0" + lru-cache "^7.4.4" + npm-pick-manifest "^8.0.0" + proc-log "^3.0.0" + promise-inflight "^1.0.1" + promise-retry "^2.0.1" + semver "^7.3.5" + which "^3.0.0" + "@npmcli/installed-package-contents@^1.0.7": version "1.0.7" resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" @@ -1599,24 +1691,32 @@ npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" -"@npmcli/map-workspaces@^2.0.3": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-2.0.4.tgz#9e5e8ab655215a262aefabf139782b894e0504fc" - integrity sha512-bMo0aAfwhVwqoVM5UzX1DJnlvVvzDCHae821jv48L1EsrYwfOZChlqWYXEtto/+BkBXetPbEWgau++/brh4oVg== +"@npmcli/installed-package-contents@^2.0.0", "@npmcli/installed-package-contents@^2.0.1": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz#bfd817eccd9e8df200919e73f57f9e3d9e4f9e33" + integrity sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ== dependencies: - "@npmcli/name-from-folder" "^1.0.1" - glob "^8.0.1" - minimatch "^5.0.1" - read-package-json-fast "^2.0.3" + npm-bundled "^3.0.0" + npm-normalize-package-bin "^3.0.0" -"@npmcli/metavuln-calculator@^3.0.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@npmcli/metavuln-calculator/-/metavuln-calculator-3.1.1.tgz#9359bd72b400f8353f6a28a25c8457b562602622" - integrity sha512-n69ygIaqAedecLeVH3KnO39M6ZHiJ2dEv5A7DGvcqCB8q17BGUgW8QaanIkbWUo2aYGZqJaOORTLAlIvKjNDKA== +"@npmcli/map-workspaces@^3.0.2": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-3.0.3.tgz#476944b63cd1f65bf83c6fdc7f4ca7be56906b1f" + integrity sha512-HlCvFuTzw4UNoKyZdqiNrln+qMF71QJkxy2dsusV8QQdoa89e2TF4dATCzBxbl4zzRzdDoWWyP5ADVrNAH9cRQ== dependencies: - cacache "^16.0.0" - json-parse-even-better-errors "^2.3.1" - pacote "^13.0.3" + "@npmcli/name-from-folder" "^2.0.0" + glob "^9.3.1" + minimatch "^7.4.2" + read-package-json-fast "^3.0.0" + +"@npmcli/metavuln-calculator@^5.0.0": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/metavuln-calculator/-/metavuln-calculator-5.0.1.tgz#426b3e524c2008bcc82dbc2ef390aefedd643d76" + integrity sha512-qb8Q9wIIlEPj3WeA1Lba91R4ZboPL0uspzV0F9uwP+9AYMVB2zOoa7Pbk12g6D2NHAinSbHh6QYmGuRyHZ874Q== + dependencies: + cacache "^17.0.0" + json-parse-even-better-errors "^3.0.0" + pacote "^15.0.0" semver "^7.3.5" "@npmcli/move-file@^1.0.1": @@ -1635,22 +1735,27 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@npmcli/name-from-folder@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz#77ecd0a4fcb772ba6fe927e2e2e155fbec2e6b1a" - integrity sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA== +"@npmcli/name-from-folder@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/name-from-folder/-/name-from-folder-2.0.0.tgz#c44d3a7c6d5c184bb6036f4d5995eee298945815" + integrity sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg== "@npmcli/node-gyp@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz#8c20e53e34e9078d18815c1d2dda6f2420d75e35" integrity sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A== -"@npmcli/package-json@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-2.0.0.tgz#3bbcf4677e21055adbe673d9f08c9f9cde942e4a" - integrity sha512-42jnZ6yl16GzjWSH7vtrmWyJDGVa/LXPdpN2rcUWolFjc9ON2N3uz0qdBbQACfmhuJZ2lbKYtmK5qx68ZPLHMA== +"@npmcli/node-gyp@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz#101b2d0490ef1aa20ed460e4c0813f0db560545a" + integrity sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA== + +"@npmcli/package-json@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-3.0.0.tgz#c9219a197e1be8dbf43c4ef8767a72277c0533b6" + integrity sha512-NnuPuM97xfiCpbTEJYtEuKz6CFbpUHtaT0+5via5pQeI25omvQDFbp1GcGJ/c4zvL/WX0qbde6YiLgfZbWFgvg== dependencies: - json-parse-even-better-errors "^2.3.1" + json-parse-even-better-errors "^3.0.0" "@npmcli/promise-spawn@^3.0.0": version "3.0.0" @@ -1659,6 +1764,20 @@ dependencies: infer-owner "^1.0.4" +"@npmcli/promise-spawn@^6.0.0", "@npmcli/promise-spawn@^6.0.1": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz#c8bc4fa2bd0f01cb979d8798ba038f314cfa70f2" + integrity sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg== + dependencies: + which "^3.0.0" + +"@npmcli/query@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/query/-/query-3.0.0.tgz#51a0dfb85811e04f244171f164b6bc83b36113a7" + integrity sha512-MFNDSJNgsLZIEBVZ0Q9w9K7o07j5N4o4yjtdz2uEpuCZlXGMuPENiRaFYk0vRqAA64qVuUQwC05g27fRtfUgnA== + dependencies: + postcss-selector-parser "^6.0.10" + "@npmcli/run-script@4.1.7": version "4.1.7" resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-4.1.7.tgz#b1a2f57568eb738e45e9ea3123fb054b400a86f7" @@ -1670,7 +1789,7 @@ read-package-json-fast "^2.0.3" which "^2.0.2" -"@npmcli/run-script@^4.1.0", "@npmcli/run-script@^4.1.3": +"@npmcli/run-script@^4.1.0": version "4.2.1" resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-4.2.1.tgz#c07c5c71bc1c70a5f2a06b0d4da976641609b946" integrity sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg== @@ -1681,76 +1800,86 @@ read-package-json-fast "^2.0.3" which "^2.0.2" -"@nrwl/cli@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.8.7.tgz#1de7ba802de24edac64e8cb4cac1459a3f403505" - integrity sha512-G1NEy4jGuZJ/7KjhLQNOe11XmoTgwJS82FW8Tbo4iceq2ItSEbe7bkA8xTSK/AzUixZIMimztb9Oyxw/n1ajGQ== +"@npmcli/run-script@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-6.0.0.tgz#f89e322c729e26ae29db6cc8cc76559074aac208" + integrity sha512-ql+AbRur1TeOdl1FY+RAwGW9fcr4ZwiVKabdvm93mujGREVuVLbdkXRJDrkTXSdCjaxYydr1wlA2v67jxWG5BQ== + dependencies: + "@npmcli/node-gyp" "^3.0.0" + "@npmcli/promise-spawn" "^6.0.0" + node-gyp "^9.0.0" + read-package-json-fast "^3.0.0" + which "^3.0.0" + +"@nrwl/cli@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.9.2.tgz#82537d3d85410b0143d37a3b4fade09675356084" + integrity sha512-QoCmyrcGakHAYTJaNBbOerRQAmqJHMYGCdqtQidV+aP9p1Dy33XxDELfhd+IYmGqngutXuEWChNpWNhPloLnoA== dependencies: - nx "15.8.7" + nx "15.9.2" "@nrwl/devkit@>=15.5.2 < 16": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-15.8.7.tgz#98e881e993c1314a20c050926df1466154782e58" - integrity sha512-A99nZrA5KN9wRn2uYX2vKByA+t2XEGoZBR5TU/bpXbPYrh92qAHkIJ8ke3ImGQOlzk4iIaZ5Me0k7k1p9Zx4wA== + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-15.9.2.tgz#482b89f1bf88d3600b11f8b7e3e4452c5766eca4" + integrity sha512-2DvTstVZb91m+d4wqUJMBHQ3elxyabdmFE6/3aXmtOGeDxTyXyDzf/1O6JvBBiL8K6XC3ZYchjtxUHgxl/NJ5A== dependencies: - "@phenomnomnominal/tsquery" "4.1.1" ejs "^3.1.7" ignore "^5.0.4" semver "7.3.4" tmp "~0.2.1" tslib "^2.3.0" -"@nrwl/nx-darwin-arm64@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.8.7.tgz#1fed566b5206afd710309079644782997ccb7895" - integrity sha512-+cu8J337gRxUHjz2TGwS/2Oh3yw8d3/T6SoBfvee1DY72VQaeYd8UTz0doOhDtmc/zowvRu7ZVsW0ytNB0jIXQ== - -"@nrwl/nx-darwin-x64@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.8.7.tgz#7aaee9f56fa526e7049fa5a9829fa72044b35055" - integrity sha512-VqHJEP0wgFu1MU0Bo1vKZ5/s7ThRfYkX8SyGUxjVTzR02CrsjC4rNxFoKD8Cc4YkUn44U/F78toGf+i2gRcjSQ== - -"@nrwl/nx-linux-arm-gnueabihf@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.8.7.tgz#379a77ea46e0f741c487eeedd3389eafab26dcae" - integrity sha512-4F/8awwqPTt7zKQolvjBNrcR1wYicPjGchLOdaqnfMxn/iRRUdh0hD11mEP5zHNv9gZs/nOIvhdBUErNjFkplQ== - -"@nrwl/nx-linux-arm64-gnu@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.8.7.tgz#201a41c0c8531de94169faa48bd9a49bed04ec4b" - integrity sha512-3ZTSZx02Vv5emQOpaDROIcLtQucoXAe73zGKYDTXB95mxbOPSjjQJ8Rtx+BeqWq9JQoZZyRcD0qnBkTTy1aLRg== - -"@nrwl/nx-linux-arm64-musl@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.8.7.tgz#f6bbf2e7a1941952c25387a36be6cfa88079975d" - integrity sha512-SZxTomiHxAh8El+swbmGSGcaA0vGbHb/rmhFAixo19INu1wBJfD6hjkVJt17h6PyEO7BIYPOpRia6Poxnyv8hA== - -"@nrwl/nx-linux-x64-gnu@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.8.7.tgz#76a88784858224a720c5a28e40ad513704f45722" - integrity sha512-BlNC6Zz1/x6CFbBFTVrgRGMOPqb7zWh5cOjBVNpoBXYTEth1UXb2r1U+gpuQ4xdUqG+uXoWhy6BHJjqBIjzLJA== - -"@nrwl/nx-linux-x64-musl@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.8.7.tgz#dd906423fa129d0c55633ebe80572bdd6be4d57f" - integrity sha512-FNYX/IKy8SUbw6bJpvwZrup2YQBYmSJwP6Rw76Vf7c32XHk7uA6AjiPWMIrZCSndXcry8fnwXvR+J2Dnyo82nQ== - -"@nrwl/nx-win32-arm64-msvc@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.8.7.tgz#145e415950d8ff507dcfbd7879f9c37477e7a620" - integrity sha512-sZALEzazjPAeLlw6IbFWsMidCZ4ZM3GKWZZ6rsAqG2y7I9t4nlUPH/y/Isl9MuLBvrBCBXbVnD20wh6EhtuwTw== - -"@nrwl/nx-win32-x64-msvc@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.8.7.tgz#66aa3cda4b9ae7b676d2282fbac129ce7a3c15d0" - integrity sha512-VMdDptI2rqkLQRCvertF29QeA/V/MnFtHbsmVzMCEv5EUfrkHbA5LLxV66LLfngmkDT1FHktffztlsMpbxvhRw== - -"@nrwl/tao@15.8.7": - version "15.8.7" - resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.8.7.tgz#ea0bd4bc1784a2578dfb7cfb93f42d98504344cb" - integrity sha512-wA7QIEh0VwWcyo32Y/xSCTwnQTGcZupe933nResXv8mAb36W8MoR5SXRx+Wdd8fJ1eWlm2tuotIrslhN+lYx/Q== - dependencies: - nx "15.8.7" +"@nrwl/nx-darwin-arm64@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.9.2.tgz#612d8d714ec876cafd6f1483bf5565704d1b75be" + integrity sha512-Yv+OVsQt3C/hmWOC+YhJZQlsyph5w1BHfbp4jyCvV1ZXBbb8NdvwxgDHPWXxKPTc1EXuB7aEX3qzxM3/OWEUJg== + +"@nrwl/nx-darwin-x64@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.9.2.tgz#3f77bd90dbabf4782d81f773cfb2739a443e595f" + integrity sha512-qHfdluHlPzV0UHOwj1ZJ+qNEhzfLGiBuy1cOth4BSzDlvMnkuqBWoprfaXoztzYcus2NSILY1/7b3Jw4DAWmMw== + +"@nrwl/nx-linux-arm-gnueabihf@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.9.2.tgz#3374a5a1692b222ce18f2213a47b4d68fb509e70" + integrity sha512-0GzwbablosnYnnJDCJvAeZv8LlelSrNwUnGhe43saeoZdAew35Ay1E34zBrg/GCGTASuz+knEEYFM+gDD9Mc6A== + +"@nrwl/nx-linux-arm64-gnu@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.9.2.tgz#e3ec95c6ee3285c77422886cf4cbec1f04804460" + integrity sha512-3mFIY7iUTPG45hSIRaM2DmraCy8W6hNoArAGRrTgYw40BIJHtLrW+Rt7DLyvVXaYCvrKugWOKtxC+jG7kpIZVA== + +"@nrwl/nx-linux-arm64-musl@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.9.2.tgz#72ce601d256083ded7380c598f1b3eb4dc2a3472" + integrity sha512-FNBnXEtockwxZa4I3NqggrJp0YIbNokJvt/clrICP+ijOacdUDkv8mJedavobkFsRsNq9gzCbRbUScKymrOLrg== + +"@nrwl/nx-linux-x64-gnu@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.9.2.tgz#2da6bb50cd80d699310e91c7331baa6cfc8ce197" + integrity sha512-gHWsP5lbe4FNQCa1Q/VLxIuik+BqAOcSzyPjdUa4gCDcbxPa8xiE57PgXB5E1XUzOWNnDTlXa/Ll07/TIuKuog== + +"@nrwl/nx-linux-x64-musl@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.9.2.tgz#39b3bda5868a53b722f1d42700dce71c5ff3f6b9" + integrity sha512-EaFUukCbmoHsYECX2AS4pxXH933yesBFVvBgD38DkoFDxDoJMVt6JqYwm+d5R7S4R2P9U3l++aurljQTRq567Q== + +"@nrwl/nx-win32-arm64-msvc@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.9.2.tgz#bc350be5cb7d0bfa6c2c5ced40c5af163a457a2c" + integrity sha512-PGAe7QMr51ivx1X3avvs8daNlvv1wGo3OFrobjlu5rSyjC1Y3qHwT9+wdlwzNZ93FIqWOq09s+rE5gfZRfpdAg== + +"@nrwl/nx-win32-x64-msvc@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.9.2.tgz#3e46c3f7af196bdbf0deb336ec4f9448c54e4a9f" + integrity sha512-Q8onNzhuAZ0l9DNkm8D4Z1AEIzJr8JiT4L2fVBLYrV/R75C2HS3q7lzvfo6oqMY6mXge1cFPcrTtg3YXBQaSWA== + +"@nrwl/tao@15.9.2": + version "15.9.2" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.9.2.tgz#e970efa8b3fb828007b02286e9e505247032b5b3" + integrity sha512-+LqNC37w9c6q6Ukdpf0z0tt1PQFNi4gwhHpJvkYQiKRETHjyrrlyqTNEPEyA7PI62RuYC6VrpVw2gzI7ufqZEA== + dependencies: + nx "15.9.2" "@octokit/auth-token@^3.0.0": version "3.0.3" @@ -1890,7 +2019,7 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -"@peculiar/asn1-schema@^2.1.6", "@peculiar/asn1-schema@^2.3.0": +"@peculiar/asn1-schema@^2.3.6": version "2.3.6" resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz#3dd3c2ade7f702a9a94dfb395c192f5fa5d6b922" integrity sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA== @@ -1907,22 +2036,15 @@ tslib "^2.0.0" "@peculiar/webcrypto@^1.0.22": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.1.tgz#821493bd5ad0f05939bd5f53b28536f68158360a" - integrity sha512-eK4C6WTNYxoI7JOabMoZICiyqRRtJB220bh0Mbj5RwRycleZf9BPyZoxsTvpP0FpmVS2aS13NKOuh5/tN3sIRw== + version "1.4.3" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.3.tgz#078b3e8f598e847b78683dc3ba65feb5029b93a7" + integrity sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A== dependencies: - "@peculiar/asn1-schema" "^2.3.0" + "@peculiar/asn1-schema" "^2.3.6" "@peculiar/json-schema" "^1.1.12" pvtsutils "^1.3.2" - tslib "^2.4.1" - webcrypto-core "^1.7.4" - -"@phenomnomnominal/tsquery@4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@phenomnomnominal/tsquery/-/tsquery-4.1.1.tgz#42971b83590e9d853d024ddb04a18085a36518df" - integrity sha512-jjMmK1tnZbm1Jq5a7fBliM4gQwjxMU7TFoRNwIyzwlO+eHPRCFv/Nv+H/Gi1jc3WR7QURG8D5d0Tn12YGrUqBQ== - dependencies: - esquery "^1.0.1" + tslib "^2.5.0" + webcrypto-core "^1.7.7" "@pkgr/utils@^2.3.1": version "2.3.1" @@ -2018,10 +2140,10 @@ dependencies: serve-static "^1.13.1" -"@react-native-community/cli-doctor@^10.2.0": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-10.2.1.tgz#b6b7a3f0f9cef1a05f1adc6393eb29c6f8f2972c" - integrity sha512-IwhdSD+mtgWdxg2eMr0fpkn08XN7r70DC1riGSmqK/DXNyWBzIZlCkDN+/TwlaUEsiFk6LQTjgCiqZSMpmDrsg== +"@react-native-community/cli-doctor@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-10.2.2.tgz#b1893604fa9fc8971064e7c00042350f96868bfe" + integrity sha512-49Ep2aQOF0PkbAR/TcyMjOm9XwBa8VQr+/Zzf4SJeYwiYLCT1NZRAVAVjYRXl0xqvq5S5mAGZZShS4AQl4WsZw== dependencies: "@react-native-community/cli-config" "^10.1.1" "@react-native-community/cli-platform-ios" "^10.2.1" @@ -2062,19 +2184,7 @@ glob "^7.1.3" logkitty "^0.7.1" -"@react-native-community/cli-platform-ios@10.2.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.0.tgz#be21c0e3bbf17358d540cc23e5556bf679f6322e" - integrity sha512-hIPK3iL/mL+0ChXmQ9uqqzNOKA48H+TAzg+hrxQLll/6dNMxDeK9/wZpktcsh8w+CyhqzKqVernGcQs7tPeKGw== - dependencies: - "@react-native-community/cli-tools" "^10.1.1" - chalk "^4.1.2" - execa "^1.0.0" - fast-xml-parser "^4.0.12" - glob "^7.1.3" - ora "^5.4.1" - -"@react-native-community/cli-platform-ios@^10.2.1": +"@react-native-community/cli-platform-ios@10.2.1", "@react-native-community/cli-platform-ios@^10.2.1": version "10.2.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-10.2.1.tgz#2e6bd2cb6d48cbb8720d7b7265bb1bab80745f72" integrity sha512-hz4zu4Y6eyj7D0lnZx8Mf2c2si8y+zh/zUTgCTaPPLzQD8jSZNNBtUUiA1cARm2razpe8marCZ1QbTMAGbf3mg== @@ -2086,21 +2196,21 @@ glob "^7.1.3" ora "^5.4.1" -"@react-native-community/cli-plugin-metro@^10.2.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-10.2.0.tgz#83cabbc04c80f7e94f88ed998b72c7d572c6f094" - integrity sha512-9eiJrKYuauEDkQLCrjJUh7tS9T0oaMQqVUSSSuyDG6du7HQcfaR4mSf21wK75jvhKiwcQLpsFmMdctAb+0v+Cg== +"@react-native-community/cli-plugin-metro@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-10.2.2.tgz#766914e3c8007dfe52b253544c4f6cd8549919ac" + integrity sha512-sTGjZlD3OGqbF9v1ajwUIXhGmjw9NyJ/14Lo0sg7xH8Pv4qUd5ZvQ6+DWYrQn3IKFUMfGFWYyL81ovLuPylrpw== dependencies: "@react-native-community/cli-server-api" "^10.1.1" "@react-native-community/cli-tools" "^10.1.1" chalk "^4.1.2" execa "^1.0.0" - metro "0.73.8" - metro-config "0.73.8" - metro-core "0.73.8" - metro-react-native-babel-transformer "0.73.8" - metro-resolver "0.73.8" - metro-runtime "0.73.8" + metro "0.73.9" + metro-config "0.73.9" + metro-core "0.73.9" + metro-react-native-babel-transformer "0.73.9" + metro-resolver "0.73.9" + metro-runtime "0.73.9" readline "^1.3.0" "@react-native-community/cli-server-api@^10.1.1": @@ -2140,17 +2250,17 @@ dependencies: joi "^17.2.1" -"@react-native-community/cli@10.2.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-10.2.0.tgz#bcb65bb3dcb03b0fc4e49619d51e12d23396b301" - integrity sha512-QH7AFBz5FX2zTZRH/o3XehHrZ0aZZEL5Sh+23nSEFgSj3bLFfvjjZhuoiRSAo7iiBdvAoXrfxQ8TXgg4Xf/7fw== +"@react-native-community/cli@10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-10.2.2.tgz#3fa438ba7f19f83e07bc337765fc1cabdcf2cac2" + integrity sha512-aZVcVIqj+OG6CrliR/Yn8wHxrvyzbFBY9cj7n0MvRw/P54QUru2nNqUTSSbqv0Qaa297yHJbe6kFDojDMSTM8Q== dependencies: "@react-native-community/cli-clean" "^10.1.1" "@react-native-community/cli-config" "^10.1.1" "@react-native-community/cli-debugger-ui" "^10.0.0" - "@react-native-community/cli-doctor" "^10.2.0" + "@react-native-community/cli-doctor" "^10.2.2" "@react-native-community/cli-hermes" "^10.2.0" - "@react-native-community/cli-plugin-metro" "^10.2.0" + "@react-native-community/cli-plugin-metro" "^10.2.2" "@react-native-community/cli-server-api" "^10.1.1" "@react-native-community/cli-tools" "^10.1.1" "@react-native-community/cli-types" "^10.0.0" @@ -2195,6 +2305,11 @@ resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== +"@sigstore/protobuf-specs@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz#957cb64ea2f5ce527cc9cf02a096baeb0d2b99b4" + integrity sha512-a31EnjuIDSX8IXBUib3cYLDRlPMU36AWX4xS8ysLaNu4ZzUesDiPt83pgrW2X1YLMe5L2HbDyaKK5BrL4cNKaQ== + "@sinclair/typebox@^0.25.16": version "0.25.24" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" @@ -2410,6 +2525,19 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== +"@tufjs/canonical-json@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz#eade9fd1f537993bc1f0949f3aea276ecc4fab31" + integrity sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ== + +"@tufjs/models@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tufjs/models/-/models-1.0.3.tgz#e6cb8a86834da7459a7c836cd892dee56b4bab44" + integrity sha512-mkFEqqRisi13DmR5pX4x+Zk97EiU8djTtpNW1GeuX410y/raAsq/T3ZCjwoRIZ8/cIBfW0olK/sywlAiWevDVw== + dependencies: + "@tufjs/canonical-json" "1.0.0" + minimatch "^7.4.6" + "@types/babel__core@^7.1.14": version "7.20.0" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" @@ -2473,17 +2601,17 @@ "@types/node" "*" "@types/eslint@^8.21.2": - version "8.21.2" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.21.2.tgz#2b61b43a8b0e66006856a2a4c8e51f6f773ead27" - integrity sha512-EMpxUyystd3uZVByZap1DACsMXvb82ypQnGn89e1Y0a+LYu3JJscUd/gqhRsVFDkaD2MIiWo0MT8EfXr3DGRKw== + version "8.37.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.37.0.tgz#29cebc6c2a3ac7fea7113207bf5a828fdf4d7ef1" + integrity sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ== dependencies: "@types/estree" "*" "@types/json-schema" "*" "@types/estree@*": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" - integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" + integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== "@types/events@^3.0.0": version "3.0.0" @@ -2588,9 +2716,9 @@ integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== "@types/luxon@^3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.2.0.tgz#99901b4ab29a5fdffc88fff59b3b47fbfbe0557b" - integrity sha512-lGmaGFoaXHuOLXFvuju2bfvZRqxAqkHPx9Y9IQdQABrinJJshJwfNCKV+u7rR3kJbiqfTF/NhOkcxxAFrObyaA== + version "3.3.0" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.0.tgz#a61043a62c0a72696c73a0a305c544c96501e006" + integrity sha512-uKRI5QORDnrGFYgcdAVnHvEIvEZ8noTpP/Bg+HeUzZghwinDlIS87DEenV5r1YoOF9G4x600YsUXLWZ19rmTmg== "@types/mime@*": version "3.0.1" @@ -2607,7 +2735,7 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== -"@types/node-fetch@^2.5.10": +"@types/node-fetch@2.6.2": version "2.6.2" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== @@ -2616,9 +2744,9 @@ form-data "^3.0.0" "@types/node@*", "@types/node@>=13.7.0", "@types/node@^16.11.7": - version "16.18.16" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.16.tgz#09ff98b144abae2d7cce3e9fe9040ab2bf73222c" - integrity sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA== + version "16.18.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.23.tgz#b6e934fe427eb7081d0015aad070acb3373c3c90" + integrity sha512-XAMpaw1s1+6zM+jn2tmw8MyaRDIJfXxqmIQIS0HfoGYPuf7dUWeiUKopwq13KFX9lEp1+THGtlaaYx39Nxr58g== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2702,9 +2830,9 @@ integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== "@types/validator@^13.7.10": - version "13.7.14" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.14.tgz#5512aef43ba353ea2fe2d0d8c7ce71c75c2ad9e6" - integrity sha512-J6OAed6rhN6zyqL9Of6ZMamhlsOEU/poBVvbHr/dKOYKTeuYYMlDkMv+b6UUV0o2i0tw73cgyv/97WTWaUl0/g== + version "13.7.15" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.15.tgz#408c99d1b5f0eecc78109c11f896f72d1f026a10" + integrity sha512-yeinDVQunb03AEP8luErFcyf/7Lf7AzKCD0NXfgVoGCCQDNpZET8Jgq74oBgqKld3hafLbfzt/3inUdQvaFeXQ== "@types/varint@^6.0.0": version "6.0.1" @@ -2740,21 +2868,21 @@ "@types/yargs-parser" "*" "@types/yargs@^17.0.8": - version "17.0.22" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a" - integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.48.1": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz#bc2400c3a23305e8c9a9c04aa40933868aaaeb47" - integrity sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg== + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.0.tgz#c0e10eeb936debe5d1c3433cf36206a95befefd0" + integrity sha512-p0QgrEyrxAWBecR56gyn3wkG15TJdI//eetInP3zYRewDh0XS+DhB3VUAd3QqvziFsfaQIoIuZMxZRB7vXYaYw== dependencies: "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.55.0" - "@typescript-eslint/type-utils" "5.55.0" - "@typescript-eslint/utils" "5.55.0" + "@typescript-eslint/scope-manager" "5.59.0" + "@typescript-eslint/type-utils" "5.59.0" + "@typescript-eslint/utils" "5.59.0" debug "^4.3.4" grapheme-splitter "^1.0.4" ignore "^5.2.0" @@ -2763,71 +2891,71 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.48.1": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.55.0.tgz#8c96a0b6529708ace1dcfa60f5e6aec0f5ed2262" - integrity sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw== + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.0.tgz#0ad7cd019346cc5d150363f64869eca10ca9977c" + integrity sha512-qK9TZ70eJtjojSUMrrEwA9ZDQ4N0e/AuoOIgXuNBorXYcBDk397D2r5MIe1B3cok/oCtdNC5j+lUUpVB+Dpb+w== dependencies: - "@typescript-eslint/scope-manager" "5.55.0" - "@typescript-eslint/types" "5.55.0" - "@typescript-eslint/typescript-estree" "5.55.0" + "@typescript-eslint/scope-manager" "5.59.0" + "@typescript-eslint/types" "5.59.0" + "@typescript-eslint/typescript-estree" "5.59.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.55.0": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz#e863bab4d4183ddce79967fe10ceb6c829791210" - integrity sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw== +"@typescript-eslint/scope-manager@5.59.0": + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.0.tgz#86501d7a17885710b6716a23be2e93fc54a4fe8c" + integrity sha512-tsoldKaMh7izN6BvkK6zRMINj4Z2d6gGhO2UsI8zGZY3XhLq1DndP3Ycjhi1JwdwPRwtLMW4EFPgpuKhbCGOvQ== dependencies: - "@typescript-eslint/types" "5.55.0" - "@typescript-eslint/visitor-keys" "5.55.0" + "@typescript-eslint/types" "5.59.0" + "@typescript-eslint/visitor-keys" "5.59.0" -"@typescript-eslint/type-utils@5.55.0": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz#74bf0233523f874738677bb73cb58094210e01e9" - integrity sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA== +"@typescript-eslint/type-utils@5.59.0": + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.0.tgz#8e8d1420fc2265989fa3a0d897bde37f3851e8c9" + integrity sha512-d/B6VSWnZwu70kcKQSCqjcXpVH+7ABKH8P1KNn4K7j5PXXuycZTPXF44Nui0TEm6rbWGi8kc78xRgOC4n7xFgA== dependencies: - "@typescript-eslint/typescript-estree" "5.55.0" - "@typescript-eslint/utils" "5.55.0" + "@typescript-eslint/typescript-estree" "5.59.0" + "@typescript-eslint/utils" "5.59.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.55.0": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.55.0.tgz#9830f8d3bcbecf59d12f821e5bc6960baaed41fd" - integrity sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug== +"@typescript-eslint/types@5.59.0": + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.0.tgz#3fcdac7dbf923ec5251545acdd9f1d42d7c4fe32" + integrity sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA== -"@typescript-eslint/typescript-estree@5.55.0": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz#8db7c8e47ecc03d49b05362b8db6f1345ee7b575" - integrity sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ== +"@typescript-eslint/typescript-estree@5.59.0": + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.0.tgz#8869156ee1dcfc5a95be3ed0e2809969ea28e965" + integrity sha512-sUNnktjmI8DyGzPdZ8dRwW741zopGxltGs/SAPgGL/AAgDpiLsCFLcMNSpbfXfmnNeHmK9h3wGmCkGRGAoUZAg== dependencies: - "@typescript-eslint/types" "5.55.0" - "@typescript-eslint/visitor-keys" "5.55.0" + "@typescript-eslint/types" "5.59.0" + "@typescript-eslint/visitor-keys" "5.59.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.55.0": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.55.0.tgz#34e97322e7ae5b901e7a870aabb01dad90023341" - integrity sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw== +"@typescript-eslint/utils@5.59.0": + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.0.tgz#063d066b3bc4850c18872649ed0da9ee72d833d5" + integrity sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.55.0" - "@typescript-eslint/types" "5.55.0" - "@typescript-eslint/typescript-estree" "5.55.0" + "@typescript-eslint/scope-manager" "5.59.0" + "@typescript-eslint/types" "5.59.0" + "@typescript-eslint/typescript-estree" "5.59.0" eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.55.0": - version "5.55.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz#01ad414fca8367706d76cdb94adf788dc5b664a2" - integrity sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw== +"@typescript-eslint/visitor-keys@5.59.0": + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.0.tgz#a59913f2bf0baeb61b5cfcb6135d3926c3854365" + integrity sha512-qZ3iXxQhanchCeaExlKPV3gDQFxMUmU35xfd5eCXB6+kUw1TUAbIy2n7QIrwz9s98DQLzNWyHp61fY0da4ZcbA== dependencies: - "@typescript-eslint/types" "5.55.0" + "@typescript-eslint/types" "5.59.0" eslint-visitor-keys "^3.3.0" "@unimodules/core@*": @@ -2851,9 +2979,9 @@ integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== "@yarnpkg/parsers@^3.0.0-rc.18": - version "3.0.0-rc.40" - resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.40.tgz#972af4bb01d797ad20e12de8126ea2276ab8fdea" - integrity sha512-sKbi5XhHKXCjzb5m0ftGuQuODM2iUXEsrCSl8MkKexNWHepCmU3IPaGTPC5gHZy4sOvsb9JqTLaZEez+kDzG+Q== + version "3.0.0-rc.42" + resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.42.tgz#3814e90a81bb1f9c06cc83c6a009139c55efe94d" + integrity sha512-eW9Mbegmb5bJjwawJM9ghjUjUqciNMhC6L7XrQPF/clXS5bbP66MstsgCT5hy9VlfUh/CfBT+0Wucf531dMjHA== dependencies: js-yaml "^3.10.0" tslib "^2.4.0" @@ -2878,6 +3006,11 @@ abbrev@1, abbrev@^1.0.0: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -3051,6 +3184,14 @@ are-we-there-yet@^3.0.0: delegates "^1.0.0" readable-stream "^3.6.0" +are-we-there-yet@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-4.0.0.tgz#3ff397dc14f08b52dd8b2a64d3cee154ab8760d2" + integrity sha512-nSXlV+u3vtVjRgihdTzbfWYzxPWGo424zPgQbHD0ZqIla3jqYAewDcvee0Ua2hjS5IfTAmjGlx1Jf0PKwjZDEw== + dependencies: + delegates "^1.0.0" + readable-stream "^4.1.0" + are-we-there-yet@~1.1.2: version "1.1.7" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" @@ -3183,7 +3324,7 @@ arrify@^2.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== -asap@^2.0.0, asap@~2.0.6: +asap@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== @@ -3264,9 +3405,9 @@ axios@^0.21.2: follow-redirects "^1.14.0" axios@^1.0.0: - version "1.3.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024" - integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ== + version "1.3.5" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.5.tgz#e07209b39a0d11848e3e341fa087acd71dadc542" + integrity sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -3468,17 +3609,15 @@ bignumber.js@^9.0.0: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== -bin-links@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-3.0.3.tgz#3842711ef3db2cd9f16a5f404a996a12db355a6e" - integrity sha512-zKdnMPWEdh4F5INR07/eBrodC7QrF5JKvqskjz/ZZRXg5YSAZIbn8zGhbhUrElzHBZ2fvEQdOU59RHcTG3GiwA== +bin-links@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-4.0.1.tgz#afeb0549e642f61ff889b58ea2f8dca78fb9d8d3" + integrity sha512-bmFEM39CyX336ZGGRsGPlc6jZHriIoHacOQcTt72MktIjpPhZoP4te2jOyUXF3BLILmJ8aNLncoPVeIIFlrDeA== dependencies: - cmd-shim "^5.0.0" - mkdirp-infer-owner "^2.0.0" - npm-normalize-package-bin "^2.0.0" - read-cmd-shim "^3.0.0" - rimraf "^3.0.0" - write-file-atomic "^4.0.0" + cmd-shim "^6.0.0" + npm-normalize-package-bin "^3.0.0" + read-cmd-shim "^4.0.0" + write-file-atomic "^5.0.0" bindings@^1.3.1: version "1.5.0" @@ -3683,7 +3822,7 @@ cacache@^15.2.0: tar "^6.0.2" unique-filename "^1.1.1" -cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: +cacache@^16.0.0, cacache@^16.1.0: version "16.1.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== @@ -3707,6 +3846,25 @@ cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: tar "^6.1.11" unique-filename "^2.0.0" +cacache@^17.0.0, cacache@^17.0.4: + version "17.0.5" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.0.5.tgz#6dbec26c11f1f6a2b558bc11ed3316577c339ebc" + integrity sha512-Y/PRQevNSsjAPWykl9aeGz8Pr+OI6BYM9fYDNMvOkuUiG9IhG4LEmaYrZZZvioMUEQ+cBCxT0v8wrnCURccyKA== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^9.3.1" + lru-cache "^7.7.1" + minipass "^4.0.0" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + p-map "^4.0.0" + promise-inflight "^1.0.1" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -3774,9 +3932,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001449: - version "1.0.30001467" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001467.tgz#1afc9c16ed61f50dd87139da87ca43a3e0051c77" - integrity sha512-cEdN/5e+RPikvl9AHm4uuLXxeCNq8rFsQ+lPHTfe/OtypP3WwnVVbjn+6uBV7PaFL6xUFzTh+sSCOz1rKhcO+Q== + version "1.0.30001480" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz#9bbd35ee44c2480a1e3a3b9f4496f5066817164a" + integrity sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ== canonicalize@^1.0.1: version "1.0.8" @@ -3895,9 +4053,9 @@ cli-spinners@2.6.1: integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== cli-spinners@^2.5.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" - integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== + version "2.8.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.8.0.tgz#e97a3e2bd00e6d85aa0c13d7f9e3ce236f7787fc" + integrity sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ== cli-width@^3.0.0: version "3.0.0" @@ -3950,13 +4108,18 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -cmd-shim@5.0.0, cmd-shim@^5.0.0: +cmd-shim@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-5.0.0.tgz#8d0aaa1a6b0708630694c4dbde070ed94c707724" integrity sha512-qkCtZ59BidfEwHltnJwkyVZn+XQojdAySM1D1gSeh11Z4pW1Kpolkyo53L5noc0nrxmIvyFwTmJRo4xs7FFLPw== dependencies: mkdirp-infer-owner "^2.0.0" +cmd-shim@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.1.tgz#a65878080548e1dca760b3aea1e21ed05194da9d" + integrity sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -4296,9 +4459,9 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.25.1: - version "3.29.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.1.tgz#15c0fb812ea27c973c18d425099afa50b934b41b" - integrity sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA== + version "3.30.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.1.tgz#961541e22db9c27fc48bfc13a3cafa8734171dfe" + integrity sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw== dependencies: browserslist "^4.21.5" @@ -4381,6 +4544,16 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -4430,11 +4603,6 @@ debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" -debuglog@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" - integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== - decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -4520,6 +4688,20 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +del@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" + integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== + dependencies: + globby "^11.0.1" + graceful-fs "^4.2.4" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.2" + p-map "^4.0.0" + rimraf "^3.0.2" + slash "^3.0.0" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -4579,14 +4761,6 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -dezalgo@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" - integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== - dependencies: - asap "^2.0.0" - wrappy "1" - did-jwt@^6.11.6: version "6.11.6" resolved "https://registry.yarnpkg.com/did-jwt/-/did-jwt-6.11.6.tgz#3eeb30d6bd01f33bfa17089574915845802a7d44" @@ -4678,9 +4852,9 @@ ejs@^3.1.7: jake "^10.8.5" electron-to-chromium@^1.4.284: - version "1.4.333" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.333.tgz#ebb21f860f8a29923717b06ec0cb54e77ed34c04" - integrity sha512-YyE8+GKyGtPEP1/kpvqsdhD6rA/TP1DUFDN4uiU/YI52NzDxmwHkEb3qjId8hLBa5siJvG0sfC3O66501jMruQ== + version "1.4.368" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.368.tgz#75901f97d3e23da2e66feb1e61fbb8e70ac96430" + integrity sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw== elliptic@^6.5.4: version "6.5.4" @@ -4724,7 +4898,7 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enhanced-resolve@^5.10.0: +enhanced-resolve@^5.12.0: version "5.12.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== @@ -4893,9 +5067,9 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-prettier@^8.3.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz#f1cc58a8afebc50980bd53475451df146c13182d" - integrity sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA== + version "8.8.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" + integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== eslint-import-resolver-node@^0.3.7: version "0.3.7" @@ -4907,22 +5081,23 @@ eslint-import-resolver-node@^0.3.7: resolve "^1.22.1" eslint-import-resolver-typescript@^3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.3.tgz#db5ed9e906651b7a59dd84870aaef0e78c663a05" - integrity sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ== + version "3.5.5" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.5.tgz#0a9034ae7ed94b254a360fbea89187b60ea7456d" + integrity sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw== dependencies: debug "^4.3.4" - enhanced-resolve "^5.10.0" - get-tsconfig "^4.2.0" - globby "^13.1.2" - is-core-module "^2.10.0" + enhanced-resolve "^5.12.0" + eslint-module-utils "^2.7.4" + get-tsconfig "^4.5.0" + globby "^13.1.3" + is-core-module "^2.11.0" is-glob "^4.0.3" - synckit "^0.8.4" + synckit "^0.8.5" eslint-module-utils@^2.7.4: - version "2.7.4" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" - integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== dependencies: debug "^3.2.7" @@ -4970,27 +5145,27 @@ eslint-scope@^5.1.1: estraverse "^4.1.1" eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + version "7.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" + integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" + integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== eslint@^8.36.0: - version "8.36.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.36.0.tgz#1bd72202200a5492f91803b113fb8a83b11285cf" - integrity sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw== + version "8.38.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.38.0.tgz#a62c6f36e548a5574dd35728ac3c6209bd1e2f1a" + integrity sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.1" - "@eslint/js" "8.36.0" + "@eslint/eslintrc" "^2.0.2" + "@eslint/js" "8.38.0" "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -5001,8 +5176,8 @@ eslint@^8.36.0: doctrine "^3.0.0" escape-string-regexp "^4.0.0" eslint-scope "^7.1.1" - eslint-visitor-keys "^3.3.0" - espree "^9.5.0" + eslint-visitor-keys "^3.4.0" + espree "^9.5.1" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -5028,21 +5203,21 @@ eslint@^8.36.0: strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^9.5.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113" - integrity sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw== +espree@^9.5.1: + version "9.5.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4" + integrity sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg== dependencies: acorn "^8.8.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.0" esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.0.1, esquery@^1.4.2: +esquery@^1.4.2: version "1.5.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== @@ -5316,9 +5491,9 @@ fast-text-encoding@^1.0.3: integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== fast-xml-parser@^4.0.12: - version "4.1.3" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.1.3.tgz#0254ad0d4d27f07e6b48254b068c0c137488dd97" - integrity sha512-LsNDahCiCcJPe8NO7HijcnukHB24tKbfDDA5IILx9dmW3Frb52lhbeX6MPNUSvyGNfav2VTYpJ/OqkRoVLrh2Q== + version "4.2.2" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.2.tgz#cb7310d1e9cf42d22c687b0fae41f3c926629368" + integrity sha512-DLzIPtQqmvmdq3VUKR7T6omPK/VCRNqgFlGtbESfyhcH2R4I8EzK1/K6E8PkRCK2EabWrUHK32NjYRbEFnnz0Q== dependencies: strnum "^1.0.5" @@ -5354,9 +5529,9 @@ ffi-napi@4.0.3, ffi-napi@^4.0.3: ref-struct-di "^1.1.0" figlet@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.5.2.tgz#dda34ff233c9a48e36fcff6741aeb5bafe49b634" - integrity sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ== + version "1.6.0" + resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.6.0.tgz#812050fa9f01043b4d44ddeb11f20fb268fa4b93" + integrity sha512-31EQGhCEITv6+hi2ORRPyn3bulaV9Fl4xOdR169cBzH/n1UqcxsiSB/noo6SJdD7Kfb1Ljit+IgR1USvF/XbdA== figures@3.2.0, figures@^3.0.0: version "3.2.0" @@ -5377,6 +5552,11 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +file-url@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/file-url/-/file-url-3.0.0.tgz#247a586a746ce9f7a8ed05560290968afc262a77" + integrity sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA== + filelist@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" @@ -5448,6 +5628,14 @@ find-replace@^3.0.0: dependencies: array-back "^3.0.1" +find-up@5.0.0, find-up@^5.0.0, find-up@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + find-up@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" @@ -5470,14 +5658,6 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -find-up@^5.0.0, find-up@~5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - find-workspaces@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/find-workspaces/-/find-workspaces-0.1.0.tgz#c01ddc81a1814b2c18927b26adb82afc97b63cea" @@ -5506,9 +5686,9 @@ flatted@^3.1.0: integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.202.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.202.0.tgz#534178266d3ceec5368415e59990db97eece5bd0" - integrity sha512-ZiXxSIXK3zPmY3zrzCofFonM2T+/3Jz5QZKJyPVtUERQEJUnYkXBQ+0H3FzyqiyJs+VXqb/UNU6/K6sziVYdxw== + version "0.204.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.204.0.tgz#48515c3d289557d465b409c60ebdf4e783af491e" + integrity sha512-cQhNPLOk5NFyDXBC8WE8dy2Gls+YqKI3FNqQbJ7UrbFyd30IdEX3t27u3VsnoVK22I872+PWeb1KhHxDgu7kAg== flow-parser@^0.185.0: version "0.185.2" @@ -5583,9 +5763,9 @@ fs-extra@9.1.0, fs-extra@^9.1.0: universalify "^2.0.0" fs-extra@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" - integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -5614,6 +5794,13 @@ fs-minipass@^2.0.0, fs-minipass@^2.1.0: dependencies: minipass "^3.0.0" +fs-minipass@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.1.tgz#853809af15b6d03e27638d1ab6432e6b378b085d" + integrity sha512-MhaJDcFRTuLidHrIttu0RDGyyXs/IYHVmlcxfLAEFIWjc1vdLAkdwT7Ace2u7DbitWC0toKMl5eJZRYNVreIMw== + dependencies: + minipass "^4.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -5659,10 +5846,24 @@ gauge@^3.0.0: strip-ansi "^6.0.1" wide-align "^1.1.2" -gauge@^4.0.3: - version "4.0.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" - integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== +gauge@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" + integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.3" + console-control-strings "^1.1.0" + has-unicode "^2.0.1" + signal-exit "^3.0.7" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.5" + +gauge@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-5.0.0.tgz#e270ca9d97dae84abf64e5277ef1ebddc7dd1e2f" + integrity sha512-0s5T5eciEG7Q3ugkxAkFtaDhrrhXsCRivA5y8C9WMHWuI8UlMOJg7+Iwf7Mccii+Dfs3H5jHepU0joPVyQU0Lw== dependencies: aproba "^1.0.3 || ^2.0.0" color-support "^1.1.3" @@ -5756,10 +5957,10 @@ get-symbol-from-current-process-h@^1.0.1, get-symbol-from-current-process-h@^1.0 resolved "https://registry.yarnpkg.com/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.2.tgz#510af52eaef873f7028854c3377f47f7bb200265" integrity sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw== -get-tsconfig@^4.2.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.4.0.tgz#64eee64596668a81b8fce18403f94f245ee0d4e5" - integrity sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ== +get-tsconfig@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.5.0.tgz#6d52d1c7b299bd3ee9cd7638561653399ac77b0f" + integrity sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ== get-uv-event-loop-napi-h@^1.0.5: version "1.0.6" @@ -5878,13 +6079,13 @@ glob@^8.0.1: minimatch "^5.0.1" once "^1.3.0" -glob@^9.2.0: - version "9.3.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.0.tgz#be6e50d172d025c3fcf87903ae25b36b787c0bb0" - integrity sha512-EAZejC7JvnQINayvB/7BJbpZpNOJ8Lrw2OZNEvQxe0vaLn1SuwMcfV7/MNaX8L/T0wmptBFI4YMtDvSBxYDc7w== +glob@^9.2.0, glob@^9.3.0, glob@^9.3.1: + version "9.3.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" + integrity sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q== dependencies: fs.realpath "^1.0.0" - minimatch "^7.4.1" + minimatch "^8.0.2" minipass "^4.2.4" path-scurry "^1.6.1" @@ -5912,7 +6113,7 @@ globalyzer@0.1.0: resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== -globby@11.1.0, globby@^11.1.0: +globby@11.1.0, globby@^11.0.1, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -5924,10 +6125,10 @@ globby@11.1.0, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^13.1.2: - version "13.1.3" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.3.tgz#f62baf5720bcb2c1330c8d4ef222ee12318563ff" - integrity sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw== +globby@^13.1.3: + version "13.1.4" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.4.tgz#2f91c116066bcec152465ba36e5caa4a13c01317" + integrity sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g== dependencies: dir-glob "^3.0.1" fast-glob "^3.2.11" @@ -6123,12 +6324,19 @@ hosted-git-info@^5.0.0: dependencies: lru-cache "^7.5.1" +hosted-git-info@^6.0.0, hosted-git-info@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-6.1.1.tgz#629442c7889a69c05de604d52996b74fe6f26d58" + integrity sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w== + dependencies: + lru-cache "^7.5.1" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-cache-semantics@^4.1.0: +http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== @@ -6215,6 +6423,13 @@ ignore-walk@^5.0.1: dependencies: minimatch "^5.0.1" +ignore-walk@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.2.tgz#c48f48397cf8ef6174fcc28aa5f8c1de6203d389" + integrity sha512-ezmQ1Dg2b3jVZh2Dh+ar6Eu2MqNSTkyb32HU2MAQQQX9tKM3q/UQ/9lf03lQ5hW+fOeoMnwxwkleZ0xcNp0/qg== + dependencies: + minimatch "^7.4.2" + ignore@^5.0.4, ignore@^5.2.0: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" @@ -6309,6 +6524,27 @@ init-package-json@3.0.2, init-package-json@^3.0.2: validate-npm-package-license "^3.0.4" validate-npm-package-name "^4.0.0" +inquirer@8.2.4: + version "8.2.4" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4" + integrity sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.5.5" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + wrap-ansi "^7.0.0" + inquirer@^7.3.3: version "7.3.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" @@ -6440,10 +6676,10 @@ is-ci@2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.10.0, is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== +is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.8.1: + version "2.12.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" + integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== dependencies: has "^1.0.3" @@ -6581,7 +6817,12 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-inside@^3.0.3: +is-path-cwd@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-inside@^3.0.2, is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== @@ -7216,9 +7457,9 @@ jest@^29.5.0: jest-cli "^29.5.0" joi@^17.2.1: - version "17.8.4" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.8.4.tgz#f2d91ab8acd3cca4079ba70669c65891739234aa" - integrity sha512-jjdRHb5WtL+KgSHvOULQEPPv4kcl+ixd1ybOFQq3rWLgEEqc03QMmilodL0GVJE14U/SQDXkUhQUSZANGDH/AA== + version "17.9.1" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.1.tgz#74899b9fa3646904afa984a11df648eca66c9018" + integrity sha512-FariIi9j6QODKATGBrEX7HZcja8Bsh3rfdGYy/Sb65sGlZWK/QWesU1ghk7aJWDj95knjXlQfSmzFSPPkLVsfw== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -7227,9 +7468,9 @@ joi@^17.2.1: "@sideway/pinpoint" "^2.0.0" js-sdsl@^4.1.4: - version "4.3.0" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" - integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" + integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg== js-sha3@^0.8.0: version "0.8.0" @@ -7306,6 +7547,11 @@ json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-parse-even-better-errors@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz#2cb2ee33069a78870a0c7e3da560026b89669cf7" + integrity sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -7376,10 +7622,10 @@ just-diff-apply@^5.2.0: resolved "https://registry.yarnpkg.com/just-diff-apply/-/just-diff-apply-5.5.0.tgz#771c2ca9fa69f3d2b54e7c3f5c1dfcbcc47f9f0f" integrity sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw== -just-diff@^5.0.1: - version "5.2.0" - resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.2.0.tgz#60dca55891cf24cd4a094e33504660692348a241" - integrity sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw== +just-diff@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-6.0.2.tgz#03b65908543ac0521caf6d8eb85035f7d27ea285" + integrity sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA== jwt-decode@^3.1.2: version "3.1.2" @@ -7429,13 +7675,14 @@ ky@^0.25.1: integrity sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA== lerna@^6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-6.5.1.tgz#eb89698e5b2891f5681f39d980f63d0519fc464f" - integrity sha512-Va1bysubwWdoWZ1ncKcoTGBXNAu/10/TwELb550TTivXmEWjCCdek4eX0BNLTEYKxu3tpV2UEeqVisUiWGn4WA== - dependencies: - "@lerna/child-process" "6.5.1" - "@lerna/create" "6.5.1" - "@npmcli/arborist" "5.3.0" + version "6.6.1" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-6.6.1.tgz#4897171aed64e244a2d0f9000eef5c5b228f9332" + integrity sha512-WJtrvmbmR+6hMB9b5pvsxJzew0lRL6hARgW/My9BM4vYaxwPIA2I0riv3qQu5Zd7lYse7FEqJkTnl9Kn1bXhLA== + dependencies: + "@lerna/child-process" "6.6.1" + "@lerna/create" "6.6.1" + "@lerna/legacy-package-management" "6.6.1" + "@npmcli/arborist" "6.2.3" "@npmcli/run-script" "4.1.7" "@nrwl/devkit" ">=15.5.2 < 16" "@octokit/plugin-enterprise-rest" "6.0.1" @@ -7477,7 +7724,7 @@ lerna@^6.5.1: node-fetch "2.6.7" npm-package-arg "8.1.1" npm-packlist "5.1.1" - npm-registry-fetch "13.3.0" + npm-registry-fetch "^14.0.3" npmlog "^6.0.2" nx ">=15.5.2 < 16" p-map "4.0.0" @@ -7486,14 +7733,13 @@ lerna@^6.5.1: p-queue "6.6.2" p-reduce "2.1.0" p-waterfall "2.1.1" - pacote "13.6.1" - path-exists "4.0.0" + pacote "13.6.2" pify "5.0.0" read-cmd-shim "3.0.0" read-package-json "5.0.1" resolve-from "5.0.0" - rimraf "^3.0.2" - semver "7.3.4" + rimraf "^4.4.1" + semver "^7.3.8" signal-exit "3.0.7" slash "3.0.0" ssri "9.0.1" @@ -7545,9 +7791,9 @@ libnpmpublish@6.0.4: ssri "^9.0.0" libphonenumber-js@^1.10.14: - version "1.10.24" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.24.tgz#a1744cf29df86d5a587562ea28dde12320eb6ab6" - integrity sha512-3Dk8f5AmrcWqg+oHhmm9hwSTqpWHBdSqsHmjCJGroULFubi0+x7JEIGmRZCuL3TI8Tx39xaKqfnhsDQ4ALa/Nw== + version "1.10.28" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.28.tgz#cae7e929cad96cee5ecc9449027192ecba39ee72" + integrity sha512-1eAgjLrZA0+2Wgw4hs+4Q/kEBycxQo8ZLYnmOvZ3AlM8ImAVAJgDPlZtISLEzD1vunc2q8s2Pn7XwB7I8U3Kzw== libsodium-wrappers@^0.7.6: version "0.7.11" @@ -7679,9 +7925,9 @@ long@^4.0.0: integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== long@^5.0.0, long@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f" - integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A== + version "5.2.3" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" + integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" @@ -7704,11 +7950,16 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^7.14.1, lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: +lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: version "7.18.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== +lru-cache@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.0.tgz#19efafa9d08d1c08eb8efd78876075f0b8b1b07b" + integrity sha512-qFXQEwchrZcMVen2uIDceR8Tii6kCJak5rzDStfEM0qA3YLMswaxIEZO0DhIbJ3aqaJiDjt+3crlplOb0tDtKQ== + lru_map@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.4.1.tgz#f7b4046283c79fb7370c36f8fca6aee4324b0a98" @@ -7761,6 +8012,27 @@ make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6: socks-proxy-agent "^7.0.0" ssri "^9.0.0" +make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1: + version "11.1.0" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.0.tgz#f26b05e89317e960b75fd5e080e40d40f8d7b2a5" + integrity sha512-7ChuOzCb1LzdQZrTy0ky6RsCoMYeM+Fh4cY0+4zsJVhNcH5Q3OJojLY1mGkD0xAhWB29lskECVb6ZopofwjldA== + dependencies: + agentkeepalive "^4.2.1" + cacache "^17.0.0" + http-cache-semantics "^4.1.1" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^7.7.1" + minipass "^4.0.0" + minipass-fetch "^3.0.0" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + promise-retry "^2.0.1" + socks-proxy-agent "^7.0.0" + ssri "^10.0.0" + make-fetch-happen@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" @@ -7864,53 +8136,53 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -metro-babel-transformer@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.73.8.tgz#521374cb9234ba126f3f8d63588db5901308b4ed" - integrity sha512-GO6H/W2RjZ0/gm1pIvdO9EP34s3XN6kzoeyxqmfqKfYhJmYZf1SzXbyiIHyMbJNwJVrsKuHqu32+GopTlKscWw== +metro-babel-transformer@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.73.9.tgz#bec8aaaf1bbdc2e469fde586fde455f8b2a83073" + integrity sha512-DlYwg9wwYIZTHtic7dyD4BP0SDftoltZ3clma76nHu43blMWsCnrImHeHsAVne3XsQ+RJaSRxhN5nkG2VyVHwA== dependencies: "@babel/core" "^7.20.0" hermes-parser "0.8.0" - metro-source-map "0.73.8" + metro-source-map "0.73.9" nullthrows "^1.1.1" -metro-cache-key@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.73.8.tgz#afc9f63454edbd9d207544445a66e8a4e119462d" - integrity sha512-VzFGu4kJGIkLjyDgVoM2ZxIHlMdCZWMqVIux9N+EeyMVMvGXTiXW8eGROgxzDhVjyR58IjfMsYpRCKz5dR+2ew== +metro-cache-key@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.73.9.tgz#7d8c441a3b7150f7b201273087ef3cf7d3435d9f" + integrity sha512-uJg+6Al7UoGIuGfoxqPBy6y1Ewq7Y8/YapGYIDh6sohInwt/kYKnPZgLDYHIPvY2deORnQ/2CYo4tOeBTnhCXQ== -metro-cache@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.73.8.tgz#85e2d7f7c7c74d1f942b7ecd168f7aceb987d883" - integrity sha512-/uFbTIw813Rvb8kSAIHvax9gWl41dtgjY2SpJLNIBLdQ6oFZ3CVo3ahZIiEZOrCeHl9xfGn5tmvNb8CEFa/Q5w== +metro-cache@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.73.9.tgz#773c2df6ba53434e58ccbe421b0c54e6da8d2890" + integrity sha512-upiRxY8rrQkUWj7ieACD6tna7xXuXdu2ZqrheksT79ePI0aN/t0memf6WcyUtJUMHZetke3j+ppELNvlmp3tOw== dependencies: - metro-core "0.73.8" + metro-core "0.73.9" rimraf "^3.0.2" -metro-config@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.73.8.tgz#8f6c22c94528919635c6688ed8d2ad8a10c70b27" - integrity sha512-sAYq+llL6ZAfro64U99ske8HcKKswxX4wIZbll9niBKG7TkWm7tfMY1jO687XEmE4683rHncZeBRav9pLngIzg== +metro-config@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.73.9.tgz#6b43c70681bdd6b00f44400fc76dddbe53374500" + integrity sha512-NiWl1nkYtjqecDmw77tbRbXnzIAwdO6DXGZTuKSkH+H/c1NKq1eizO8Fe+NQyFtwR9YLqn8Q0WN1nmkwM1j8CA== dependencies: cosmiconfig "^5.0.5" jest-validate "^26.5.2" - metro "0.73.8" - metro-cache "0.73.8" - metro-core "0.73.8" - metro-runtime "0.73.8" + metro "0.73.9" + metro-cache "0.73.9" + metro-core "0.73.9" + metro-runtime "0.73.9" -metro-core@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.73.8.tgz#a31ba7d7bfe3f4c2ac2c7a2493aa4229ecad701e" - integrity sha512-Aew4dthbZf8bRRjlYGL3cnai3+LKYTf6mc7YS2xLQRWtgGZ1b/H8nQtBvXZpfRYFcS84UeEQ10vwIf5eR3qPdQ== +metro-core@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.73.9.tgz#410c5c0aeae840536c10039f68098fdab3da568e" + integrity sha512-1NTs0IErlKcFTfYyRT3ljdgrISWpl1nys+gaHkXapzTSpvtX9F1NQNn5cgAuE+XIuTJhbsCdfIJiM2JXbrJQaQ== dependencies: lodash.throttle "^4.1.1" - metro-resolver "0.73.8" + metro-resolver "0.73.9" -metro-file-map@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.73.8.tgz#88d666e7764e1b0adf5fd634d91e97e3135d2db7" - integrity sha512-CM552hUO9om02jJdLszOCIDADKNaaeVz8CjYXItndvgr5jmFlQYAR+UMvaDzeT8oYdAV1DXAljma2CS2UBymPg== +metro-file-map@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.73.9.tgz#09c04a8e8ef1eaa6ecb2b9cb8cb53bb0fa0167ec" + integrity sha512-R/Wg3HYeQhYY3ehWtfedw8V0ne4lpufG7a21L3GWer8tafnC9pmjoCKEbJz9XZkVj9i1FtxE7UTbrtZNeIILxQ== dependencies: abort-controller "^3.0.0" anymatch "^3.0.3" @@ -7928,39 +8200,39 @@ metro-file-map@0.73.8: optionalDependencies: fsevents "^2.3.2" -metro-hermes-compiler@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.73.8.tgz#c522e2c97afc8bdc249755d88146a75720bc2498" - integrity sha512-2d7t+TEoQLk+jyXgBykmAtPPJK2B46DB3qUYIMKDFDDaKzCljrojyVuGgQq6SM1f95fe6HDAQ3K9ihTjeB90yw== +metro-hermes-compiler@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.73.9.tgz#6f473e67e8f76066066f00e2e0ecce865f7d445d" + integrity sha512-5B3vXIwQkZMSh3DQQY23XpTCpX9kPLqZbA3rDuAcbGW0tzC3f8dCenkyBb0GcCzyTDncJeot/A7oVCVK6zapwg== -metro-inspector-proxy@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.73.8.tgz#67d5aadfc33fe97f61c716eb168db4bd5d0e3c96" - integrity sha512-F0QxwDTox0TDeXVRN7ZmI7BknBjPDVKQ1ZeKznFBiMa0SXiD1kzoksfpDbZ6hTEKrhVM9Ep0YQmC7avwZouOnA== +metro-inspector-proxy@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.73.9.tgz#8e11cd300adf3f904f1f5afe28b198312cdcd8c2" + integrity sha512-B3WrWZnlYhtTrv0IaX3aUAhi2qVILPAZQzb5paO1e+xrz4YZHk9c7dXv7qe7B/IQ132e3w46y3AL7rFo90qVjA== dependencies: connect "^3.6.5" debug "^2.2.0" ws "^7.5.1" yargs "^17.5.1" -metro-minify-terser@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.73.8.tgz#a0fe857d6aaf99cba3a2aef59ee06ac409682c6b" - integrity sha512-pnagyXAoMPhihWrHRIWqCxrP6EJ8Hfugv5RXBb6HbOANmwajn2uQuzeu18+dXaN1yPoDCMCgpg/UA4ibFN5jtQ== +metro-minify-terser@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.73.9.tgz#301aef2e106b0802f7a14ef0f2b4883b20c80018" + integrity sha512-MTGPu2qV5qtzPJ2SqH6s58awHDtZ4jd7lmmLR+7TXDwtZDjIBA0YVfI0Zak2Haby2SqoNKrhhUns/b4dPAQAVg== dependencies: terser "^5.15.0" -metro-minify-uglify@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.73.8.tgz#b2e2430014c340479db4fc393a2ea4c5bad75ecd" - integrity sha512-9wZqKfraVfmtMXdOzRyan+6r1woQXqqa4KeXfVh7+Mxl+5+J0Lmw6EvTrWawsaOEpvpn32q9MfoHC1d8plDJwA== +metro-minify-uglify@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.73.9.tgz#cf4f8c19b688deea103905689ec736c2f2acd733" + integrity sha512-gzxD/7WjYcnCNGiFJaA26z34rjOp+c/Ft++194Wg91lYep3TeWQ0CnH8t2HRS7AYDHU81SGWgvD3U7WV0g4LGA== dependencies: uglify-es "^3.1.9" -metro-react-native-babel-preset@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.73.8.tgz#04908f264f5d99c944ae20b5b11f659431328431" - integrity sha512-spNrcQJTbQntEIqJnCA6yL4S+dzV9fXCk7U+Rm7yJasZ4o4Frn7jP23isu7FlZIp1Azx1+6SbP7SgQM+IP5JgQ== +metro-react-native-babel-preset@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.73.9.tgz#ef54637dd20f025197beb49e71309a9c539e73e2" + integrity sha512-AoD7v132iYDV4K78yN2OLgTPwtAKn0XlD2pOhzyBxiI8PeXzozhbKyPV7zUOJUPETj+pcEVfuYj5ZN/8+bhbCw== dependencies: "@babel/core" "^7.20.0" "@babel/plugin-proposal-async-generator-functions" "^7.0.0" @@ -8001,64 +8273,64 @@ metro-react-native-babel-preset@0.73.8: "@babel/template" "^7.0.0" react-refresh "^0.4.0" -metro-react-native-babel-transformer@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.73.8.tgz#cbcd4b243216878431dc4311ce46f02a928e3991" - integrity sha512-oH/LCCJPauteAE28c0KJAiSrkV+1VJbU0PwA9UwaWnle+qevs/clpKQ8LrIr33YbBj4CiI1kFoVRuNRt5h4NFg== +metro-react-native-babel-transformer@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.73.9.tgz#4f4f0cfa5119bab8b53e722fabaf90687d0cbff0" + integrity sha512-DSdrEHuQ22ixY7DyipyKkIcqhOJrt5s6h6X7BYJCP9AMUfXOwLe2biY3BcgJz5GOXv8/Akry4vTCvQscVS1otQ== dependencies: "@babel/core" "^7.20.0" babel-preset-fbjs "^3.4.0" hermes-parser "0.8.0" - metro-babel-transformer "0.73.8" - metro-react-native-babel-preset "0.73.8" - metro-source-map "0.73.8" + metro-babel-transformer "0.73.9" + metro-react-native-babel-preset "0.73.9" + metro-source-map "0.73.9" nullthrows "^1.1.1" -metro-resolver@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.73.8.tgz#65cc158575d130363296f66a33257c7971228640" - integrity sha512-GiBWont7/OgAftkkj2TiEp+Gf1PYZUk8xV4MbtnQjIKyy3MlGY3GbpMQ1BHih9GUQqlF0n9jsUlC2K5P0almXQ== +metro-resolver@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.73.9.tgz#f3cf77e6c7606a34aa81bad40edb856aad671cf3" + integrity sha512-Ej3wAPOeNRPDnJmkK0zk7vJ33iU07n+oPhpcf5L0NFkWneMmSM2bflMPibI86UjzZGmRfn0AhGhs8yGeBwQ/Xg== dependencies: absolute-path "^0.0.0" -metro-runtime@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.73.8.tgz#dadae7c154fbbde24390cf7f7e7d934a2768cd18" - integrity sha512-M+Bg9M4EN5AEpJ8NkiUsawD75ifYvYfHi05w6QzHXaqOrsTeaRbbeLuOGCYxU2f/tPg17wQV97/rqUQzs9qEtA== +metro-runtime@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.73.9.tgz#0b24c0b066b8629ee855a6e5035b65061fef60d5" + integrity sha512-d5Hs83FpKB9r8q8Vb95+fa6ESpwysmPr4lL1I2rM2qXAFiO7OAPT9Bc23WmXgidkBtD0uUFdB2lG+H1ATz8rZg== dependencies: "@babel/runtime" "^7.0.0" react-refresh "^0.4.0" -metro-source-map@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.73.8.tgz#5134174e3d43de26ad331b95f637944c6547d441" - integrity sha512-wozFXuBYMAy7b8BCYwC+qoXsvayVJBHWtSTlSLva99t+CoUSG9JO9kg1umzbOz28YYPxKmvb/wbnLMkHdas2cA== +metro-source-map@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.73.9.tgz#89ca41f6346aeb12f7f23496fa363e520adafebe" + integrity sha512-l4VZKzdqafipriETYR6lsrwtavCF1+CMhCOY9XbyWeTrpGSNgJQgdeJpttzEZTHQQTLR0csQo0nD1ef3zEP6IQ== dependencies: "@babel/traverse" "^7.20.0" "@babel/types" "^7.20.0" invariant "^2.2.4" - metro-symbolicate "0.73.8" + metro-symbolicate "0.73.9" nullthrows "^1.1.1" - ob1 "0.73.8" + ob1 "0.73.9" source-map "^0.5.6" vlq "^1.0.0" -metro-symbolicate@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.73.8.tgz#96920f607bce484283d822ee5fe18d932f69c03d" - integrity sha512-xkBAcceYYp0GGdCCuMzkCF1ejHsd0lYlbKBkjSRgM0Nlj80VapPaSwumYoAvSaDxcbkvS7/sCjURGp5DsSFgRQ== +metro-symbolicate@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.73.9.tgz#cb452299a36e5b86b2826e7426d51221635c48bf" + integrity sha512-4TUOwxRHHqbEHxRqRJ3wZY5TA8xq7AHMtXrXcjegMH9FscgYztsrIG9aNBUBS+VLB6g1qc6BYbfIgoAnLjCDyw== dependencies: invariant "^2.2.4" - metro-source-map "0.73.8" + metro-source-map "0.73.9" nullthrows "^1.1.1" source-map "^0.5.6" through2 "^2.0.1" vlq "^1.0.0" -metro-transform-plugins@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.73.8.tgz#07be7fd94a448ea1b245ab02ce7d277d757f9a32" - integrity sha512-IxjlnB5eA49M0WfvPEzvRikK3Rr6bECUUfcZt/rWpSphq/mttgyLYcHQ+VTZZl0zHolC3cTLwgoDod4IIJBn1A== +metro-transform-plugins@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.73.9.tgz#9fffbe1b24269e3d114286fa681abc570072d9b8" + integrity sha512-r9NeiqMngmooX2VOKLJVQrMuV7PAydbqst5bFhdVBPcFpZkxxqyzjzo+kzrszGy2UpSQBZr2P1L6OMjLHwQwfQ== dependencies: "@babel/core" "^7.20.0" "@babel/generator" "^7.20.0" @@ -8066,29 +8338,29 @@ metro-transform-plugins@0.73.8: "@babel/traverse" "^7.20.0" nullthrows "^1.1.1" -metro-transform-worker@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.73.8.tgz#701a006c2b4d93f1bb24802f3f2834c963153db9" - integrity sha512-B8kR6lmcvyG4UFSF2QDfr/eEnWJvg0ZadooF8Dg6m/3JSm9OAqfSoC0YrWqAuvtWImNDnbeKWN7/+ns44Hv6tg== +metro-transform-worker@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.73.9.tgz#30384cef2d5e35a4abe91b15bf1a8344f5720441" + integrity sha512-Rq4b489sIaTUENA+WCvtu9yvlT/C6zFMWhU4sq+97W29Zj0mPBjdk+qGT5n1ZBgtBIJzZWt1KxeYuc17f4aYtQ== dependencies: "@babel/core" "^7.20.0" "@babel/generator" "^7.20.0" "@babel/parser" "^7.20.0" "@babel/types" "^7.20.0" babel-preset-fbjs "^3.4.0" - metro "0.73.8" - metro-babel-transformer "0.73.8" - metro-cache "0.73.8" - metro-cache-key "0.73.8" - metro-hermes-compiler "0.73.8" - metro-source-map "0.73.8" - metro-transform-plugins "0.73.8" + metro "0.73.9" + metro-babel-transformer "0.73.9" + metro-cache "0.73.9" + metro-cache-key "0.73.9" + metro-hermes-compiler "0.73.9" + metro-source-map "0.73.9" + metro-transform-plugins "0.73.9" nullthrows "^1.1.1" -metro@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.73.8.tgz#25f014e4064eb34a4833c316e0a9094528061a8c" - integrity sha512-2EMJME9w5x7Uzn+DnQ4hzWr33u/aASaOBGdpf4lxbrlk6/vl4UBfX1sru6KU535qc/0Z1BMt4Vq9qsP3ZGFmWg== +metro@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.73.9.tgz#150e69a6735fab0bcb4f6ee97fd1efc65b3ec36f" + integrity sha512-BlYbPmTF60hpetyNdKhdvi57dSqutb+/oK0u3ni4emIh78PiI0axGo7RfdsZ/mn3saASXc94tDbpC5yn7+NpEg== dependencies: "@babel/code-frame" "^7.0.0" "@babel/core" "^7.20.0" @@ -8112,23 +8384,23 @@ metro@0.73.8: invariant "^2.2.4" jest-worker "^27.2.0" lodash.throttle "^4.1.1" - metro-babel-transformer "0.73.8" - metro-cache "0.73.8" - metro-cache-key "0.73.8" - metro-config "0.73.8" - metro-core "0.73.8" - metro-file-map "0.73.8" - metro-hermes-compiler "0.73.8" - metro-inspector-proxy "0.73.8" - metro-minify-terser "0.73.8" - metro-minify-uglify "0.73.8" - metro-react-native-babel-preset "0.73.8" - metro-resolver "0.73.8" - metro-runtime "0.73.8" - metro-source-map "0.73.8" - metro-symbolicate "0.73.8" - metro-transform-plugins "0.73.8" - metro-transform-worker "0.73.8" + metro-babel-transformer "0.73.9" + metro-cache "0.73.9" + metro-cache-key "0.73.9" + metro-config "0.73.9" + metro-core "0.73.9" + metro-file-map "0.73.9" + metro-hermes-compiler "0.73.9" + metro-inspector-proxy "0.73.9" + metro-minify-terser "0.73.9" + metro-minify-uglify "0.73.9" + metro-react-native-babel-preset "0.73.9" + metro-resolver "0.73.9" + metro-runtime "0.73.9" + metro-source-map "0.73.9" + metro-symbolicate "0.73.9" + metro-transform-plugins "0.73.9" + metro-transform-worker "0.73.9" mime-types "^2.1.27" node-fetch "^2.2.0" nullthrows "^1.1.1" @@ -8231,10 +8503,24 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^7.4.1: - version "7.4.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.2.tgz#157e847d79ca671054253b840656720cb733f10f" - integrity sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA== +minimatch@^6.1.6: + version "6.2.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-6.2.0.tgz#2b70fd13294178c69c04dfc05aebdb97a4e79e42" + integrity sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^7.4.2, minimatch@^7.4.6: + version "7.4.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb" + integrity sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^8.0.2: + version "8.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229" + integrity sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA== dependencies: brace-expansion "^2.0.1" @@ -8281,6 +8567,17 @@ minipass-fetch@^2.0.3: optionalDependencies: encoding "^0.1.13" +minipass-fetch@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.2.tgz#2f7275ae13f2fb0f2a469cee4f78250c25c80ab3" + integrity sha512-/ZpF1CQaWYqjbhfFgKNt3azxztEpc/JUPuMkqOgrnMQqcU8CbE409AUdJYTIWryl3PP5CBaTJZT71N49MXP/YA== + dependencies: + minipass "^4.0.0" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" @@ -8325,10 +8622,15 @@ minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3, minipass@^3. dependencies: yallist "^4.0.0" -minipass@^4.0.0, minipass@^4.0.2, minipass@^4.2.4: - version "4.2.5" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb" - integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q== +minipass@^4.0.0, minipass@^4.2.4: + version "4.2.8" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" + integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== minizlib@^1.3.3: version "1.3.3" @@ -8570,7 +8872,7 @@ node-fetch@3.0.0-beta.9: data-uri-to-buffer "^3.0.1" fetch-blob "^2.1.1" -node-fetch@^2.0, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: version "2.6.9" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== @@ -8667,6 +8969,13 @@ nopt@^6.0.0: dependencies: abbrev "^1.0.0" +nopt@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.1.0.tgz#91f6a3366182176e72ecab93a09c19b63b485f28" + integrity sha512-ZFPLe9Iu0tnx7oWhFxAo4s7QTn8+NNDDxYNaKLjE7Dp0tbakQ3M1QhQzsnzXHQBTUO3K9BmwaxnyO8Ayn2I95Q== + dependencies: + abbrev "^2.0.0" + normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -8697,6 +9006,16 @@ normalize-package-data@^4.0.0: semver "^7.3.5" validate-npm-package-license "^3.0.4" +normalize-package-data@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-5.0.0.tgz#abcb8d7e724c40d88462b84982f7cbf6859b4588" + integrity sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q== + dependencies: + hosted-git-info "^6.0.0" + is-core-module "^2.8.1" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" + normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -8716,6 +9035,13 @@ npm-bundled@^2.0.0: dependencies: npm-normalize-package-bin "^2.0.0" +npm-bundled@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-3.0.0.tgz#7e8e2f8bb26b794265028491be60321a25a39db7" + integrity sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ== + dependencies: + npm-normalize-package-bin "^3.0.0" + npm-install-checks@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-5.0.0.tgz#5ff27d209a4e3542b8ac6b0c1db6063506248234" @@ -8723,6 +9049,13 @@ npm-install-checks@^5.0.0: dependencies: semver "^7.1.1" +npm-install-checks@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-6.1.1.tgz#b459b621634d06546664207fde16810815808db1" + integrity sha512-dH3GmQL4vsPtld59cOn8uY0iOqRmqKvV+DLGwNXV/Q7MDgD2QfOADWd/mFXcIE5LVhYYGjA3baz6W9JneqnuCw== + dependencies: + semver "^7.1.1" + npm-normalize-package-bin@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" @@ -8733,6 +9066,11 @@ npm-normalize-package-bin@^2.0.0: resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz#9447a1adaaf89d8ad0abe24c6c84ad614a675fff" integrity sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ== +npm-normalize-package-bin@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.0.tgz#6097436adb4ef09e2628b59a7882576fe53ce485" + integrity sha512-g+DPQSkusnk7HYXr75NtzkIP4+N81i3RPsGFidF3DzHd9MT9wWngmqoeg/fnHFz5MNdtG4w03s+QnhewSLTT2Q== + npm-package-arg@8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.1.tgz#00ebf16ac395c63318e67ce66780a06db6df1b04" @@ -8742,6 +9080,16 @@ npm-package-arg@8.1.1: semver "^7.0.0" validate-npm-package-name "^3.0.0" +npm-package-arg@^10.0.0, npm-package-arg@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-10.1.0.tgz#827d1260a683806685d17193073cc152d3c7e9b1" + integrity sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA== + dependencies: + hosted-git-info "^6.0.0" + proc-log "^3.0.0" + semver "^7.3.5" + validate-npm-package-name "^5.0.0" + npm-package-arg@^9.0.0, npm-package-arg@^9.0.1: version "9.1.2" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-9.1.2.tgz#fc8acecb00235f42270dda446f36926ddd9ac2bc" @@ -8781,6 +9129,13 @@ npm-packlist@^5.1.0: npm-bundled "^2.0.0" npm-normalize-package-bin "^2.0.0" +npm-packlist@^7.0.0: + version "7.0.4" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-7.0.4.tgz#033bf74110eb74daf2910dc75144411999c5ff32" + integrity sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q== + dependencies: + ignore-walk "^6.0.0" + npm-pick-manifest@^7.0.0: version "7.0.2" resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-7.0.2.tgz#1d372b4e7ea7c6712316c0e99388a73ed3496e84" @@ -8791,18 +9146,28 @@ npm-pick-manifest@^7.0.0: npm-package-arg "^9.0.0" semver "^7.3.5" -npm-registry-fetch@13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-13.3.0.tgz#0ce10fa4a699a1e70685ecf41bbfb4150d74231b" - integrity sha512-10LJQ/1+VhKrZjIuY9I/+gQTvumqqlgnsCufoXETHAPFTS3+M+Z5CFhZRDHGavmJ6rOye3UvNga88vl8n1r6gg== +npm-pick-manifest@^8.0.0, npm-pick-manifest@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-8.0.1.tgz#c6acd97d1ad4c5dbb80eac7b386b03ffeb289e5f" + integrity sha512-mRtvlBjTsJvfCCdmPtiu2bdlx8d/KXtF7yNXNWe7G0Z36qWA9Ny5zXsI2PfBZEv7SXgoxTmNaTzGSbbzDZChoA== dependencies: - make-fetch-happen "^10.0.6" - minipass "^3.1.6" - minipass-fetch "^2.0.3" + npm-install-checks "^6.0.0" + npm-normalize-package-bin "^3.0.0" + npm-package-arg "^10.0.0" + semver "^7.3.5" + +npm-registry-fetch@14.0.3: + version "14.0.3" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.3.tgz#8545e321c2b36d2c6fe6e009e77e9f0e527f547b" + integrity sha512-YaeRbVNpnWvsGOjX2wk5s85XJ7l1qQBGAp724h8e2CZFFhMSuw9enom7K1mWVUtvXO1uUSFIAPofQK0pPN0ZcA== + dependencies: + make-fetch-happen "^11.0.0" + minipass "^4.0.0" + minipass-fetch "^3.0.0" minipass-json-stream "^1.0.1" minizlib "^2.1.2" - npm-package-arg "^9.0.1" - proc-log "^2.0.0" + npm-package-arg "^10.0.0" + proc-log "^3.0.0" npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1: version "13.3.1" @@ -8817,6 +9182,19 @@ npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1: npm-package-arg "^9.0.1" proc-log "^2.0.0" +npm-registry-fetch@^14.0.0, npm-registry-fetch@^14.0.3: + version "14.0.4" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.4.tgz#43dfa55ce7c0d0c545d625c7a916bab5b95f7038" + integrity sha512-pMS2DRkwg+M44ct65zrN/Cr9IHK1+n6weuefAo6Er4lc+/8YBCU0Czq04H3ZiSigluh7pb2rMM5JpgcytctB+Q== + dependencies: + make-fetch-happen "^11.0.0" + minipass "^4.0.0" + minipass-fetch "^3.0.0" + minipass-json-stream "^1.0.1" + minizlib "^2.1.2" + npm-package-arg "^10.0.0" + proc-log "^3.0.0" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -8831,6 +9209,16 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npmlog@6.0.2, npmlog@^6.0.0, npmlog@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" + integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== + dependencies: + are-we-there-yet "^3.0.0" + console-control-strings "^1.1.0" + gauge "^4.0.3" + set-blocking "^2.0.0" + npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -8851,14 +9239,14 @@ npmlog@^5.0.1: gauge "^3.0.0" set-blocking "^2.0.0" -npmlog@^6.0.0, npmlog@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" - integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== +npmlog@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-7.0.1.tgz#7372151a01ccb095c47d8bf1d0771a4ff1f53ac8" + integrity sha512-uJ0YFk/mCQpLBt+bxN88AKd+gyqZvZDbtiNxk6Waqcj2aPRyfVx8ITawkyQynxUagInjdYT1+qj4NfA5KJJUxg== dependencies: - are-we-there-yet "^3.0.0" + are-we-there-yet "^4.0.0" console-control-strings "^1.1.0" - gauge "^4.0.3" + gauge "^5.0.0" set-blocking "^2.0.0" nullthrows@^1.1.1: @@ -8871,13 +9259,13 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== -nx@15.8.7, "nx@>=15.5.2 < 16": - version "15.8.7" - resolved "https://registry.yarnpkg.com/nx/-/nx-15.8.7.tgz#a89156244f6f94407d7603375ae2f52733c7aff4" - integrity sha512-u6p/1gU20WU61orxK7hcXBsVspPHy3X66XVAAakkYcaOBlsJhJrR7Og191qIyjEkqEWmcekiDQVw3D6XfagL4Q== +nx@15.9.2, "nx@>=15.5.2 < 16": + version "15.9.2" + resolved "https://registry.yarnpkg.com/nx/-/nx-15.9.2.tgz#d7ace1e5ae64a47f1b553dc5da08dbdd858bde96" + integrity sha512-wtcs+wsuplSckvgk+bV+/XuGlo+sVWzSG0RpgWBjQYeqA3QsVFEAPVY66Z5cSoukDbTV77ddcAjEw+Rz8oOR1A== dependencies: - "@nrwl/cli" "15.8.7" - "@nrwl/tao" "15.8.7" + "@nrwl/cli" "15.9.2" + "@nrwl/tao" "15.9.2" "@parcel/watcher" "2.0.4" "@yarnpkg/lockfile" "^1.1.0" "@yarnpkg/parsers" "^3.0.0-rc.18" @@ -8912,20 +9300,20 @@ nx@15.8.7, "nx@>=15.5.2 < 16": yargs "^17.6.2" yargs-parser "21.1.1" optionalDependencies: - "@nrwl/nx-darwin-arm64" "15.8.7" - "@nrwl/nx-darwin-x64" "15.8.7" - "@nrwl/nx-linux-arm-gnueabihf" "15.8.7" - "@nrwl/nx-linux-arm64-gnu" "15.8.7" - "@nrwl/nx-linux-arm64-musl" "15.8.7" - "@nrwl/nx-linux-x64-gnu" "15.8.7" - "@nrwl/nx-linux-x64-musl" "15.8.7" - "@nrwl/nx-win32-arm64-msvc" "15.8.7" - "@nrwl/nx-win32-x64-msvc" "15.8.7" - -ob1@0.73.8: - version "0.73.8" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.73.8.tgz#c569f1a15ce2d04da6fd70293ad44b5a93b11978" - integrity sha512-1F7j+jzD+edS6ohQP7Vg5f3yiIk5i3x1uLrNIHOmLHWzWK1t3zrDpjnoXghccdVlsU+UjbyURnDynm4p0GgXeA== + "@nrwl/nx-darwin-arm64" "15.9.2" + "@nrwl/nx-darwin-x64" "15.9.2" + "@nrwl/nx-linux-arm-gnueabihf" "15.9.2" + "@nrwl/nx-linux-arm64-gnu" "15.9.2" + "@nrwl/nx-linux-arm64-musl" "15.9.2" + "@nrwl/nx-linux-x64-gnu" "15.9.2" + "@nrwl/nx-linux-x64-musl" "15.9.2" + "@nrwl/nx-win32-arm64-msvc" "15.9.2" + "@nrwl/nx-win32-x64-msvc" "15.9.2" + +ob1@0.73.9: + version "0.73.9" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.73.9.tgz#d5677a0dd3e2f16ad84231278d79424436c38c59" + integrity sha512-kHOzCOFXmAM26fy7V/YuXNKne2TyRiXbFAvPBIbuedJCZZWQZHLdPzMeXJI4Egt6IcfDttRzN3jQ90wOwq1iNw== object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" @@ -9186,34 +9574,7 @@ p-waterfall@2.1.1: dependencies: p-reduce "^2.0.0" -pacote@13.6.1: - version "13.6.1" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-13.6.1.tgz#ac6cbd9032b4c16e5c1e0c60138dfe44e4cc589d" - integrity sha512-L+2BI1ougAPsFjXRyBhcKmfT016NscRFLv6Pz5EiNf1CCFJFU0pSKKQwsZTyAQB+sTuUL4TyFyp6J1Ork3dOqw== - dependencies: - "@npmcli/git" "^3.0.0" - "@npmcli/installed-package-contents" "^1.0.7" - "@npmcli/promise-spawn" "^3.0.0" - "@npmcli/run-script" "^4.1.0" - cacache "^16.0.0" - chownr "^2.0.0" - fs-minipass "^2.1.0" - infer-owner "^1.0.4" - minipass "^3.1.6" - mkdirp "^1.0.4" - npm-package-arg "^9.0.0" - npm-packlist "^5.1.0" - npm-pick-manifest "^7.0.0" - npm-registry-fetch "^13.0.1" - proc-log "^2.0.0" - promise-retry "^2.0.1" - read-package-json "^5.0.0" - read-package-json-fast "^2.0.3" - rimraf "^3.0.2" - ssri "^9.0.0" - tar "^6.1.11" - -pacote@^13.0.3, pacote@^13.6.1: +pacote@13.6.2, pacote@^13.6.1: version "13.6.2" resolved "https://registry.yarnpkg.com/pacote/-/pacote-13.6.2.tgz#0d444ba3618ab3e5cd330b451c22967bbd0ca48a" integrity sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg== @@ -9240,6 +9601,30 @@ pacote@^13.0.3, pacote@^13.6.1: ssri "^9.0.0" tar "^6.1.11" +pacote@^15.0.0, pacote@^15.0.8: + version "15.1.1" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.1.1.tgz#94d8c6e0605e04d427610b3aacb0357073978348" + integrity sha512-eeqEe77QrA6auZxNHIp+1TzHQ0HBKf5V6c8zcaYZ134EJe1lCi+fjXATkNiEEfbG+e50nu02GLvUtmZcGOYabQ== + dependencies: + "@npmcli/git" "^4.0.0" + "@npmcli/installed-package-contents" "^2.0.1" + "@npmcli/promise-spawn" "^6.0.1" + "@npmcli/run-script" "^6.0.0" + cacache "^17.0.0" + fs-minipass "^3.0.0" + minipass "^4.0.0" + npm-package-arg "^10.0.0" + npm-packlist "^7.0.0" + npm-pick-manifest "^8.0.0" + npm-registry-fetch "^14.0.0" + proc-log "^3.0.0" + promise-retry "^2.0.1" + read-package-json "^6.0.0" + read-package-json-fast "^3.0.0" + sigstore "^1.0.0" + ssri "^10.0.0" + tar "^6.1.11" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -9247,13 +9632,13 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-conflict-json@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/parse-conflict-json/-/parse-conflict-json-2.0.2.tgz#3d05bc8ffe07d39600dc6436c6aefe382033d323" - integrity sha512-jDbRGb00TAPFsKWCpZZOT93SxVP9nONOSgES3AevqRq/CHvavEBvKAjxX9p5Y5F0RZLxH9Ufd9+RwtCsa+lFDA== +parse-conflict-json@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/parse-conflict-json/-/parse-conflict-json-3.0.1.tgz#67dc55312781e62aa2ddb91452c7606d1969960c" + integrity sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw== dependencies: - json-parse-even-better-errors "^2.3.1" - just-diff "^5.0.1" + json-parse-even-better-errors "^3.0.0" + just-diff "^6.0.0" just-diff-apply "^5.2.0" parse-json@^4.0.0: @@ -9298,16 +9683,16 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== -path-exists@4.0.0, path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -9329,12 +9714,12 @@ path-parse@^1.0.7: integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-scurry@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.1.tgz#dab45f7bb1d3f45a0e271ab258999f4ab7e23132" - integrity sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA== + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.7.0.tgz#99c741a2cfbce782294a39994d63748b5a24f6db" + integrity sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg== dependencies: - lru-cache "^7.14.1" - minipass "^4.0.2" + lru-cache "^9.0.0" + minipass "^5.0.0" path-to-regexp@0.1.7: version "0.1.7" @@ -9407,6 +9792,14 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== +postcss-selector-parser@^6.0.10: + version "6.0.11" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" + integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -9420,9 +9813,18 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.8.4" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" - integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== + version "2.8.7" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" + integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== + +pretty-format@29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.3.tgz#25500ada21a53c9e8423205cf0337056b201244c" + integrity sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA== + dependencies: + "@jest/schemas" "^29.4.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" pretty-format@^26.5.2, pretty-format@^26.6.2: version "26.6.2" @@ -9448,20 +9850,30 @@ proc-log@^2.0.0, proc-log@^2.0.1: resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" integrity sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw== +proc-log@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" + integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + promise-all-reject-late@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== promise-call-limit@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-call-limit/-/promise-call-limit-1.0.1.tgz#4bdee03aeb85674385ca934da7114e9bcd3c6e24" - integrity sha512-3+hgaa19jzCGLuSCbieeRsu5C2joKfYn8pY6JAuXFRVfF4IO+L7UPpFWNTeWT9pM7uhskvbPPd/oEOktCn317Q== + version "1.0.2" + resolved "https://registry.yarnpkg.com/promise-call-limit/-/promise-call-limit-1.0.2.tgz#f64b8dd9ef7693c9c7613e7dfe8d6d24de3031ea" + integrity sha512-1vTUnfI2hzui8AEIixbdAJlFY4LFDXqQswy/2eOlThAscXCY4It8FdVuI0fMJGAB2aWGbdQf/gv0skKYXmdrHA== promise-inflight@^1.0.1: version "1.0.1" @@ -9660,9 +10072,9 @@ rc@^1.2.8: strip-json-comments "~2.0.1" react-devtools-core@^4.26.1: - version "4.27.2" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.2.tgz#d20fc57e258c656eedabafc2c851d38b33583148" - integrity sha512-8SzmIkpO87alD7Xr6gWIEa1jHkMjawOZ+6egjazlnjB4UUcbnzGDf/vBJ4BzGuWWEM+pzrxuzsPpcMqlQkYK2g== + version "4.27.5" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.5.tgz#35e41c09e7662ea29948d3caaeeea82f068cbbac" + integrity sha512-QJTriF1V4oyIenViCvM6qQuvcevQsp0sbKkHBZIQOij+AwY9DdOBY+dOeuymUqO5zV61CbmGxWsAIjeWlFS++w== dependencies: shell-quote "^1.6.1" ws "^7" @@ -9707,10 +10119,10 @@ react-native-get-random-values@^1.8.0: dependencies: fast-base64-decode "^1.0.0" -react-native-gradle-plugin@^0.71.16: - version "0.71.16" - resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.16.tgz#822bb0c680e03b5df5aa65f2e5ffc2bc2930854a" - integrity sha512-H2BjG2zk7B7Wii9sXvd9qhCVRQYDAHSWdMw9tscmZBqSP62DkIWEQSk4/B2GhQ4aK9ydVXgtqR6tBeg3yy8TSA== +react-native-gradle-plugin@^0.71.17: + version "0.71.17" + resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.17.tgz#cf780a27270f0a32dca8184eff91555d7627dd00" + integrity sha512-OXXYgpISEqERwjSlaCiaQY6cTY5CH6j73gdkWpK0hedxtiWMWgH+i5TOi4hIGYitm9kQBeyDu+wim9fA8ROFJA== react-native-securerandom@^0.1.1: version "0.1.1" @@ -9720,14 +10132,14 @@ react-native-securerandom@^0.1.1: base64-js "*" react-native@^0.71.4: - version "0.71.4" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.4.tgz#f03f600efe68f745d19454ab17f9c1a9ef304790" - integrity sha512-3hSYqvWrOdKhpV3HpEKp1/CkWx8Sr/N/miCrmUIAsVTSJUR7JW0VvIsrV9urDhUj/s6v2WF4n7qIEEJsmTCrPw== + version "0.71.6" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.6.tgz#e8f07baf55abd1015eaa7040ceaa4aa632c2c04f" + integrity sha512-gHrDj7qaAaiE41JwaFCh3AtvOqOLuRgZtHKzNiwxakG/wvPAYmG73ECfWHGxjxIx/QT17Hp37Da3ipCei/CayQ== dependencies: "@jest/create-cache-key-function" "^29.2.1" - "@react-native-community/cli" "10.2.0" + "@react-native-community/cli" "10.2.2" "@react-native-community/cli-platform-android" "10.2.0" - "@react-native-community/cli-platform-ios" "10.2.0" + "@react-native-community/cli-platform-ios" "10.2.1" "@react-native/assets" "1.0.0" "@react-native/normalize-color" "2.1.0" "@react-native/polyfills" "2.0.0" @@ -9740,16 +10152,16 @@ react-native@^0.71.4: jest-environment-node "^29.2.1" jsc-android "^250231.0.0" memoize-one "^5.0.0" - metro-react-native-babel-transformer "0.73.8" - metro-runtime "0.73.8" - metro-source-map "0.73.8" + metro-react-native-babel-transformer "0.73.9" + metro-runtime "0.73.9" + metro-source-map "0.73.9" mkdirp "^0.5.1" nullthrows "^1.1.1" pretty-format "^26.5.2" promise "^8.3.0" react-devtools-core "^4.26.1" react-native-codegen "^0.71.5" - react-native-gradle-plugin "^0.71.16" + react-native-gradle-plugin "^0.71.17" react-refresh "^0.4.0" react-shallow-renderer "^16.15.0" regenerator-runtime "^0.13.2" @@ -9777,12 +10189,12 @@ read-cmd-shim@3.0.0: resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.0.tgz#62b8c638225c61e6cc607f8f4b779f3b8238f155" integrity sha512-KQDVjGqhZk92PPNRj9ZEXEuqg8bUobSKRw+q0YQ3TKI5xkce7bUJobL4Z/OtiEbAAv70yEpYIXp4iQ9L8oPVog== -read-cmd-shim@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.1.tgz#868c235ec59d1de2db69e11aec885bc095aea087" - integrity sha512-kEmDUoYf/CDy8yZbLTmhB1X9kkjf9Q80PCNsDMb7ufrGd6zZSQA1+UyjrO+pZm5K/S4OXCWJeiIt1JA8kAsa6g== +read-cmd-shim@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz#640a08b473a49043e394ae0c7a34dd822c73b9bb" + integrity sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q== -read-package-json-fast@^2.0.2, read-package-json-fast@^2.0.3: +read-package-json-fast@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== @@ -9790,6 +10202,14 @@ read-package-json-fast@^2.0.2, read-package-json-fast@^2.0.3: json-parse-even-better-errors "^2.3.0" npm-normalize-package-bin "^1.0.1" +read-package-json-fast@^3.0.0, read-package-json-fast@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz#394908a9725dc7a5f14e70c8e7556dff1d2b1049" + integrity sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw== + dependencies: + json-parse-even-better-errors "^3.0.0" + npm-normalize-package-bin "^3.0.0" + read-package-json@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-5.0.1.tgz#1ed685d95ce258954596b13e2e0e76c7d0ab4c26" @@ -9810,6 +10230,16 @@ read-package-json@^5.0.0: normalize-package-data "^4.0.0" npm-normalize-package-bin "^2.0.0" +read-package-json@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-6.0.1.tgz#566cb06bc05dbddefba4607e9096d5a9efbcd836" + integrity sha512-AaHqXxfAVa+fNL07x8iAghfKOds/XXsu7zoouIVsbm7PEbQ3nMWXlvjcbrNLjElnUHWQtAo4QEa0RXuvD4XlpA== + dependencies: + glob "^9.3.0" + json-parse-even-better-errors "^3.0.0" + normalize-package-data "^5.0.0" + npm-normalize-package-bin "^3.0.0" + read-pkg-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" @@ -9875,15 +10305,15 @@ readable-stream@^2.0.6, readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readdir-scoped-modules@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" - integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== +readable-stream@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.3.0.tgz#0914d0c72db03b316c9733bb3461d64a3cc50cba" + integrity sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ== dependencies: - debuglog "^1.0.1" - dezalgo "^1.0.0" - graceful-fs "^4.1.2" - once "^1.3.0" + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" readline@^1.3.0: version "1.3.0" @@ -10049,16 +10479,16 @@ resolve-url@^0.2.1: integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== resolve.exports@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.1.tgz#cee884cd4e3f355660e501fa3276b27d7ffe5a20" - integrity sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw== + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.11.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -10104,20 +10534,13 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@^4.0.7: +rimraf@^4.0.7, rimraf@^4.4.0, rimraf@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.1.tgz#bd33364f67021c5b79e93d7f4fa0568c7c21b755" integrity sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og== dependencies: glob "^9.2.0" -rimraf@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.0.tgz#c7a9f45bb2ec058d2e60ef9aca5167974313d605" - integrity sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ== - dependencies: - glob "^9.2.0" - rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" @@ -10211,13 +10634,20 @@ semver@7.3.4: dependencies: lru-cache "^6.0.0" -semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: +semver@7.3.8: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" +semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: + version "7.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" + integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== + dependencies: + lru-cache "^6.0.0" + semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -10316,9 +10746,9 @@ shebang-regex@^3.0.0: integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@^1.6.1, shell-quote@^1.7.3: - version "1.8.0" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.0.tgz#20d078d0eaf71d54f43bd2ba14a1b5b9bfa5c8ba" - integrity sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ== + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== side-channel@^1.0.4: version "1.0.4" @@ -10334,6 +10764,15 @@ signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, s resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +sigstore@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.3.0.tgz#cfcb636ff5ef3df1dafb34552ef1ee5e72be1a83" + integrity sha512-dhdv+jOAi1RgLHw13lxumk3rlgZtumUz9QrCNPTx9MazUnUV3BfAb74oYAMPQQ7uaeogB5vTosbz3POzKbEHUQ== + dependencies: + "@sigstore/protobuf-specs" "^0.1.0" + make-fetch-happen "^11.0.1" + tuf-js "^1.1.3" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -10537,6 +10976,13 @@ ssri@9.0.1, ssri@^9.0.0: dependencies: minipass "^3.1.1" +ssri@^10.0.0, ssri@^10.0.1: + version "10.0.3" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.3.tgz#7f83da39058ca1d599d174e9eee4237659710bf4" + integrity sha512-lJtX/BFPI/VEtxZmLfeh7pzisIs6micwZ3eruD3+ds9aPsXKlYpwDS2Q7omD6WC42WO9+bnUSzlMmfv8uK8meg== + dependencies: + minipass "^4.0.0" + ssri@^8.0.0, ssri@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" @@ -10766,7 +11212,7 @@ symbol-observable@^2.0.3: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" integrity sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== -synckit@^0.8.4: +synckit@^0.8.5: version "0.8.5" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== @@ -10842,6 +11288,11 @@ temp-dir@1.0.0: resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" integrity sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ== +temp-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e" + integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== + temp@0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" @@ -10857,10 +11308,21 @@ temp@^0.8.4: dependencies: rimraf "~2.6.2" +tempy@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tempy/-/tempy-1.0.0.tgz#4f192b3ee3328a2684d0e3fc5c491425395aab65" + integrity sha512-eLXG5B1G0mRPHmgH2WydPl5v4jH35qEn3y/rA/aahKhIa91Pn119SsU7n7v/433gtT9ONzC8ISvNHIh2JSTm0w== + dependencies: + del "^6.0.0" + is-stream "^2.0.0" + temp-dir "^2.0.0" + type-fest "^0.16.0" + unique-string "^2.0.0" + terser@^5.15.0: - version "5.16.6" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.6.tgz#f6c7a14a378ee0630fbe3ac8d1f41b4681109533" - integrity sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg== + version "5.17.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.1.tgz#948f10830454761e2eeedc6debe45c532c83fd69" + integrity sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw== dependencies: "@jridgewell/source-map" "^0.3.2" acorn "^8.5.0" @@ -10990,10 +11452,10 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -treeverse@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-2.0.0.tgz#036dcef04bc3fd79a9b79a68d4da03e882d8a9ca" - integrity sha512-N5gJCkLu1aXccpOTtqV6ddSEi6ZmGkh3hjmbu1IjcavJK4qyOVQmi0myQKM7z5jVGmD68SJoliaVrMmVObhj6A== +treeverse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-3.0.0.tgz#dd82de9eb602115c6ebd77a574aae67003cb48c8" + integrity sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ== trim-newlines@^3.0.0: version "3.0.1" @@ -11001,9 +11463,9 @@ trim-newlines@^3.0.0: integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== ts-jest@^29.0.5: - version "29.0.5" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.0.5.tgz#c5557dcec8fe434fcb8b70c3e21c6b143bfce066" - integrity sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA== + version "29.1.0" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.0.tgz#4a9db4104a49b76d2b368ea775b6c9535c603891" + integrity sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" @@ -11049,9 +11511,9 @@ tsconfig-paths@^3.14.1: strip-bom "^3.0.0" tsconfig-paths@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz#4819f861eef82e6da52fb4af1e8c930a39ed979a" - integrity sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw== + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== dependencies: json5 "^2.2.2" minimist "^1.2.6" @@ -11062,7 +11524,7 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.5.0: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== @@ -11086,6 +11548,14 @@ tsyringe@^4.7.0: dependencies: tslib "^1.9.3" +tuf-js@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-1.1.4.tgz#e85a936b16859c7fae23e5f040bc0f7b559b3192" + integrity sha512-Lw2JRM3HTYhEtQJM2Th3aNCPbnXirtWMl065BawwmM2pX6XStH/ZO9e8T2hh0zk/HUa+1i6j+Lv6eDitKTau6A== + dependencies: + "@tufjs/models" "1.0.3" + make-fetch-happen "^11.0.1" + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -11098,6 +11568,11 @@ type-detect@4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860" + integrity sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg== + type-fest@^0.18.0: version "0.18.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" @@ -11134,9 +11609,9 @@ type-fest@^0.8.1: integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== type-fest@^3.2.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.6.1.tgz#cf8025edeebfd6cf48de73573a5e1423350b9993" - integrity sha512-htXWckxlT6U4+ilVgweNliPqlsVSSucbxVexRYllyMVJDtf5rTjv6kF/s+qAd4QSL1BZcnJPEJavYBPQiWuZDA== + version "3.8.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.8.0.tgz#ce80d1ca7c7d11c5540560999cbd410cb5b3a385" + integrity sha512-FVNSzGQz9Th+/9R6Lvv7WIAkstylfHN2/JYxkyhhmKFYh9At2DST8t6L6Lref9eYO8PXFTfG9Sg1Agg0K3vq3Q== type-is@~1.6.18: version "1.6.18" @@ -11262,6 +11737,13 @@ unique-filename@^2.0.0: dependencies: unique-slug "^3.0.0" +unique-filename@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== + dependencies: + unique-slug "^4.0.0" + unique-slug@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" @@ -11276,6 +11758,20 @@ unique-slug@^3.0.0: dependencies: imurmurhash "^0.1.4" +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== + dependencies: + imurmurhash "^0.1.4" + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + universal-user-agent@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" @@ -11304,15 +11800,15 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -upath@^2.0.1: +upath@2.0.1, upath@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== update-browserslist-db@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -11344,7 +11840,7 @@ utf8@^3.0.0: resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== @@ -11405,6 +11901,13 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +validate-npm-package-name@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz#f16afd48318e6f90a1ec101377fa0384cfc8c713" + integrity sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ== + dependencies: + builtins "^5.0.0" + validator@^13.7.0: version "13.9.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" @@ -11445,19 +11948,19 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: defaults "^1.0.3" web-did-resolver@^2.0.21: - version "2.0.21" - resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.21.tgz#065797dee3e37cd9f19261d04a90144fe576e5df" - integrity sha512-vKYz0s9spYfYrKhrF88F44lkofS1yj6TCF40+i077a7boru2BNROl5VZEIVL9jJRUDsNzvmVSKkq3kS8kZnB2Q== + version "2.0.23" + resolved "https://registry.yarnpkg.com/web-did-resolver/-/web-did-resolver-2.0.23.tgz#59806a8bc6f5709403929a3d2b49c06279580632" + integrity sha512-7yOKnY9E322cVFfVkpV6g2j7QWB3H32aezGn2VagBmTAQr74zf0hxRN0p/PzK/kcgnc/oDCIRuiWUGwJEJAh0w== dependencies: cross-fetch "^3.1.5" did-resolver "^4.0.0" -webcrypto-core@^1.7.4: - version "1.7.6" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.6.tgz#e32c4a12a13de4251f8f9ef336a6cba7cdec9b55" - integrity sha512-TBPiewB4Buw+HI3EQW+Bexm19/W4cP/qZG/02QJCXN+iN+T5sl074vZ3rJcle/ZtDBQSgjkbsQO/1eFcxnSBUA== +webcrypto-core@^1.7.7: + version "1.7.7" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.7.tgz#06f24b3498463e570fed64d7cab149e5437b162c" + integrity sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g== dependencies: - "@peculiar/asn1-schema" "^2.1.6" + "@peculiar/asn1-schema" "^2.3.6" "@peculiar/json-schema" "^1.1.12" asn1js "^3.0.1" pvtsutils "^1.3.2" @@ -11528,6 +12031,13 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +which@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/which/-/which-3.0.0.tgz#a9efd016db59728758a390d23f1687b6e8f59f8e" + integrity sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ== + dependencies: + isexe "^2.0.0" + wide-align@^1.1.0, wide-align@^1.1.2, wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" @@ -11593,7 +12103,7 @@ write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: imurmurhash "^0.1.4" signal-exit "^3.0.2" -write-file-atomic@^4.0.0, write-file-atomic@^4.0.2: +write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== @@ -11601,6 +12111,14 @@ write-file-atomic@^4.0.0, write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" +write-file-atomic@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.0.tgz#54303f117e109bf3d540261125c8ea5a7320fab0" + integrity sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + write-json-file@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a" From ef6164a3ba69d5135ae5671594e3ba82b606a05a Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 28 Apr 2023 13:57:25 -0300 Subject: [PATCH 131/139] ci: downgrade ubuntu docker image (#1442) * ci: downgrade ubuntu docker image Signed-off-by: Ariel Gentile * use new runners Signed-off-by: Ry Jones --------- Signed-off-by: Ariel Gentile Signed-off-by: Ry Jones Co-authored-by: Ry Jones --- .github/actions/setup-libssl/action.yml | 22 ------------------- .../setup-postgres-wallet-plugin/action.yml | 2 +- .github/workflows/continuous-deployment.yml | 4 ++-- .github/workflows/continuous-integration.yml | 16 ++++---------- Dockerfile | 12 ++-------- demo/package.json | 2 +- packages/anoncreds-rs/package.json | 4 ++-- packages/anoncreds/package.json | 2 +- yarn.lock | 18 +++++++-------- 9 files changed, 22 insertions(+), 60 deletions(-) delete mode 100644 .github/actions/setup-libssl/action.yml diff --git a/.github/actions/setup-libssl/action.yml b/.github/actions/setup-libssl/action.yml deleted file mode 100644 index 9710ea6e88..0000000000 --- a/.github/actions/setup-libssl/action.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Setup libSSL -description: Install libssl and libssl-dev 1.1 -author: 'gentilester@gmail.com' - -runs: - using: composite - steps: - - name: Install libssl1.1 - run: | - curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl1.1.deb - sudo dpkg -i libssl1.1.deb - shell: bash - - - name: Instal libssl-dev.1.1 - run: | - curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl-dev1.1.deb - sudo dpkg -i libssl-dev1.1.deb - shell: bash - -branding: - icon: scissors - color: purple diff --git a/.github/actions/setup-postgres-wallet-plugin/action.yml b/.github/actions/setup-postgres-wallet-plugin/action.yml index 81f41d3578..a03b2f3fde 100644 --- a/.github/actions/setup-postgres-wallet-plugin/action.yml +++ b/.github/actions/setup-postgres-wallet-plugin/action.yml @@ -10,7 +10,7 @@ runs: # so pointing rust version to 1.63.0 - name: Setup Postgres wallet plugin run: | - sudo apt-get install -y libzmq3-dev libsodium-dev pkg-config + sudo apt-get install -y libzmq3-dev libsodium-dev pkg-config libssl-dev curl https://sh.rustup.rs -sSf | bash -s -- -y export PATH="/root/.cargo/bin:${PATH}" rustup default 1.63.0 diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 94c44825b4..82d66fb8f7 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -7,7 +7,7 @@ on: jobs: release-canary: - runs-on: ubuntu-20.04 + runs-on: aries-ubuntu-2004 name: Release Canary if: "!startsWith(github.event.head_commit.message, 'chore(release): v')" steps: @@ -56,7 +56,7 @@ jobs: git push origin v${{ steps.get-version.outputs.version }} --no-verify release-stable: - runs-on: ubuntu-20.04 + runs-on: aries-ubuntu-2004 name: Create Stable Release # Only run if the last pushed commit is a release commit if: "startsWith(github.event.head_commit.message, 'chore(release): v')" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 7ac7f26bb5..19ea4923ba 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -27,7 +27,7 @@ jobs: # validation scripts. To still be able to run the CI we can manually trigger it by adding the 'ci-test' # label to the pull request ci-trigger: - runs-on: aries-ubuntu-latest + runs-on: aries-ubuntu-2004 outputs: triggered: ${{ steps.check.outputs.triggered }} steps: @@ -46,16 +46,13 @@ jobs: echo "::set-output name=triggered::${SHOULD_RUN}" validate: - runs-on: aries-ubuntu-latest + runs-on: aries-ubuntu-2004 name: Validate steps: - name: Checkout aries-framework-javascript uses: actions/checkout@v2 # setup dependencies - - name: Setup Libssl - uses: ./.github/actions/setup-libssl - - name: Setup Libindy uses: ./.github/actions/setup-libindy @@ -80,7 +77,7 @@ jobs: run: yarn build integration-test: - runs-on: aries-ubuntu-latest + runs-on: aries-ubuntu-2004 name: Integration Tests strategy: @@ -92,8 +89,6 @@ jobs: uses: actions/checkout@v2 # setup dependencies - - name: Setup Libssl - uses: ./.github/actions/setup-libssl - name: Setup Libindy uses: ./.github/actions/setup-libindy @@ -132,7 +127,7 @@ jobs: if: always() version-stable: - runs-on: aries-ubuntu-latest + runs-on: aries-ubuntu-2004 name: Release stable needs: [integration-test, validate] if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' @@ -144,9 +139,6 @@ jobs: fetch-depth: 0 # setup dependencies - - name: Setup Libssl - uses: ./.github/actions/setup-libssl - - name: Setup Libindy uses: ./.github/actions/setup-libindy diff --git a/Dockerfile b/Dockerfile index 7f55d81dfe..cd68166f9e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 as base +FROM ubuntu:20.04 as base ENV DEBIAN_FRONTEND noninteractive @@ -9,15 +9,7 @@ RUN apt-get update -y && apt-get install -y \ # Only needed to build indy-sdk build-essential \ git \ - libzmq3-dev libsodium-dev pkg-config - -# libssl1.1 (required by libindy) -RUN curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl1.1.deb -RUN dpkg -i libssl1.1.deb - -# libssl-dev1.1 (required to compile libindy with posgres plugin) -RUN curl http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.1.1-1ubuntu2.1~18.04.21_amd64.deb -o libssl-dev1.1.deb -RUN dpkg -i libssl-dev1.1.deb + libzmq3-dev libsodium-dev pkg-config libssl-dev # libindy RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 diff --git a/demo/package.json b/demo/package.json index 85b1951908..16ae0447fe 100644 --- a/demo/package.json +++ b/demo/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@hyperledger/indy-vdr-nodejs": "^0.1.0-dev.14", - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.15", "@hyperledger/aries-askar-nodejs": "^0.1.0-dev.8", "inquirer": "^8.2.5" }, diff --git a/packages/anoncreds-rs/package.json b/packages/anoncreds-rs/package.json index e4a42ac41a..633ea1f08e 100644 --- a/packages/anoncreds-rs/package.json +++ b/packages/anoncreds-rs/package.json @@ -26,14 +26,14 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@aries-framework/anoncreds": "0.3.3", - "@hyperledger/anoncreds-shared": "^0.1.0-dev.14", + "@hyperledger/anoncreds-shared": "^0.1.0-dev.15", "class-transformer": "^0.5.1", "class-validator": "0.14.0", "rxjs": "^7.2.0", "tsyringe": "^4.7.0" }, "devDependencies": { - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.15", "reflect-metadata": "^0.1.13", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 5104079b86..fabc377594 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -32,7 +32,7 @@ }, "devDependencies": { "@aries-framework/node": "0.3.3", - "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.14", + "@hyperledger/anoncreds-nodejs": "^0.1.0-dev.15", "indy-sdk": "^1.16.0-dev-1636", "rimraf": "^4.4.0", "rxjs": "^7.8.0", diff --git a/yarn.lock b/yarn.lock index b6a201b761..9ce029601f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1036,12 +1036,12 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@hyperledger/anoncreds-nodejs@^0.1.0-dev.14": - version "0.1.0-dev.14" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.14.tgz#00d4dca419f04a580cc02611d0c803d3d5c04fbf" - integrity sha512-N0H89rRBOaLeX+j5NHSibU1m88lWOXWg2t/orZZtY9iTt8op1oFV0IcWHeUJ1lPDJnOiozpn+AF/f5G0kipK7w== +"@hyperledger/anoncreds-nodejs@^0.1.0-dev.15": + version "0.1.0-dev.15" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-nodejs/-/anoncreds-nodejs-0.1.0-dev.15.tgz#8d248f53c318d91e82030a7fb23740fe4b2bef7a" + integrity sha512-3QiKzjVhbQ+N07vMiR0XCBxxy51RwhKf/z0/mHiVYy1ZcmuTok5dE/jTjAwmHh+jvuAwqk+O4ebWmFItRx1K7Q== dependencies: - "@hyperledger/anoncreds-shared" "0.1.0-dev.14" + "@hyperledger/anoncreds-shared" "0.1.0-dev.15" "@mapbox/node-pre-gyp" "^1.0.10" ffi-napi "4.0.3" node-cache "5.1.2" @@ -1049,10 +1049,10 @@ ref-napi "3.0.3" ref-struct-di "1.1.1" -"@hyperledger/anoncreds-shared@0.1.0-dev.14", "@hyperledger/anoncreds-shared@^0.1.0-dev.14": - version "0.1.0-dev.14" - resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.14.tgz#0cdaa18a59b8223bb3eb2116970b1aa56cf5acfc" - integrity sha512-4yS7NgZZF9nfE50OquvbLWuZSzjOOiPufC/n2Ywb5lL2VloBblXMI2CezCZU1POmyAl7xnoT99pi2Od1fQaJxQ== +"@hyperledger/anoncreds-shared@0.1.0-dev.15", "@hyperledger/anoncreds-shared@^0.1.0-dev.15": + version "0.1.0-dev.15" + resolved "https://registry.yarnpkg.com/@hyperledger/anoncreds-shared/-/anoncreds-shared-0.1.0-dev.15.tgz#644e9cbc16a174f46b2f54dd7cb215a251d3da0a" + integrity sha512-nTO5KDTlDxpadk1j/r5T8E4wfS16rWKgZpyyG6dxg/7WhwZQkIcTsbPNnPH+NHklGju/ee+WT7rWlojpJ6XFVQ== "@hyperledger/aries-askar-nodejs@^0.1.0-dev.8": version "0.1.0-dev.8" From 83cf387fa52bb51d8adb2d5fedc5111994d4dde1 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 1 May 2023 12:33:16 +0200 Subject: [PATCH 132/139] fix: small issues with migration and WAL files (#1443) Signed-off-by: Timo Glastra --- packages/askar/src/wallet/AskarWallet.ts | 5 ++++ .../src/IndySdkToAskarMigrationUpdater.ts | 29 ++++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index d850a18f3f..1ca7c91286 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -602,7 +602,12 @@ export class AskarWallet implements Wallet { try { cek = AskarKey.generate(KeyAlgs.Chacha20C20P) + senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined + if (senderVerkey && !senderKey) { + throw new WalletError(`Unable to pack message. Sender key ${senderVerkey} not found in wallet.`) + } + senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined const recipients: JweRecipient[] = [] diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index 006c3657a0..76a1fea5d3 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -101,7 +101,7 @@ export class IndySdkToAskarMigrationUpdater { } /* - * Checks whether the destination locations are allready used. This might + * Checks whether the destination locations are already used. This might * happen if you want to migrate a wallet when you already have a new wallet * with the same id. */ @@ -130,10 +130,20 @@ export class IndySdkToAskarMigrationUpdater { return `${this.fs.tempPath}/${this.walletConfig.id}.db` } + private async copyDatabaseWithOptionalWal(src: string, dest: string) { + // Copy the supplied database to the backup destination + await this.fs.copyFile(src, dest) + + // If a wal-file is included, also copy it (https://www.sqlite.org/wal.html) + if (await this.fs.exists(`${src}-wal`)) { + await this.fs.copyFile(`${src}-wal`, `${dest}-wal`) + } + } + /** * Backup the database file. This function makes sure that the the indy-sdk * database file is backed up within our temporary directory path. If some - * error occurs, `this.revertDatbase()` will be called to revert the backup. + * error occurs, `this.revertDatabase()` will be called to revert the backup. */ private async backupDatabase() { const src = this.dbPath @@ -143,8 +153,8 @@ export class IndySdkToAskarMigrationUpdater { // Create the directories for the backup await this.fs.createDirectory(dest) - // Copy the supplied database to the backup destination - await this.fs.copyFile(src, dest) + // Copy the supplied database to the backup destination, with optional wal-file + await this.copyDatabaseWithOptionalWal(src, dest) if (!(await this.fs.exists(dest))) { throw new IndySdkToAskarMigrationError('Could not locate the new backup file') @@ -158,6 +168,11 @@ export class IndySdkToAskarMigrationUpdater { private async cleanBackup() { this.agent.config.logger.trace(`Deleting the backup file at '${this.backupFile}'`) await this.fs.delete(this.backupFile) + + // Also delete wal-file if it exists + if (await this.fs.exists(`${this.backupFile}-wal`)) { + await this.fs.delete(`${this.backupFile}-wal`) + } } /** @@ -174,8 +189,8 @@ export class IndySdkToAskarMigrationUpdater { this.agent.config.logger.trace(`Moving upgraded database from ${src} to ${dest}`) - // Copy the file from the database path to the new location - await this.fs.copyFile(src, dest) + // Copy the file from the database path to the new location, with optional wal-file + await this.copyDatabaseWithOptionalWal(src, dest) } /** @@ -253,7 +268,7 @@ export class IndySdkToAskarMigrationUpdater { const keySk = TypedArrayEncoder.fromBase58(signKey) const key = Key.fromSecretBytes({ algorithm: KeyAlgs.Ed25519, - secretKey: keySk.subarray(0, 32), + secretKey: new Uint8Array(keySk.slice(0, 32)), }) await txn.insertKey({ name: row.name, key }) From 9a43afec7ea72a6fa8c6133f0fad05d8a3d2a595 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 1 May 2023 14:26:50 +0200 Subject: [PATCH 133/139] fix: migration of link secret (#1444) Signed-off-by: Timo Glastra --- packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts | 6 +++--- .../src/IndySdkToAskarMigrationUpdater.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts b/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts index 3ad404e225..d665084092 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/linkSecret.ts @@ -12,9 +12,9 @@ export async function migrateLinkSecretToV0_4(agent: Ag const linkSecretRepository = agent.dependencyManager.resolve(AnonCredsLinkSecretRepository) agent.config.logger.debug(`Fetching default link secret record from storage`) - const defaultLinkdSecret = await linkSecretRepository.findDefault(agent.context) + const defaultLinkSecret = await linkSecretRepository.findDefault(agent.context) - if (!defaultLinkdSecret) { + if (!defaultLinkSecret) { // If no default link secret record exists, we create one based on the wallet id and set is as default agent.config.logger.debug(`No default link secret record found. Creating one based on wallet id.`) @@ -35,7 +35,7 @@ export async function migrateLinkSecretToV0_4(agent: Ag await linkSecretRepository.save(agent.context, linkSecret) } else { agent.config.logger.debug( - `Default link secret record with record id ${defaultLinkdSecret.id} and link secret id ${defaultLinkdSecret.linkSecretId} found. Skipping...` + `Default link secret record with record id ${defaultLinkSecret.id} and link secret id ${defaultLinkSecret.linkSecretId} found. Skipping...` ) } diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index 76a1fea5d3..0af8f27508 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -337,7 +337,7 @@ export class IndySdkToAskarMigrationUpdater { for (const row of masterSecrets) { this.agent.config.logger.debug(`Migrating ${row.name} to the new askar format`) - const isDefault = masterSecrets.length === 0 ?? row.name === this.walletConfig.id + const isDefault = masterSecrets.length === 0 || row.name === this.walletConfig.id const { value: { ms }, From 414595727d611ff774c4f404a4eeea509cf03a71 Mon Sep 17 00:00:00 2001 From: Alexander Shenshin <93187809+AlexanderShenshin@users.noreply.github.com> Date: Wed, 3 May 2023 09:55:34 +0300 Subject: [PATCH 134/139] fix: Emit RoutingCreated event for mediator routing record (#1445) --- .../src/modules/routing/services/MediatorService.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/core/src/modules/routing/services/MediatorService.ts b/packages/core/src/modules/routing/services/MediatorService.ts index 8bd916e9a3..751f928a60 100644 --- a/packages/core/src/modules/routing/services/MediatorService.ts +++ b/packages/core/src/modules/routing/services/MediatorService.ts @@ -212,6 +212,17 @@ export class MediatorService { await this.mediatorRoutingRepository.save(agentContext, routingRecord) + this.eventEmitter.emit(agentContext, { + type: RoutingEventTypes.RoutingCreatedEvent, + payload: { + routing: { + endpoints: agentContext.config.endpoints, + routingKeys: [], + recipientKey: routingKey, + }, + }, + }) + return routingRecord } From 61daf0cb27de80a5e728e2e9dad13d729baf476c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 8 May 2023 22:37:31 +0200 Subject: [PATCH 135/139] fix: small updates to cheqd module and demo (#1439) Signed-off-by: Timo Glastra --- DEVREADME.md | 22 +- demo/src/Alice.ts | 5 + demo/src/BaseAgent.ts | 16 +- demo/src/Faber.ts | 55 ++-- packages/askar/src/wallet/AskarWallet.ts | 2 +- packages/cheqd/src/dids/CheqdDidRegistrar.ts | 28 +- packages/cheqd/src/dids/CheqdDidResolver.ts | 12 +- packages/cheqd/src/dids/didCheqdUtil.ts | 13 +- .../tests/cheqd-did-registrar.e2e.test.ts | 11 +- .../cheqd/tests/cheqd-did-utils.e2e.test.ts | 2 +- yarn.lock | 260 +++++++++--------- 11 files changed, 228 insertions(+), 198 deletions(-) diff --git a/DEVREADME.md b/DEVREADME.md index e6bf748ab4..595f7bae5f 100644 --- a/DEVREADME.md +++ b/DEVREADME.md @@ -47,7 +47,7 @@ docker pull postgres docker run --name postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres ``` -### Setup Ledger +### Setup Indy Ledger For testing we've added a setup to this repo that allows you to quickly setup an indy ledger. @@ -74,6 +74,20 @@ docker exec indy-pool add-did-from-seed 000000000000000000000000Trustee9 TRUSTEE # docker exec indy-pool add-did "NkGXDEPgpFGjQKMYmz6SyF" "CrSA1WbYYWLJoHm16Xw1VEeWxFvXtWjtsfEzMsjB5vDT" ``` +### Setup Cheqd Ledger + +In addition, there's also a docker command to run a cheqd test network. + +```sh +docker run --rm -d -p 26657:26657 ghcr.io/cheqd/cheqd-testnet:latest +``` + +If you want to run tests without the cheqd ledger, you can use the following ignore pattern: + +```sh +yarn test --testPathIgnorePatterns packages/cheqd +``` + ### Run all tests You can run the tests using the following command. @@ -91,13 +105,13 @@ GENESIS_TXN_PATH=network/genesis/local-genesis.txn TEST_AGENT_PUBLIC_DID_SEED=00 Locally, you might want to run the tests without postgres tests. You can do that by ignoring the tests: ```sh -yarn test --testPathIgnorePatterns ./packages/indy-sdk/tests/postgres.e2e.test.ts -u +yarn test --testPathIgnorePatterns postgres.e2e.test.ts ``` -In case you run into trouble running the tests, e.g. complaining about snapshots not being up-to-date, you can try and remove the data stored for the indy-client. On a Unix system with default setup you achieve this by running: +In case you run into trouble running the tests, e.g. complaining about snapshots not being up-to-date, you can try and remove the data stored for the indy-client or AFJ. Note this removes all wallets and data, so make sure you're okay with all data being removed. On a Unix system with default setup you achieve this by running: ```sh -rm -rf ~/.indy-client +rm -rf ~/.indy-client ~/.afj ``` ## Usage with Docker diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 2de378d8c1..8374e27238 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -46,6 +46,11 @@ export class Alice extends BaseAgent { } public async acceptCredentialOffer(credentialRecord: CredentialExchangeRecord) { + const linkSecretIds = await this.agent.modules.anoncreds.getLinkSecretIds() + if (linkSecretIds.length === 0) { + await this.agent.modules.anoncreds.createLinkSecret() + } + await this.agent.credentials.acceptOffer({ credentialRecordId: credentialRecord.id, }) diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index ac0281fbff..c2e787e32a 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -3,7 +3,9 @@ import type { IndySdkPoolConfig } from '@aries-framework/indy-sdk' import type { IndyVdrPoolConfig } from '@aries-framework/indy-vdr' import { + AnonCredsCredentialFormatService, AnonCredsModule, + AnonCredsProofFormatService, LegacyIndyCredentialFormatService, LegacyIndyProofFormatService, V1CredentialProtocol, @@ -31,7 +33,7 @@ import { HttpOutboundTransport, } from '@aries-framework/core' import { IndySdkAnonCredsRegistry, IndySdkModule, IndySdkSovDidResolver } from '@aries-framework/indy-sdk' -import { IndyVdrAnonCredsRegistry, IndyVdrModule, IndyVdrSovDidResolver } from '@aries-framework/indy-vdr' +import { IndyVdrIndyDidResolver, IndyVdrAnonCredsRegistry, IndyVdrModule } from '@aries-framework/indy-vdr' import { agentDependencies, HttpInboundTransport } from '@aries-framework/node' import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' @@ -46,7 +48,7 @@ const bcovrin = `{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","blsk {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node3","blskey":"3WFpdbg7C5cnLYZwFZevJqhubkFALBfCBBok15GdrKMUhUjGsk3jV6QKj6MZgEubF7oqCafxNdkm7eswgA4sdKTRc82tLGzZBd6vNqU8dupzup6uYUf32KTHTPQbuUM8Yk4QFXjEf2Usu2TJcNkdgpyeUSX42u5LqdDDpNSWUK5deC5","blskey_pop":"QwDeb2CkNSx6r8QC8vGQK3GRv7Yndn84TGNijX8YXHPiagXajyfTjoR87rXUu4G4QLk2cF8NNyqWiYMus1623dELWwx57rLCFqGh7N4ZRbGDRP4fnVcaKg1BcUxQ866Ven4gw8y4N56S5HzxXNBZtLYmhGHvDtk6PFkFwCvxYrNYjh","client_ip":"138.197.138.255","client_port":9706,"node_ip":"138.197.138.255","node_port":9705,"services":["VALIDATOR"]},"dest":"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya"},"metadata":{"from":"4cU41vWW82ArfxJxHkzXPG"},"type":"0"},"txnMetadata":{"seqNo":3,"txnId":"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4"},"ver":"1"} {"reqSignature":{},"txn":{"data":{"data":{"alias":"Node4","blskey":"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw","blskey_pop":"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP","client_ip":"138.197.138.255","client_port":9708,"node_ip":"138.197.138.255","node_port":9707,"services":["VALIDATOR"]},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"},"metadata":{"from":"TWwCRQRZ2ZHMJFn9TzLp7W"},"type":"0"},"txnMetadata":{"seqNo":4,"txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"},"ver":"1"}` -const indyNetworkConfig = { +export const indyNetworkConfig = { // Need unique network id as we will have multiple agent processes in the agent id: randomUUID(), genesisTransactions: bcovrin, @@ -55,7 +57,7 @@ const indyNetworkConfig = { connectOnStartup: true, } satisfies IndySdkPoolConfig | IndyVdrPoolConfig -type DemoAgent = Agent | ReturnType> +type DemoAgent = Agent> export class BaseAgent { public port: number @@ -92,7 +94,7 @@ export class BaseAgent { this.agent = new Agent({ config, dependencies: agentDependencies, - modules: useLegacyIndySdk ? getLegacyIndySdkModules() : getAskarAnonCredsIndyModules(), + modules: getAskarAnonCredsIndyModules(), }) this.agent.registerInboundTransport(new HttpInboundTransport({ port })) this.agent.registerOutboundTransport(new HttpOutboundTransport()) @@ -120,7 +122,7 @@ function getAskarAnonCredsIndyModules() { indyCredentialFormat: legacyIndyCredentialFormatService, }), new V2CredentialProtocol({ - credentialFormats: [legacyIndyCredentialFormatService], + credentialFormats: [legacyIndyCredentialFormatService, new AnonCredsCredentialFormatService()], }), ], }), @@ -131,7 +133,7 @@ function getAskarAnonCredsIndyModules() { indyProofFormat: legacyIndyProofFormatService, }), new V2ProofProtocol({ - proofFormats: [legacyIndyProofFormatService], + proofFormats: [legacyIndyProofFormatService, new AnonCredsProofFormatService()], }), ], }), @@ -157,7 +159,7 @@ function getAskarAnonCredsIndyModules() { }) ), dids: new DidsModule({ - resolvers: [new IndyVdrSovDidResolver(), new CheqdDidResolver()], + resolvers: [new IndyVdrIndyDidResolver(), new CheqdDidResolver()], registrars: [new CheqdDidRegistrar()], }), askar: new AskarModule({ diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index 0aae2e702f..0fa9c7e537 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -5,7 +5,7 @@ import type BottomBar from 'inquirer/lib/ui/bottom-bar' import { KeyType, TypedArrayEncoder, utils, ConnectionEventTypes } from '@aries-framework/core' import { ui } from 'inquirer' -import { BaseAgent } from './BaseAgent' +import { BaseAgent, indyNetworkConfig } from './BaseAgent' import { Color, greenText, Output, purpleText, redText } from './OutputClass' export enum RegistryOptions { @@ -33,28 +33,23 @@ export class Faber extends BaseAgent { public async importDid(registry: string) { // NOTE: we assume the did is already registered on the ledger, we just store the private key in the wallet // and store the existing did in the wallet - const privateKey = TypedArrayEncoder.fromString('afjdemoverysercure00000000000000') - const key = await this.agent.wallet.createKey({ - keyType: KeyType.Ed25519, - privateKey, + // indy did is based on private key (seed) + const unqualifiedIndyDid = '2jEvRuKmfBJTRa7QowDpNN' + const cheqdDid = 'did:cheqd:testnet:d37eba59-513d-42d3-8f9f-d1df0548b675' + const indyDid = `did:indy:${indyNetworkConfig.indyNamespace}:${unqualifiedIndyDid}` + + const did = registry === RegistryOptions.indy ? indyDid : cheqdDid + await this.agent.dids.import({ + did, + overwrite: true, + privateKeys: [ + { + keyType: KeyType.Ed25519, + privateKey: TypedArrayEncoder.fromString('afjdemoverysercure00000000000000'), + }, + ], }) - // did is first 16 bytes of public key encoded as base58 - const unqualifiedIndyDid = TypedArrayEncoder.toBase58(key.publicKey.slice(0, 16)) - const cheqdDid = 'did:cheqd:testnet:2d6841a0-8614-44c0-95c5-d54c61e420f2' - switch (registry) { - case RegistryOptions.indy: - await this.agent.dids.import({ - did: `did:sov:${unqualifiedIndyDid}`, - }) - this.anonCredsIssuerId = unqualifiedIndyDid - break - case RegistryOptions.cheqd: - await this.agent.dids.import({ - did: cheqdDid, - }) - this.anonCredsIssuerId = cheqdDid - break - } + this.anonCredsIssuerId = did } private async getConnectionRecord() { @@ -149,9 +144,7 @@ export class Faber extends BaseAgent { const { schemaState } = await this.agent.modules.anoncreds.registerSchema({ schema: schemaTemplate, - options: { - didIndyNamespace: 'bcovrin:test', - }, + options: {}, }) if (schemaState.state !== 'finished') { @@ -175,9 +168,7 @@ export class Faber extends BaseAgent { issuerId: this.anonCredsIssuerId, tag: 'latest', }, - options: { - didIndyNamespace: 'bcovrin:test', - }, + options: {}, }) if (credentialDefinitionState.state !== 'finished') { @@ -202,9 +193,9 @@ export class Faber extends BaseAgent { await this.agent.credentials.offerCredential({ connectionId: connectionRecord.id, - protocolVersion: 'v1', + protocolVersion: 'v2', credentialFormats: { - indy: { + anoncreds: { attributes: [ { name: 'name', @@ -255,10 +246,10 @@ export class Faber extends BaseAgent { await this.printProofFlow(greenText('\nRequesting proof...\n', false)) await this.agent.proofs.requestProof({ - protocolVersion: 'v1', + protocolVersion: 'v2', connectionId: connectionRecord.id, proofFormats: { - indy: { + anoncreds: { name: 'proof-request', version: '1.0', requested_attributes: proofAttribute, diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 1ca7c91286..62aa371b5a 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -526,7 +526,7 @@ export class AskarWallet implements Wallet { if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) } - throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}`, { cause: error }) + throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}. ${error.message}`, { cause: error }) } } diff --git a/packages/cheqd/src/dids/CheqdDidRegistrar.ts b/packages/cheqd/src/dids/CheqdDidRegistrar.ts index 37de51ad1f..a23ecf1456 100644 --- a/packages/cheqd/src/dids/CheqdDidRegistrar.ts +++ b/packages/cheqd/src/dids/CheqdDidRegistrar.ts @@ -5,13 +5,13 @@ import type { DidCreateResult, DidDeactivateResult, DidUpdateResult, - DidDocument, VerificationMethod, } from '@aries-framework/core' import type { CheqdNetwork, DIDDocument, DidStdFee, TVerificationKey, VerificationMethods } from '@cheqd/sdk' import type { SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' import { + DidDocument, DidDocumentRole, DidRecord, DidRepository, @@ -21,6 +21,7 @@ import { utils, TypedArrayEncoder, getKeyFromVerificationMethod, + JsonTransformer, } from '@aries-framework/core' import { MethodSpecificIdAlgo, createDidVerificationMethod } from '@cheqd/sdk' import { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' @@ -65,28 +66,43 @@ export class CheqdDidRegistrar implements DidRegistrar { keyType: KeyType.Ed25519, privateKey: privateKey, }) + didDocument = generateDidDoc({ verificationMethod: verificationMethod.type as VerificationMethods, verificationMethodId: verificationMethod.id || 'key-1', methodSpecificIdAlgo: (methodSpecificIdAlgo as MethodSpecificIdAlgo) || MethodSpecificIdAlgo.Uuid, network: network as CheqdNetwork, publicKey: TypedArrayEncoder.toHex(key.publicKey), - }) satisfies DidDocument + }) + + const contextMapping = { + Ed25519VerificationKey2018: 'https://w3id.org/security/suites/ed25519-2018/v1', + Ed25519VerificationKey2020: 'https://w3id.org/security/suites/ed25519-2020/v1', + JsonWebKey2020: 'https://w3id.org/security/suites/jws-2020/v1', + } + const contextUrl = contextMapping[verificationMethod.type] + + // Add the context to the did document + // NOTE: cheqd sdk uses https://www.w3.org/ns/did/v1 while AFJ did doc uses https://w3id.org/did/v1 + // We should align these at some point. For now we just return a consistent value. + didDocument.context = ['https://www.w3.org/ns/did/v1', contextUrl] } else { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Provide a didDocument or atleast one verificationMethod with seed in secret', + reason: 'Provide a didDocument or at least one verificationMethod with seed in secret', }, } } - const payloadToSign = await createMsgCreateDidDocPayloadToSign(didDocument as DIDDocument, versionId) + const didDocumentJson = didDocument.toJSON() as DIDDocument + + const payloadToSign = await createMsgCreateDidDocPayloadToSign(didDocumentJson, versionId) const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) - const response = await cheqdLedgerService.create(didDocument as DIDDocument, signInputs, versionId) + const response = await cheqdLedgerService.create(didDocumentJson, signInputs, versionId) if (response.code !== 0) { throw new Error(`${response.rawLog}`) } @@ -263,7 +279,7 @@ export class CheqdDidRegistrar implements DidRegistrar { didState: { state: 'finished', did: didDocument.id, - didDocument: didDocument as DidDocument, + didDocument: JsonTransformer.fromJSON(didDocument, DidDocument), secret: options.secret, }, } diff --git a/packages/cheqd/src/dids/CheqdDidResolver.ts b/packages/cheqd/src/dids/CheqdDidResolver.ts index d599e8d596..edd94c2aa1 100644 --- a/packages/cheqd/src/dids/CheqdDidResolver.ts +++ b/packages/cheqd/src/dids/CheqdDidResolver.ts @@ -1,8 +1,8 @@ import type { ParsedCheqdDid } from '../anoncreds/utils/identifiers' -import type { AgentContext, DidDocument, DidResolutionResult, DidResolver, ParsedDid } from '@aries-framework/core' +import type { AgentContext, DidResolutionResult, DidResolver, ParsedDid } from '@aries-framework/core' import type { Metadata } from '@cheqd/ts-proto/cheqd/resource/v2' -import { AriesFrameworkError, utils } from '@aries-framework/core' +import { DidDocument, AriesFrameworkError, utils, JsonTransformer } from '@aries-framework/core' import { cheqdDidMetadataRegex, @@ -132,7 +132,7 @@ export class CheqdDidResolver implements DidResolver { const { didDocumentVersionsMetadata } = await cheqdLedgerService.resolveMetadata(did) return { - didDocument: { id: did } as DidDocument, + didDocument: new DidDocument({ id: did }), didDocumentMetadata: didDocumentVersionsMetadata, didResolutionMetadata: {}, } @@ -144,7 +144,7 @@ export class CheqdDidResolver implements DidResolver { const metadata = await cheqdLedgerService.resolveCollectionResources(did, id) return { - didDocument: { id: did } as DidDocument, + didDocument: new DidDocument({ id: did }), didDocumentMetadata: { linkedResourceMetadata: metadata, }, @@ -168,7 +168,7 @@ export class CheqdDidResolver implements DidResolver { const metadata = await cheqdLedgerService.resolveResourceMetadata(did, id, resourceId) return { - didDocument: { id: did } as DidDocument, + didDocument: new DidDocument({ id: did }), didDocumentMetadata: { linkedResourceMetadata: metadata, }, @@ -184,7 +184,7 @@ export class CheqdDidResolver implements DidResolver { didDocumentMetadata.linkedResourceMetadata = resources return { - didDocument: didDocument as DidDocument, + didDocument: JsonTransformer.fromJSON(didDocument, DidDocument), didDocumentMetadata, didResolutionMetadata: {}, } diff --git a/packages/cheqd/src/dids/didCheqdUtil.ts b/packages/cheqd/src/dids/didCheqdUtil.ts index 37a2c94ad1..97c193292e 100644 --- a/packages/cheqd/src/dids/didCheqdUtil.ts +++ b/packages/cheqd/src/dids/didCheqdUtil.ts @@ -1,8 +1,13 @@ -import type { DidDocument } from '@aries-framework/core' import type { CheqdNetwork, DIDDocument, MethodSpecificIdAlgo, TVerificationKey } from '@cheqd/sdk' import type { Metadata } from '@cheqd/ts-proto/cheqd/resource/v2' -import { AriesFrameworkError, JsonEncoder, TypedArrayEncoder } from '@aries-framework/core' +import { + DidDocument, + AriesFrameworkError, + JsonEncoder, + TypedArrayEncoder, + JsonTransformer, +} from '@aries-framework/core' import { createDidPayload, createDidVerificationMethod, @@ -102,8 +107,8 @@ export function generateDidDoc(options: IDidDocOptions) { throw new Error('Invalid DID options') } const verificationMethods = createDidVerificationMethod([verificationMethod], [verificationKeys]) - - return createDidPayload(verificationMethods, [verificationKeys]) as DidDocument + const didPayload = createDidPayload(verificationMethods, [verificationKeys]) + return JsonTransformer.fromJSON(didPayload, DidDocument) } export interface IDidDocOptions { diff --git a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts index e2b8e1dd1b..2bf12aeb95 100644 --- a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts @@ -1,7 +1,7 @@ import type { CheqdDidCreateOptions } from '../src' import type { DidDocument } from '@aries-framework/core' -import { Agent, TypedArrayEncoder } from '@aries-framework/core' +import { Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' import { generateKeyPairFromSeed } from '@stablelib/ed25519' import { getAgentOptions } from '../../core/tests/helpers' @@ -94,6 +94,7 @@ describe('Cheqd DID registrar', () => { methodSpecificIdAlgo: 'base58btc', }, }) + expect(createResult).toMatchObject({ didState: { state: 'finished', @@ -119,12 +120,8 @@ describe('Cheqd DID registrar', () => { }) const deactivateResult = await agent.dids.deactivate({ did }) - expect(deactivateResult).toMatchObject({ - didState: { - state: 'finished', - didDocument, - }, - }) + expect(deactivateResult.didState.didDocument?.toJSON()).toMatchObject(didDocument.toJSON()) + expect(deactivateResult.didState.state).toEqual('finished') const resolvedDocument = await agent.dids.resolve(did) expect(resolvedDocument.didDocumentMetadata.deactivated).toBe(true) diff --git a/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts b/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts index b5cc7b0401..cec86ac799 100644 --- a/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-utils.e2e.test.ts @@ -37,7 +37,7 @@ describe('Test Cheqd Did Utils', () => { }) it('should create MsgCreateDidDocPayloadToSign', async () => { - const result = await createMsgCreateDidDocPayloadToSign(validDidDoc() as DIDDocument, '1.0') + const result = await createMsgCreateDidDocPayloadToSign(validDidDoc().toJSON() as DIDDocument, '1.0') expect(result).toBeDefined() }) diff --git a/yarn.lock b/yarn.lock index 9ce029601f..5b6b66c9c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -753,9 +753,9 @@ integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@cheqd/sdk@cjs": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.2.0.tgz#0b07c0e2337fbe2bd7d62a49ce9fb4515711b52a" - integrity sha512-HOhm6tqtX/h1ccoPuNEpF9R8tACbwDAshhjtBeV77FdbTfKN891xpjrbpnmlHQ6D8DlWEQwWIYPwDXZ0EmZWBQ== + version "2.3.0" + resolved "https://registry.yarnpkg.com/@cheqd/sdk/-/sdk-2.3.0.tgz#0594ccb501d1ad74f3360fa5beb12f002dc8e8d2" + integrity sha512-ncHwBdAAyauuLLWHXUNzUJo6Y7z02nhNOt87n2owUhvf5K2UbNcHHB/CzNwcQIc7OGcShPABydug8KE1ueNk/Q== dependencies: "@cheqd/ts-proto" "^2.2.0" "@cosmjs/amino" "^0.29.5" @@ -990,10 +990,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.38.0": - version "8.38.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.38.0.tgz#73a8a0d8aa8a8e6fe270431c5e72ae91b5337892" - integrity sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g== +"@eslint/js@8.39.0": + version "8.39.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.39.0.tgz#58b536bcc843f4cd1e02a7e6171da5c040f4d44b" + integrity sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng== "@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": version "1.1.3" @@ -1929,10 +1929,10 @@ resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-14.0.0.tgz#949c5019028c93f189abbc2fb42f333290f7134a" integrity sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw== -"@octokit/openapi-types@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-16.0.0.tgz#d92838a6cd9fb4639ca875ddb3437f1045cc625e" - integrity sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA== +"@octokit/openapi-types@^17.0.0": + version "17.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-17.0.0.tgz#7356d287f48b20e9a1f497ef8dfaabdff9cf8622" + integrity sha512-V8BVJGN0ZmMlURF55VFHFd/L92XQQ43KvFjNmY1IYbCN3V/h/uUFV6iQi19WEHM395Nn+1qhUbViCAD/1czzog== "@octokit/plugin-enterprise-rest@6.0.1": version "6.0.1" @@ -2005,11 +2005,11 @@ "@octokit/openapi-types" "^14.0.0" "@octokit/types@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.0.0.tgz#6050db04ddf4188ec92d60e4da1a2ce0633ff635" - integrity sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw== + version "9.1.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.1.2.tgz#1a8d35b1f4a3d2ad386e223f249dd5f7506979c1" + integrity sha512-LPbJIuu1WNoRHbN4UMysEdlissRFpTCWyoKT7kHPufI8T+XX33/qilfMWJo3mCOjNIKu0+43oSQPf+HJa0+TTQ== dependencies: - "@octokit/openapi-types" "^16.0.0" + "@octokit/openapi-types" "^17.0.0" "@parcel/watcher@2.0.4": version "2.0.4" @@ -2693,9 +2693,9 @@ "@types/istanbul-lib-report" "*" "@types/jest@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.0.tgz#337b90bbcfe42158f39c2fb5619ad044bbb518ac" - integrity sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg== + version "29.5.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.1.tgz#83c818aa9a87da27d6da85d3378e5a34d2f31a47" + integrity sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -2744,9 +2744,9 @@ form-data "^3.0.0" "@types/node@*", "@types/node@>=13.7.0", "@types/node@^16.11.7": - version "16.18.23" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.23.tgz#b6e934fe427eb7081d0015aad070acb3373c3c90" - integrity sha512-XAMpaw1s1+6zM+jn2tmw8MyaRDIJfXxqmIQIS0HfoGYPuf7dUWeiUKopwq13KFX9lEp1+THGtlaaYx39Nxr58g== + version "16.18.24" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.24.tgz#f21925dd56cd3467b4e1e0c5071d0f2af5e9a316" + integrity sha512-zvSN2Esek1aeLdKDYuntKAYjti9Z2oT4I8bfkLLhIxHlv3dwZ5vvATxOc31820iYm4hQRCwjUgDpwSMFjfTUnw== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2875,14 +2875,14 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.48.1": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.0.tgz#c0e10eeb936debe5d1c3433cf36206a95befefd0" - integrity sha512-p0QgrEyrxAWBecR56gyn3wkG15TJdI//eetInP3zYRewDh0XS+DhB3VUAd3QqvziFsfaQIoIuZMxZRB7vXYaYw== + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz#9b09ee1541bff1d2cebdcb87e7ce4a4003acde08" + integrity sha512-AVi0uazY5quFB9hlp2Xv+ogpfpk77xzsgsIEWyVS7uK/c7MZ5tw7ZPbapa0SbfkqE0fsAMkz5UwtgMLVk2BQAg== dependencies: "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.59.0" - "@typescript-eslint/type-utils" "5.59.0" - "@typescript-eslint/utils" "5.59.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/type-utils" "5.59.1" + "@typescript-eslint/utils" "5.59.1" debug "^4.3.4" grapheme-splitter "^1.0.4" ignore "^5.2.0" @@ -2891,71 +2891,71 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.48.1": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.0.tgz#0ad7cd019346cc5d150363f64869eca10ca9977c" - integrity sha512-qK9TZ70eJtjojSUMrrEwA9ZDQ4N0e/AuoOIgXuNBorXYcBDk397D2r5MIe1B3cok/oCtdNC5j+lUUpVB+Dpb+w== + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.1.tgz#73c2c12127c5c1182d2e5b71a8fa2a85d215cbb4" + integrity sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g== dependencies: - "@typescript-eslint/scope-manager" "5.59.0" - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/typescript-estree" "5.59.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/typescript-estree" "5.59.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.0.tgz#86501d7a17885710b6716a23be2e93fc54a4fe8c" - integrity sha512-tsoldKaMh7izN6BvkK6zRMINj4Z2d6gGhO2UsI8zGZY3XhLq1DndP3Ycjhi1JwdwPRwtLMW4EFPgpuKhbCGOvQ== +"@typescript-eslint/scope-manager@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.1.tgz#8a20222719cebc5198618a5d44113705b51fd7fe" + integrity sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA== dependencies: - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/visitor-keys" "5.59.0" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/visitor-keys" "5.59.1" -"@typescript-eslint/type-utils@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.0.tgz#8e8d1420fc2265989fa3a0d897bde37f3851e8c9" - integrity sha512-d/B6VSWnZwu70kcKQSCqjcXpVH+7ABKH8P1KNn4K7j5PXXuycZTPXF44Nui0TEm6rbWGi8kc78xRgOC4n7xFgA== +"@typescript-eslint/type-utils@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.1.tgz#63981d61684fd24eda2f9f08c0a47ecb000a2111" + integrity sha512-ZMWQ+Oh82jWqWzvM3xU+9y5U7MEMVv6GLioM3R5NJk6uvP47kZ7YvlgSHJ7ERD6bOY7Q4uxWm25c76HKEwIjZw== dependencies: - "@typescript-eslint/typescript-estree" "5.59.0" - "@typescript-eslint/utils" "5.59.0" + "@typescript-eslint/typescript-estree" "5.59.1" + "@typescript-eslint/utils" "5.59.1" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.0.tgz#3fcdac7dbf923ec5251545acdd9f1d42d7c4fe32" - integrity sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA== +"@typescript-eslint/types@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.1.tgz#03f3fedd1c044cb336ebc34cc7855f121991f41d" + integrity sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg== -"@typescript-eslint/typescript-estree@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.0.tgz#8869156ee1dcfc5a95be3ed0e2809969ea28e965" - integrity sha512-sUNnktjmI8DyGzPdZ8dRwW741zopGxltGs/SAPgGL/AAgDpiLsCFLcMNSpbfXfmnNeHmK9h3wGmCkGRGAoUZAg== +"@typescript-eslint/typescript-estree@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.1.tgz#4aa546d27fd0d477c618f0ca00b483f0ec84c43c" + integrity sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA== dependencies: - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/visitor-keys" "5.59.0" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/visitor-keys" "5.59.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.0.tgz#063d066b3bc4850c18872649ed0da9ee72d833d5" - integrity sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA== +"@typescript-eslint/utils@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.1.tgz#d89fc758ad23d2157cfae53f0b429bdf15db9473" + integrity sha512-MkTe7FE+K1/GxZkP5gRj3rCztg45bEhsd8HYjczBuYm+qFHP5vtZmjx3B0yUCDotceQ4sHgTyz60Ycl225njmA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.59.0" - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/typescript-estree" "5.59.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/typescript-estree" "5.59.1" eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.0.tgz#a59913f2bf0baeb61b5cfcb6135d3926c3854365" - integrity sha512-qZ3iXxQhanchCeaExlKPV3gDQFxMUmU35xfd5eCXB6+kUw1TUAbIy2n7QIrwz9s98DQLzNWyHp61fY0da4ZcbA== +"@typescript-eslint/visitor-keys@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.1.tgz#0d96c36efb6560d7fb8eb85de10442c10d8f6058" + integrity sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA== dependencies: - "@typescript-eslint/types" "5.59.0" + "@typescript-eslint/types" "5.59.1" eslint-visitor-keys "^3.3.0" "@unimodules/core@*": @@ -3405,9 +3405,9 @@ axios@^0.21.2: follow-redirects "^1.14.0" axios@^1.0.0: - version "1.3.5" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.5.tgz#e07209b39a0d11848e3e341fa087acd71dadc542" - integrity sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw== + version "1.3.6" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.6.tgz#1ace9a9fb994314b5f6327960918406fa92c6646" + integrity sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -3932,9 +3932,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001449: - version "1.0.30001480" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz#9bbd35ee44c2480a1e3a3b9f4496f5066817164a" - integrity sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ== + version "1.0.30001481" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz#f58a717afe92f9e69d0e35ff64df596bfad93912" + integrity sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ== canonicalize@^1.0.1: version "1.0.8" @@ -4658,7 +4658,7 @@ define-lazy-prop@^2.0.0: resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== -define-properties@^1.1.3, define-properties@^1.1.4: +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== @@ -4852,9 +4852,9 @@ ejs@^3.1.7: jake "^10.8.5" electron-to-chromium@^1.4.284: - version "1.4.368" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.368.tgz#75901f97d3e23da2e66feb1e61fbb8e70ac96430" - integrity sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw== + version "1.4.371" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.371.tgz#393983ef087268a20c926a89be30e9f0bfc803b0" + integrity sha512-jlBzY4tFcJaiUjzhRTCWAqRvTO/fWzjA3Bls0mykzGZ7zvcMP7h05W6UcgzfT9Ca1SW2xyKDOFRyI0pQeRNZGw== elliptic@^6.5.4: version "6.5.4" @@ -4899,9 +4899,9 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: once "^1.4.0" enhanced-resolve@^5.12.0: - version "5.12.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" - integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== + version "5.13.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz#26d1ecc448c02de997133217b5c1053f34a0a275" + integrity sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -5144,7 +5144,7 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.1.1: +eslint-scope@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== @@ -5158,14 +5158,14 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.0: integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== eslint@^8.36.0: - version "8.38.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.38.0.tgz#a62c6f36e548a5574dd35728ac3c6209bd1e2f1a" - integrity sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg== + version "8.39.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.39.0.tgz#7fd20a295ef92d43809e914b70c39fd5a23cf3f1" + integrity sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.4.0" "@eslint/eslintrc" "^2.0.2" - "@eslint/js" "8.38.0" + "@eslint/js" "8.39.0" "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -5175,7 +5175,7 @@ eslint@^8.36.0: debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" + eslint-scope "^7.2.0" eslint-visitor-keys "^3.4.0" espree "^9.5.1" esquery "^1.4.2" @@ -5686,9 +5686,9 @@ flatted@^3.1.0: integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.204.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.204.0.tgz#48515c3d289557d465b409c60ebdf4e783af491e" - integrity sha512-cQhNPLOk5NFyDXBC8WE8dy2Gls+YqKI3FNqQbJ7UrbFyd30IdEX3t27u3VsnoVK22I872+PWeb1KhHxDgu7kAg== + version "0.204.1" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.204.1.tgz#a894bc5e8ad520134c1d13383b8991d03cbf8b01" + integrity sha512-PoeSF0VhSORn3hYzD/NxsQjXX1iLU0UZXzVwZXnRWjeVsedmvDo4epd7PtCQjxveGajmVlyVW35BOOOkqLqJpw== flow-parser@^0.185.0: version "0.185.2" @@ -5826,7 +5826,7 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" -functions-have-names@^1.2.2: +functions-have-names@^1.2.2, functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -7457,9 +7457,9 @@ jest@^29.5.0: jest-cli "^29.5.0" joi@^17.2.1: - version "17.9.1" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.1.tgz#74899b9fa3646904afa984a11df648eca66c9018" - integrity sha512-FariIi9j6QODKATGBrEX7HZcja8Bsh3rfdGYy/Sb65sGlZWK/QWesU1ghk7aJWDj95knjXlQfSmzFSPPkLVsfw== + version "17.9.2" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.2.tgz#8b2e4724188369f55451aebd1d0b1d9482470690" + integrity sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -7956,9 +7956,9 @@ lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== lru-cache@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.0.tgz#19efafa9d08d1c08eb8efd78876075f0b8b1b07b" - integrity sha512-qFXQEwchrZcMVen2uIDceR8Tii6kCJak5rzDStfEM0qA3YLMswaxIEZO0DhIbJ3aqaJiDjt+3crlplOb0tDtKQ== + version "9.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.1.tgz#c58a93de58630b688de39ad04ef02ef26f1902f1" + integrity sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A== lru_map@^0.4.1: version "0.4.1" @@ -9602,9 +9602,9 @@ pacote@13.6.2, pacote@^13.6.1: tar "^6.1.11" pacote@^15.0.0, pacote@^15.0.8: - version "15.1.1" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.1.1.tgz#94d8c6e0605e04d427610b3aacb0357073978348" - integrity sha512-eeqEe77QrA6auZxNHIp+1TzHQ0HBKf5V6c8zcaYZ134EJe1lCi+fjXATkNiEEfbG+e50nu02GLvUtmZcGOYabQ== + version "15.1.2" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.1.2.tgz#78b4c1403231fab368c752943f1969c6d8f026bb" + integrity sha512-EAGJrMiIjBTBB6tWGrx9hFJTOo14B3HSAoa/W9SawFEBhUqjxN7qqaFlGVF9jfY/mIri8Mb2xafmkRgWxYXxIQ== dependencies: "@npmcli/git" "^4.0.0" "@npmcli/installed-package-contents" "^2.0.1" @@ -9621,7 +9621,7 @@ pacote@^15.0.0, pacote@^15.0.8: promise-retry "^2.0.1" read-package-json "^6.0.0" read-package-json-fast "^3.0.0" - sigstore "^1.0.0" + sigstore "^1.3.0" ssri "^10.0.0" tar "^6.1.11" @@ -9813,9 +9813,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.3.1: - version "2.8.7" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" - integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== pretty-format@29.4.3: version "29.4.3" @@ -9998,9 +9998,9 @@ punycode@^2.1.0: integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== pure-rand@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.1.tgz#31207dddd15d43f299fdcdb2f572df65030c19af" - integrity sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg== + version "6.0.2" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" + integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ== pvtsutils@^1.3.2: version "1.3.2" @@ -10072,9 +10072,9 @@ rc@^1.2.8: strip-json-comments "~2.0.1" react-devtools-core@^4.26.1: - version "4.27.5" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.5.tgz#35e41c09e7662ea29948d3caaeeea82f068cbbac" - integrity sha512-QJTriF1V4oyIenViCvM6qQuvcevQsp0sbKkHBZIQOij+AwY9DdOBY+dOeuymUqO5zV61CbmGxWsAIjeWlFS++w== + version "4.27.6" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.6.tgz#e5a613014f7506801ed6c1a97bd0e6316cc9c48a" + integrity sha512-jeFNhEzcSwpiqmw+zix5IFibNEPmUodICN7ClrlRKGktzO/3FMteMb52l1NRUiz/ABSYt9hOZ9IPgVDrg5pyUw== dependencies: shell-quote "^1.6.1" ws "^7" @@ -10132,9 +10132,9 @@ react-native-securerandom@^0.1.1: base64-js "*" react-native@^0.71.4: - version "0.71.6" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.6.tgz#e8f07baf55abd1015eaa7040ceaa4aa632c2c04f" - integrity sha512-gHrDj7qaAaiE41JwaFCh3AtvOqOLuRgZtHKzNiwxakG/wvPAYmG73ECfWHGxjxIx/QT17Hp37Da3ipCei/CayQ== + version "0.71.7" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.7.tgz#d0ae409f6ee4fc7e7a876b4ca9d8d28934133228" + integrity sha512-Id6iRLS581fJMFGbBl1jP5uSmjExtGOvw5Gvh7694zISXjsRAsFMmU+izs0pyCLqDBoHK7y4BT7WGPGw693nYw== dependencies: "@jest/create-cache-key-function" "^29.2.1" "@react-native-community/cli" "10.2.2" @@ -10404,13 +10404,13 @@ regex-not@^1.0.0, regex-not@^1.0.2: safe-regex "^1.1.0" regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" regexpu-core@^5.3.1: version "5.3.2" @@ -10764,10 +10764,10 @@ signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, s resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -sigstore@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.3.0.tgz#cfcb636ff5ef3df1dafb34552ef1ee5e72be1a83" - integrity sha512-dhdv+jOAi1RgLHw13lxumk3rlgZtumUz9QrCNPTx9MazUnUV3BfAb74oYAMPQQ7uaeogB5vTosbz3POzKbEHUQ== +sigstore@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.4.0.tgz#2e3a28c08b1b8246744c27cfb179c525c3f164d8" + integrity sha512-N7TRpSbFjY/TrFDg6yGAQSYBrQ5s6qmPiq4pD6fkv1LoyfMsLG0NwZWG2s5q+uttLHgyVyTa0Rogx2P78rN8kQ== dependencies: "@sigstore/protobuf-specs" "^0.1.0" make-fetch-happen "^11.0.1" @@ -11609,9 +11609,9 @@ type-fest@^0.8.1: integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== type-fest@^3.2.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.8.0.tgz#ce80d1ca7c7d11c5540560999cbd410cb5b3a385" - integrity sha512-FVNSzGQz9Th+/9R6Lvv7WIAkstylfHN2/JYxkyhhmKFYh9At2DST8t6L6Lref9eYO8PXFTfG9Sg1Agg0K3vq3Q== + version "3.9.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.9.0.tgz#36a9e46e6583649f9e6098b267bc577275e9e4f4" + integrity sha512-hR8JP2e8UiH7SME5JZjsobBlEiatFoxpzCP+R3ZeCo7kAaG1jXQE5X/buLzogM6GJu8le9Y4OcfNuIQX0rZskA== type-is@~1.6.18: version "1.6.18" @@ -12001,9 +12001,9 @@ which-boxed-primitive@^1.0.2: is-symbol "^1.0.3" which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== + version "2.0.1" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== which-typed-array@^1.1.9: version "1.1.9" @@ -12196,9 +12196,9 @@ yaml@^1.10.0: integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yaml@^2.1.3: - version "2.2.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.1.tgz#3014bf0482dcd15147aa8e56109ce8632cd60ce4" - integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw== + version "2.2.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.2.tgz#ec551ef37326e6d42872dad1970300f8eb83a073" + integrity sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA== yargs-parser@20.2.4: version "20.2.4" From 7dd406170c75801529daf4bebebde81e84a4cb79 Mon Sep 17 00:00:00 2001 From: Karim Stekelenburg Date: Wed, 10 May 2023 11:39:04 +0200 Subject: [PATCH 136/139] fix: remove scope check from response (#1450) Signed-off-by: Karim Stekelenburg --- packages/openid4vc-client/src/OpenId4VcClientService.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts index b458cd631d..629e1d589f 100644 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ b/packages/openid4vc-client/src/OpenId4VcClientService.ts @@ -257,12 +257,11 @@ export class OpenId4VcClientService { this.logger.debug('Full server metadata', serverMetadata) - if (!accessToken.scope) { - throw new AriesFrameworkError( - "Access token response doesn't contain a scope. Only scoped issuer URIs are supported at this time." - ) + if (accessToken.scope) { + for (const credentialType of accessToken.scope.split(' ')) { + this.assertCredentialHasFormat(credentialFormat, credentialType, serverMetadata) + } } - this.assertCredentialHasFormat(credentialFormat, accessToken.scope, serverMetadata) // proof of possession const callbacks = this.getSignCallback(agentContext) From 700d3f89728ce9d35e22519e505d8203a4c9031e Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 11 May 2023 10:15:10 +0200 Subject: [PATCH 137/139] feat: support for did:jwk and p-256, p-384, p-512 (#1446) Signed-off-by: Timo Glastra --- packages/askar/src/utils/askarKeyTypes.ts | 15 +- packages/askar/src/wallet/AskarWallet.ts | 10 +- .../src/wallet/__tests__/AskarWallet.test.ts | 14 +- packages/cheqd/src/dids/CheqdDidRegistrar.ts | 33 +-- packages/core/package.json | 2 +- packages/core/src/crypto/EcCompression.ts | 101 +++++++++ packages/core/src/crypto/Jwk.ts | 73 +++++++ packages/core/src/crypto/JwkTypes.ts | 135 +++++++++++- packages/core/src/crypto/Key.ts | 34 +-- packages/core/src/crypto/KeyType.ts | 3 + .../core/src/crypto/__tests__/Jwk.test.ts | 97 +++++++++ packages/core/src/crypto/keyUtils.ts | 6 + packages/core/src/crypto/multiCodecKey.ts | 5 +- .../core/src/modules/dids/DidsModuleConfig.ts | 23 ++- .../dids/__tests__/DidsModuleConfig.test.ts | 17 +- .../__tests__/__fixtures__/didKeyP256.json | 32 +++ .../__tests__/__fixtures__/didKeyP384.json | 32 +++ .../__tests__/__fixtures__/didKeyP521.json | 32 +++ .../domain/key-type/__tests__/ed25519.test.ts | 1 - .../domain/key-type/__tests__/jwk.test.ts | 42 ++++ .../modules/dids/domain/key-type/ed25519.ts | 7 +- .../dids/domain/key-type/keyDidJsonWebKey.ts | 20 ++ .../dids/domain/key-type/keyDidMapping.ts | 21 +- .../src/modules/dids/domain/keyDidDocument.ts | 35 +++- .../verificationMethod/JsonWebKey2020.ts | 36 ++++ .../verificationMethod/VerificationMethod.ts | 8 +- .../dids/domain/verificationMethod/index.ts | 8 +- .../core/src/modules/dids/methods/index.ts | 1 + .../src/modules/dids/methods/jwk/DidJwk.ts | 55 +++++ .../dids/methods/jwk/JwkDidRegistrar.ts | 118 +++++++++++ .../dids/methods/jwk/JwkDidResolver.ts | 32 +++ .../dids/methods/jwk/__tests__/DidJwk.test.ts | 23 +++ .../jwk/__tests__/JwkDidRegistrar.test.ts | 193 ++++++++++++++++++ .../jwk/__tests__/JwkDidResolver.test.ts | 34 +++ .../__fixtures__/p256DidJwkEyJjcnYi0i.ts | 33 +++ .../__fixtures__/x25519DidJwkEyJrdHkiOiJ.ts | 21 ++ .../dids/methods/jwk/didJwkDidDocument.ts | 35 ++++ .../src/modules/dids/methods/jwk/index.ts | 3 + .../dids/methods/key/__tests__/DidKey.test.ts | 14 +- packages/core/src/modules/vc/constants.ts | 1 + yarn.lock | 5 + 41 files changed, 1337 insertions(+), 73 deletions(-) create mode 100644 packages/core/src/crypto/EcCompression.ts create mode 100644 packages/core/src/crypto/Jwk.ts create mode 100644 packages/core/src/crypto/__tests__/Jwk.test.ts create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP256.json create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP384.json create mode 100644 packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP521.json create mode 100644 packages/core/src/modules/dids/domain/key-type/__tests__/jwk.test.ts create mode 100644 packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts create mode 100644 packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/DidJwk.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/JwkDidRegistrar.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/__tests__/DidJwk.test.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidRegistrar.test.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidResolver.test.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/p256DidJwkEyJjcnYi0i.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/x25519DidJwkEyJrdHkiOiJ.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts create mode 100644 packages/core/src/modules/dids/methods/jwk/index.ts diff --git a/packages/askar/src/utils/askarKeyTypes.ts b/packages/askar/src/utils/askarKeyTypes.ts index bb837f962e..5d59a26493 100644 --- a/packages/askar/src/utils/askarKeyTypes.ts +++ b/packages/askar/src/utils/askarKeyTypes.ts @@ -1,6 +1,13 @@ -import type { KeyType } from '@aries-framework/core' - +import { KeyType } from '@aries-framework/core' import { KeyAlgs } from '@hyperledger/aries-askar-shared' -export const keyTypeSupportedByAskar = (keyType: KeyType) => - Object.entries(KeyAlgs).find(([, value]) => value === keyType.toString()) !== undefined +const keyTypeToAskarAlg = { + [KeyType.Ed25519]: KeyAlgs.Ed25519, + [KeyType.X25519]: KeyAlgs.X25519, + [KeyType.Bls12381g1]: KeyAlgs.Bls12381G1, + [KeyType.Bls12381g2]: KeyAlgs.Bls12381G2, + [KeyType.Bls12381g1g2]: KeyAlgs.Bls12381G1G2, + [KeyType.P256]: KeyAlgs.EcSecp256r1, +} as const + +export const keyTypeSupportedByAskar = (keyType: KeyType) => keyType in keyTypeToAskarAlg diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 62aa371b5a..8d87b0c840 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -34,15 +34,7 @@ import { WalletNotFoundError, KeyDerivationMethod, } from '@aries-framework/core' -import { - KdfMethod, - StoreKeyMethod, - KeyAlgs, - CryptoBox, - Store, - Key as AskarKey, - keyAlgFromString, -} from '@hyperledger/aries-askar-shared' +import { KeyAlgs, CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-shared' // eslint-disable-next-line import/order import BigNumber from 'bn.js' diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index efeccd9fcd..00eb26f77e 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -100,6 +100,14 @@ describeRunInNodeVersion([18], 'AskarWallet basic operations', () => { }) }) + test('Create P-256 keypair', async () => { + await expect( + askarWallet.createKey({ seed: Buffer.concat([seed, seed]), keyType: KeyType.P256 }) + ).resolves.toMatchObject({ + keyType: KeyType.P256, + }) + }) + test('throws WalletKeyExistsError when a key already exists', async () => { const privateKey = TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b68') await expect(askarWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).resolves.toEqual(expect.any(Key)) @@ -108,10 +116,8 @@ describeRunInNodeVersion([18], 'AskarWallet basic operations', () => { ) }) - describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { - test('Fail to create a Bls12381g1g2 keypair', async () => { - await expect(askarWallet.createKey({ seed, keyType: KeyType.Bls12381g1g2 })).rejects.toThrowError(WalletError) - }) + test('Fail to create a P384 keypair', async () => { + await expect(askarWallet.createKey({ seed, keyType: KeyType.P384 })).rejects.toThrowError(WalletError) }) test('Create a signature with a ed25519 keypair', async () => { diff --git a/packages/cheqd/src/dids/CheqdDidRegistrar.ts b/packages/cheqd/src/dids/CheqdDidRegistrar.ts index a23ecf1456..0f4c243098 100644 --- a/packages/cheqd/src/dids/CheqdDidRegistrar.ts +++ b/packages/cheqd/src/dids/CheqdDidRegistrar.ts @@ -5,7 +5,6 @@ import type { DidCreateResult, DidDeactivateResult, DidUpdateResult, - VerificationMethod, } from '@aries-framework/core' import type { CheqdNetwork, DIDDocument, DidStdFee, TVerificationKey, VerificationMethods } from '@cheqd/sdk' import type { SignInfo } from '@cheqd/ts-proto/cheqd/did/v2' @@ -22,6 +21,7 @@ import { TypedArrayEncoder, getKeyFromVerificationMethod, JsonTransformer, + VerificationMethod, } from '@aries-framework/core' import { MethodSpecificIdAlgo, createDidVerificationMethod } from '@cheqd/sdk' import { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2' @@ -182,16 +182,19 @@ export class CheqdDidRegistrar implements DidRegistrar { }) didDocument.verificationMethod?.concat( - createDidVerificationMethod( - [verificationMethod.type as VerificationMethods], - [ - { - methodSpecificId: didDocument.id.split(':')[3], - didUrl: didDocument.id, - keyId: `${didDocument.id}#${verificationMethod.id}`, - publicKey: TypedArrayEncoder.toHex(key.publicKey), - }, - ] + JsonTransformer.fromJSON( + createDidVerificationMethod( + [verificationMethod.type as VerificationMethods], + [ + { + methodSpecificId: didDocument.id.split(':')[3], + didUrl: didDocument.id, + keyId: `${didDocument.id}#${verificationMethod.id}`, + publicKey: TypedArrayEncoder.toHex(key.publicKey), + }, + ] + ), + VerificationMethod ) ) } @@ -253,6 +256,7 @@ export class CheqdDidRegistrar implements DidRegistrar { try { const { didDocument, didDocumentMetadata } = await cheqdLedgerService.resolve(did) + const didRecord = await didRepository.findCreatedDid(agentContext, did) if (!didDocument || didDocumentMetadata.deactivated || !didRecord) { return { @@ -265,7 +269,8 @@ export class CheqdDidRegistrar implements DidRegistrar { } } const payloadToSign = createMsgDeactivateDidDocPayloadToSign(didDocument, versionId) - const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) + const didDocumentInstance = JsonTransformer.fromJSON(didDocument, DidDocument) + const signInputs = await this.signPayload(agentContext, payloadToSign, didDocumentInstance.verificationMethod) const response = await cheqdLedgerService.deactivate(didDocument, signInputs, versionId) if (response.code !== 0) { throw new Error(`${response.rawLog}`) @@ -332,7 +337,9 @@ export class CheqdDidRegistrar implements DidRegistrar { data, }) const payloadToSign = MsgCreateResourcePayload.encode(resourcePayload).finish() - const signInputs = await this.signPayload(agentContext, payloadToSign, didDocument.verificationMethod) + + const didDocumentInstance = JsonTransformer.fromJSON(didDocument, DidDocument) + const signInputs = await this.signPayload(agentContext, payloadToSign, didDocumentInstance.verificationMethod) const response = await cheqdLedgerService.createResource(did, resourcePayload, signInputs) if (response.code !== 0) { throw new Error(`${response.rawLog}`) diff --git a/packages/core/package.json b/packages/core/package.json index be6bdb7498..9eb5d56a3f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -33,6 +33,7 @@ "node-fetch": "^2.6.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", @@ -51,7 +52,6 @@ "web-did-resolver": "^2.0.21" }, "devDependencies": { - "@types/bn.js": "^5.1.0", "@types/events": "^3.0.0", "@types/luxon": "^3.2.0", "@types/object-inspect": "^1.8.0", diff --git a/packages/core/src/crypto/EcCompression.ts b/packages/core/src/crypto/EcCompression.ts new file mode 100644 index 0000000000..42cbd30398 --- /dev/null +++ b/packages/core/src/crypto/EcCompression.ts @@ -0,0 +1,101 @@ +/** + * Based on https://github.com/transmute-industries/verifiable-data/blob/main/packages/web-crypto-key-pair/src/compression/ec-compression.ts + */ + +// native BigInteger is only supported in React Native 0.70+, so we use big-integer for now. +import bigInt from 'big-integer' + +import { Buffer } from '../utils/buffer' + +const curveToPointLength = { + 'P-256': 64, + 'P-384': 96, + 'P-521': 132, +} + +function getConstantsForCurve(curve: 'P-256' | 'P-384' | 'P-521') { + let two, prime, b, pIdent + + if (curve === 'P-256') { + two = bigInt(2) + prime = two.pow(256).subtract(two.pow(224)).add(two.pow(192)).add(two.pow(96)).subtract(1) + + pIdent = prime.add(1).divide(4) + + b = bigInt('5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b', 16) + } + + if (curve === 'P-384') { + two = bigInt(2) + prime = two.pow(384).subtract(two.pow(128)).subtract(two.pow(96)).add(two.pow(32)).subtract(1) + + pIdent = prime.add(1).divide(4) + b = bigInt('b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef', 16) + } + + if (curve === 'P-521') { + two = bigInt(2) + prime = two.pow(521).subtract(1) + b = bigInt( + '00000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00', + 16 + ) + pIdent = prime.add(1).divide(4) + } + + if (!prime || !b || !pIdent) { + throw new Error(`Unsupported curve ${curve}`) + } + + return { prime, b, pIdent } +} + +// see https://stackoverflow.com/questions/17171542/algorithm-for-elliptic-curve-point-compression +// https://github.com/w3c-ccg/did-method-key/pull/36 +/** + * Point compress elliptic curve key + * @return Compressed representation + */ +function compressECPoint(x: Uint8Array, y: Uint8Array): Uint8Array { + const out = new Uint8Array(x.length + 1) + out[0] = 2 + (y[y.length - 1] & 1) + out.set(x, 1) + return out +} + +function padWithZeroes(number: number | string, length: number) { + let value = '' + number + while (value.length < length) { + value = '0' + value + } + return value +} + +export function compress(publicKey: Uint8Array): Uint8Array { + const publicKeyHex = Buffer.from(publicKey).toString('hex') + const xHex = publicKeyHex.slice(0, publicKeyHex.length / 2) + const yHex = publicKeyHex.slice(publicKeyHex.length / 2, publicKeyHex.length) + const xOctet = Uint8Array.from(Buffer.from(xHex, 'hex')) + const yOctet = Uint8Array.from(Buffer.from(yHex, 'hex')) + return compressECPoint(xOctet, yOctet) +} + +export function expand(publicKey: Uint8Array, curve: 'P-256' | 'P-384' | 'P-521'): Uint8Array { + const publicKeyComponent = Buffer.from(publicKey).toString('hex') + const { prime, b, pIdent } = getConstantsForCurve(curve) + const signY = new Number(publicKeyComponent[1]).valueOf() - 2 + const x = bigInt(publicKeyComponent.substring(2), 16) + // y^2 = x^3 - 3x + b + let y = x.pow(3).subtract(x.multiply(3)).add(b).modPow(pIdent, prime) + + // If the parity doesn't match it's the *other* root + if (y.mod(2).toJSNumber() !== signY) { + // y = prime - y + y = prime.subtract(y) + } + + return Buffer.from( + padWithZeroes(x.toString(16), curveToPointLength[curve]) + padWithZeroes(y.toString(16), curveToPointLength[curve]), + 'hex' + ) +} diff --git a/packages/core/src/crypto/Jwk.ts b/packages/core/src/crypto/Jwk.ts new file mode 100644 index 0000000000..2c8d49d243 --- /dev/null +++ b/packages/core/src/crypto/Jwk.ts @@ -0,0 +1,73 @@ +import type { + Ed25519JwkPublicKey, + Jwk, + P256JwkPublicKey, + P384JwkPublicKey, + P521JwkPublicKey, + X25519JwkPublicKey, +} from './JwkTypes' +import type { Key } from './Key' + +import { TypedArrayEncoder, Buffer } from '../utils' + +import { compress, expand } from './EcCompression' +import { + jwkCurveToKeyTypeMapping, + keyTypeToJwkCurveMapping, + isEd25519JwkPublicKey, + isX25519JwkPublicKey, + isP256JwkPublicKey, + isP384JwkPublicKey, + isP521JwkPublicKey, +} from './JwkTypes' +import { KeyType } from './KeyType' + +export function getKeyDataFromJwk(jwk: Jwk): { keyType: KeyType; publicKey: Uint8Array } { + // ed25519, x25519 + if (isEd25519JwkPublicKey(jwk) || isX25519JwkPublicKey(jwk)) { + return { + publicKey: TypedArrayEncoder.fromBase64(jwk.x), + keyType: jwkCurveToKeyTypeMapping[jwk.crv], + } + } + + // p-256, p-384, p-521 + if (isP256JwkPublicKey(jwk) || isP384JwkPublicKey(jwk) || isP521JwkPublicKey(jwk)) { + // TODO: do we want to use the compressed key in the Key instance? + const publicKeyBuffer = Buffer.concat([TypedArrayEncoder.fromBase64(jwk.x), TypedArrayEncoder.fromBase64(jwk.y)]) + const compressedPublicKey = compress(publicKeyBuffer) + + return { + publicKey: compressedPublicKey, + keyType: jwkCurveToKeyTypeMapping[jwk.crv], + } + } + + throw new Error(`Unsupported JWK kty '${jwk.kty}' and crv '${jwk.crv}'`) +} + +export function getJwkFromKey(key: Key): Jwk { + if (key.keyType === KeyType.Ed25519 || key.keyType === KeyType.X25519) { + return { + kty: 'OKP', + crv: keyTypeToJwkCurveMapping[key.keyType], + x: TypedArrayEncoder.toBase64URL(key.publicKey), + } satisfies Ed25519JwkPublicKey | X25519JwkPublicKey + } + + if (key.keyType === KeyType.P256 || key.keyType === KeyType.P384 || key.keyType === KeyType.P521) { + const crv = keyTypeToJwkCurveMapping[key.keyType] + const expanded = expand(key.publicKey, crv) + const x = expanded.slice(0, expanded.length / 2) + const y = expanded.slice(expanded.length / 2) + + return { + kty: 'EC', + crv, + x: TypedArrayEncoder.toBase64URL(x), + y: TypedArrayEncoder.toBase64URL(y), + } satisfies P256JwkPublicKey | P384JwkPublicKey | P521JwkPublicKey + } + + throw new Error(`Cannot encode Key as JWK. Unsupported key type '${key.keyType}'`) +} diff --git a/packages/core/src/crypto/JwkTypes.ts b/packages/core/src/crypto/JwkTypes.ts index 144e771f16..560b2c5e1d 100644 --- a/packages/core/src/crypto/JwkTypes.ts +++ b/packages/core/src/crypto/JwkTypes.ts @@ -1,6 +1,139 @@ +import { KeyType } from './KeyType' + +export type JwkCurve = 'Ed25519' | 'X25519' | 'P-256' | 'P-384' | 'P-521' | 'Bls12381G1' | 'Bls12381G2' + export interface Jwk { kty: 'EC' | 'OKP' - crv: 'Ed25519' | 'X25519' | 'P-256' | 'P-384' | 'secp256k1' + crv: JwkCurve x: string y?: string + use?: 'sig' | 'enc' +} + +export interface Ed25519JwkPublicKey extends Jwk { + kty: 'OKP' + crv: 'Ed25519' + x: string + y?: never + use?: 'sig' +} + +export interface X25519JwkPublicKey extends Jwk { + kty: 'OKP' + crv: 'X25519' + x: string + y?: never + use?: 'enc' +} + +export interface P256JwkPublicKey extends Jwk { + kty: 'EC' + crv: 'P-256' + x: string + y: string + use?: 'sig' | 'enc' +} + +export interface P384JwkPublicKey extends Jwk { + kty: 'EC' + crv: 'P-384' + x: string + y: string + use?: 'sig' | 'enc' +} + +export interface P521JwkPublicKey extends Jwk { + kty: 'EC' + crv: 'P-521' + x: string + y: string + use?: 'sig' | 'enc' +} + +export function isEd25519JwkPublicKey(jwk: Jwk): jwk is Ed25519JwkPublicKey { + return jwk.kty === 'OKP' && jwk.crv === 'Ed25519' && jwk.x !== undefined && (!jwk.use || jwk.use === 'sig') +} + +export function isX25519JwkPublicKey(jwk: Jwk): jwk is X25519JwkPublicKey { + return jwk.kty === 'OKP' && jwk.crv === 'X25519' && jwk.x !== undefined && (!jwk.use || jwk.use === 'enc') +} + +export function isP256JwkPublicKey(jwk: Jwk): jwk is P256JwkPublicKey { + return ( + jwk.kty === 'EC' && + jwk.crv === 'P-256' && + jwk.x !== undefined && + jwk.y !== undefined && + (!jwk.use || ['sig', 'enc'].includes(jwk.use)) + ) +} + +export function isP384JwkPublicKey(jwk: Jwk): jwk is P384JwkPublicKey { + return ( + jwk.kty === 'EC' && + jwk.crv === 'P-384' && + jwk.x !== undefined && + jwk.y !== undefined && + (!jwk.use || ['sig', 'enc'].includes(jwk.use)) + ) +} + +export function isP521JwkPublicKey(jwk: Jwk): jwk is P521JwkPublicKey { + return ( + jwk.kty === 'EC' && + jwk.crv === 'P-521' && + jwk.x !== undefined && + jwk.y !== undefined && + (!jwk.use || ['sig', 'enc'].includes(jwk.use)) + ) +} + +export const jwkCurveToKeyTypeMapping = { + Ed25519: KeyType.Ed25519, + X25519: KeyType.X25519, + 'P-256': KeyType.P256, + 'P-384': KeyType.P384, + 'P-521': KeyType.P521, + Bls12381G1: KeyType.Bls12381g1, + Bls12381G2: KeyType.Bls12381g2, +} as const + +export const keyTypeToJwkCurveMapping = { + [KeyType.Ed25519]: 'Ed25519', + [KeyType.X25519]: 'X25519', + [KeyType.P256]: 'P-256', + [KeyType.P384]: 'P-384', + [KeyType.P521]: 'P-521', + [KeyType.Bls12381g1]: 'Bls12381G1', + [KeyType.Bls12381g2]: 'Bls12381G2', +} as const + +const keyTypeSigningSupportedMapping = { + [KeyType.Ed25519]: true, + [KeyType.X25519]: false, + [KeyType.P256]: true, + [KeyType.P384]: true, + [KeyType.P521]: true, + [KeyType.Bls12381g1]: true, + [KeyType.Bls12381g2]: true, + [KeyType.Bls12381g1g2]: true, +} as const + +const keyTypeEncryptionSupportedMapping = { + [KeyType.Ed25519]: false, + [KeyType.X25519]: true, + [KeyType.P256]: true, + [KeyType.P384]: true, + [KeyType.P521]: true, + [KeyType.Bls12381g1]: false, + [KeyType.Bls12381g2]: false, + [KeyType.Bls12381g1g2]: false, +} as const + +export function isSigningSupportedForKeyType(keyType: KeyType): boolean { + return keyTypeSigningSupportedMapping[keyType] +} + +export function isEncryptionSupportedForKeyType(keyType: KeyType): boolean { + return keyTypeEncryptionSupportedMapping[keyType] } diff --git a/packages/core/src/crypto/Key.ts b/packages/core/src/crypto/Key.ts index 47576e3ffa..85a56dafbd 100644 --- a/packages/core/src/crypto/Key.ts +++ b/packages/core/src/crypto/Key.ts @@ -1,10 +1,11 @@ import type { Jwk } from './JwkTypes' +import type { KeyType } from './KeyType' -import { AriesFrameworkError } from '../error' import { Buffer, MultiBaseEncoder, TypedArrayEncoder, VarintEncoder } from '../utils' -import { KeyType } from './KeyType' -import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeytype } from './multiCodecKey' +import { getJwkFromKey, getKeyDataFromJwk } from './Jwk' +import { isEncryptionSupportedForKeyType, isSigningSupportedForKeyType } from './JwkTypes' +import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeyType } from './multiCodecKey' export class Key { public readonly publicKey: Buffer @@ -36,7 +37,7 @@ export class Key { } public get prefixedPublicKey() { - const multiCodecPrefix = getMultiCodecPrefixByKeytype(this.keyType) + const multiCodecPrefix = getMultiCodecPrefixByKeyType(this.keyType) // Create Buffer with length of the prefix bytes, then use varint to fill the prefix bytes const prefixBytes = VarintEncoder.encode(multiCodecPrefix) @@ -53,22 +54,21 @@ export class Key { return TypedArrayEncoder.toBase58(this.publicKey) } + public get supportsEncrypting() { + return isEncryptionSupportedForKeyType(this.keyType) + } + + public get supportsSigning() { + return isSigningSupportedForKeyType(this.keyType) + } + public toJwk(): Jwk { - if (this.keyType !== KeyType.Ed25519) { - throw new AriesFrameworkError(`JWK creation is only supported for Ed25519 key types. Received ${this.keyType}`) - } - - return { - kty: 'OKP', - crv: 'Ed25519', - x: TypedArrayEncoder.toBase64URL(this.publicKey), - } + return getJwkFromKey(this) } public static fromJwk(jwk: Jwk) { - if (jwk.crv !== 'Ed25519') { - throw new AriesFrameworkError('Only JWKs with Ed25519 key type is supported.') - } - return Key.fromPublicKeyBase58(TypedArrayEncoder.toBase58(TypedArrayEncoder.fromBase64(jwk.x)), KeyType.Ed25519) + const { keyType, publicKey } = getKeyDataFromJwk(jwk) + + return Key.fromPublicKey(publicKey, keyType) } } diff --git a/packages/core/src/crypto/KeyType.ts b/packages/core/src/crypto/KeyType.ts index 858762f670..d378e4bffb 100644 --- a/packages/core/src/crypto/KeyType.ts +++ b/packages/core/src/crypto/KeyType.ts @@ -4,4 +4,7 @@ export enum KeyType { Bls12381g1 = 'bls12381g1', Bls12381g2 = 'bls12381g2', X25519 = 'x25519', + P256 = 'p256', + P384 = 'p384', + P521 = 'p521', } diff --git a/packages/core/src/crypto/__tests__/Jwk.test.ts b/packages/core/src/crypto/__tests__/Jwk.test.ts new file mode 100644 index 0000000000..0cbe81255e --- /dev/null +++ b/packages/core/src/crypto/__tests__/Jwk.test.ts @@ -0,0 +1,97 @@ +import type { + Ed25519JwkPublicKey, + P256JwkPublicKey, + P384JwkPublicKey, + P521JwkPublicKey, + X25519JwkPublicKey, +} from '../JwkTypes' + +import { getJwkFromKey, getKeyDataFromJwk } from '../Jwk' +import { Key } from '../Key' +import { KeyType } from '../KeyType' + +describe('jwk', () => { + it('Ed25519', () => { + const fingerprint = 'z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp' + const jwk = { + kty: 'OKP', + crv: 'Ed25519', + x: 'O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik', + } satisfies Ed25519JwkPublicKey + + const { keyType, publicKey } = getKeyDataFromJwk(jwk) + expect(keyType).toEqual(KeyType.Ed25519) + expect(Key.fromPublicKey(publicKey, KeyType.Ed25519).fingerprint).toEqual(fingerprint) + + const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) + expect(actualJwk).toEqual(jwk) + }) + + it('X25519', () => { + const fingerprint = 'z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW' + const jwk = { + kty: 'OKP', + crv: 'X25519', + x: 'W_Vcc7guviK-gPNDBmevVw-uJVamQV5rMNQGUwCqlH0', + } satisfies X25519JwkPublicKey + + const { keyType, publicKey } = getKeyDataFromJwk(jwk) + expect(keyType).toEqual(KeyType.X25519) + expect(Key.fromPublicKey(publicKey, KeyType.X25519).fingerprint).toEqual(fingerprint) + + const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) + expect(actualJwk).toEqual(jwk) + }) + + it('P-256', () => { + const fingerprint = 'zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv' + const jwk = { + kty: 'EC', + crv: 'P-256', + x: 'igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns', + y: 'efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM', + } satisfies P256JwkPublicKey + + const { keyType, publicKey } = getKeyDataFromJwk(jwk) + expect(keyType).toEqual(KeyType.P256) + expect(Key.fromPublicKey(publicKey, KeyType.P256).fingerprint).toEqual(fingerprint) + + const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) + expect(actualJwk).toEqual(jwk) + }) + + it('P-384', () => { + const fingerprint = 'z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9' + const jwk = { + kty: 'EC', + crv: 'P-384', + x: 'lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc', + y: 'y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv', + } satisfies P384JwkPublicKey + + const { keyType, publicKey } = getKeyDataFromJwk(jwk) + expect(keyType).toEqual(KeyType.P384) + expect(Key.fromPublicKey(publicKey, KeyType.P384).fingerprint).toEqual(fingerprint) + + const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) + expect(actualJwk).toEqual(jwk) + }) + + it('P-521', () => { + const fingerprint = + 'z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7' + const jwk = { + kty: 'EC', + crv: 'P-521', + x: 'ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS', + y: 'AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC', + } satisfies P521JwkPublicKey + + const { keyType, publicKey } = getKeyDataFromJwk(jwk) + expect(keyType).toEqual(KeyType.P521) + expect(Key.fromPublicKey(publicKey, KeyType.P521).fingerprint).toEqual(fingerprint) + + const actualJwk = getJwkFromKey(Key.fromFingerprint(fingerprint)) + expect(actualJwk).toEqual(jwk) + }) +}) diff --git a/packages/core/src/crypto/keyUtils.ts b/packages/core/src/crypto/keyUtils.ts index 9022d2095d..d772c63e92 100644 --- a/packages/core/src/crypto/keyUtils.ts +++ b/packages/core/src/crypto/keyUtils.ts @@ -9,6 +9,9 @@ export function isValidSeed(seed: Buffer, keyType: KeyType): boolean { [KeyType.Bls12381g1]: 32, [KeyType.Bls12381g2]: 32, [KeyType.Bls12381g1g2]: 32, + [KeyType.P256]: 64, + [KeyType.P384]: 64, + [KeyType.P521]: 64, } return Buffer.isBuffer(seed) && seed.length >= minimumSeedLength[keyType] @@ -21,6 +24,9 @@ export function isValidPrivateKey(privateKey: Buffer, keyType: KeyType): boolean [KeyType.Bls12381g1]: 32, [KeyType.Bls12381g2]: 32, [KeyType.Bls12381g1g2]: 32, + [KeyType.P256]: 32, + [KeyType.P384]: 48, + [KeyType.P521]: 66, } return Buffer.isBuffer(privateKey) && privateKey.length === privateKeyLength[keyType] diff --git a/packages/core/src/crypto/multiCodecKey.ts b/packages/core/src/crypto/multiCodecKey.ts index 20d3f4b070..1ebcbd5ca9 100644 --- a/packages/core/src/crypto/multiCodecKey.ts +++ b/packages/core/src/crypto/multiCodecKey.ts @@ -7,6 +7,9 @@ const multiCodecPrefixMap: Record = { 236: KeyType.X25519, 237: KeyType.Ed25519, 238: KeyType.Bls12381g1g2, + 4608: KeyType.P256, + 4609: KeyType.P384, + 4610: KeyType.P521, } export function getKeyTypeByMultiCodecPrefix(multiCodecPrefix: number): KeyType { @@ -19,7 +22,7 @@ export function getKeyTypeByMultiCodecPrefix(multiCodecPrefix: number): KeyType return keyType } -export function getMultiCodecPrefixByKeytype(keyType: KeyType): number { +export function getMultiCodecPrefixByKeyType(keyType: KeyType): number { const codes = Object.keys(multiCodecPrefixMap) const code = codes.find((key) => multiCodecPrefixMap[key] === keyType) diff --git a/packages/core/src/modules/dids/DidsModuleConfig.ts b/packages/core/src/modules/dids/DidsModuleConfig.ts index 057772d8d8..8e065657b4 100644 --- a/packages/core/src/modules/dids/DidsModuleConfig.ts +++ b/packages/core/src/modules/dids/DidsModuleConfig.ts @@ -1,6 +1,14 @@ import type { DidRegistrar, DidResolver } from './domain' -import { KeyDidRegistrar, PeerDidRegistrar, KeyDidResolver, PeerDidResolver, WebDidResolver } from './methods' +import { + KeyDidRegistrar, + PeerDidRegistrar, + KeyDidResolver, + PeerDidResolver, + WebDidResolver, + JwkDidRegistrar, + JwkDidResolver, +} from './methods' /** * DidsModuleConfigOptions defines the interface for the options of the DidsModuleConfig class. @@ -15,7 +23,7 @@ export interface DidsModuleConfigOptions { * registered, as it is needed for the connections and out of band module to function. Other did methods can be * disabled. * - * @default [KeyDidRegistrar, PeerDidRegistrar] + * @default [KeyDidRegistrar, PeerDidRegistrar, JwkDidRegistrar] */ registrars?: DidRegistrar[] @@ -27,7 +35,7 @@ export interface DidsModuleConfigOptions { * registered, as it is needed for the connections and out of band module to function. Other did methods can be * disabled. * - * @default [WebDidResolver, KeyDidResolver, PeerDidResolver] + * @default [WebDidResolver, KeyDidResolver, PeerDidResolver, JwkDidResolver] */ resolvers?: DidResolver[] } @@ -46,7 +54,7 @@ export class DidsModuleConfig { // This prevents creating new instances every time this property is accessed if (this._registrars) return this._registrars - let registrars = this.options.registrars ?? [new KeyDidRegistrar(), new PeerDidRegistrar()] + let registrars = this.options.registrars ?? [new KeyDidRegistrar(), new PeerDidRegistrar(), new JwkDidRegistrar()] // Add peer did registrar if it is not included yet if (!registrars.find((registrar) => registrar instanceof PeerDidRegistrar)) { @@ -67,7 +75,12 @@ export class DidsModuleConfig { // This prevents creating new instances every time this property is accessed if (this._resolvers) return this._resolvers - let resolvers = this.options.resolvers ?? [new WebDidResolver(), new KeyDidResolver(), new PeerDidResolver()] + let resolvers = this.options.resolvers ?? [ + new WebDidResolver(), + new KeyDidResolver(), + new PeerDidResolver(), + new JwkDidResolver(), + ] // Add peer did resolver if it is not included yet if (!resolvers.find((resolver) => resolver instanceof PeerDidResolver)) { diff --git a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts index 797a7f8615..ef3dc3dc66 100644 --- a/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts +++ b/packages/core/src/modules/dids/__tests__/DidsModuleConfig.test.ts @@ -1,17 +1,30 @@ import type { DidRegistrar, DidResolver } from '../domain' -import { KeyDidRegistrar, PeerDidRegistrar, KeyDidResolver, PeerDidResolver, WebDidResolver } from '..' import { DidsModuleConfig } from '../DidsModuleConfig' +import { + KeyDidRegistrar, + PeerDidRegistrar, + KeyDidResolver, + PeerDidResolver, + WebDidResolver, + JwkDidRegistrar, + JwkDidResolver, +} from '../methods' describe('DidsModuleConfig', () => { test('sets default values', () => { const config = new DidsModuleConfig() - expect(config.registrars).toEqual([expect.any(KeyDidRegistrar), expect.any(PeerDidRegistrar)]) + expect(config.registrars).toEqual([ + expect.any(KeyDidRegistrar), + expect.any(PeerDidRegistrar), + expect.any(JwkDidRegistrar), + ]) expect(config.resolvers).toEqual([ expect.any(WebDidResolver), expect.any(KeyDidResolver), expect.any(PeerDidResolver), + expect.any(JwkDidResolver), ]) }) diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP256.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP256.json new file mode 100644 index 0000000000..5465e191de --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP256.json @@ -0,0 +1,32 @@ +{ + "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/suites/jws-2020/v1"], + "id": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv", + "verificationMethod": [ + { + "id": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv", + "type": "JsonWebKey2020", + "controller": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv", + "publicKeyJwk": { + "kty": "EC", + "crv": "P-256", + "x": "igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns", + "y": "efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM" + } + } + ], + "assertionMethod": [ + "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv" + ], + "authentication": [ + "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv" + ], + "capabilityInvocation": [ + "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv" + ], + "capabilityDelegation": [ + "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv" + ], + "keyAgreement": [ + "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv" + ] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP384.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP384.json new file mode 100644 index 0000000000..b5249b1afc --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP384.json @@ -0,0 +1,32 @@ +{ + "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/suites/jws-2020/v1"], + "id": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9", + "verificationMethod": [ + { + "id": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9", + "type": "JsonWebKey2020", + "controller": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9", + "publicKeyJwk": { + "kty": "EC", + "crv": "P-384", + "x": "lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc", + "y": "y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv" + } + } + ], + "assertionMethod": [ + "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9" + ], + "authentication": [ + "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9" + ], + "capabilityInvocation": [ + "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9" + ], + "capabilityDelegation": [ + "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9" + ], + "keyAgreement": [ + "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9" + ] +} diff --git a/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP521.json b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP521.json new file mode 100644 index 0000000000..bafea05578 --- /dev/null +++ b/packages/core/src/modules/dids/__tests__/__fixtures__/didKeyP521.json @@ -0,0 +1,32 @@ +{ + "@context": ["https://w3id.org/did/v1", "https://w3id.org/security/suites/jws-2020/v1"], + "id": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7", + "verificationMethod": [ + { + "id": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7", + "type": "JsonWebKey2020", + "controller": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7", + "publicKeyJwk": { + "kty": "EC", + "crv": "P-521", + "x": "ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS", + "y": "AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC" + } + } + ], + "assertionMethod": [ + "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7" + ], + "authentication": [ + "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7" + ], + "capabilityInvocation": [ + "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7" + ], + "capabilityDelegation": [ + "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7" + ], + "keyAgreement": [ + "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7" + ] +} diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts index 0b654c1a53..c66b4fc7aa 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts @@ -55,7 +55,6 @@ describe('ed25519', () => { expect(keyDidEd25519.supportedVerificationMethodTypes).toMatchObject([ 'Ed25519VerificationKey2018', 'Ed25519VerificationKey2020', - 'JsonWebKey2020', ]) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/jwk.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/jwk.test.ts new file mode 100644 index 0000000000..aa186aef56 --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/jwk.test.ts @@ -0,0 +1,42 @@ +import { Key } from '../../../../../crypto/Key' +import { JsonTransformer } from '../../../../../utils' +import didKeyP256Fixture from '../../../__tests__/__fixtures__/didKeyP256.json' +import { VerificationMethod } from '../../verificationMethod' +import { VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 } from '../../verificationMethod/JsonWebKey2020' +import { keyDidJsonWebKey } from '../keyDidJsonWebKey' + +const TEST_P256_FINGERPRINT = 'zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv' +const TEST_P256_DID = `did:key:${TEST_P256_FINGERPRINT}` + +describe('keyDidJsonWebKey', () => { + it('should return a valid verification method', async () => { + const key = Key.fromFingerprint(TEST_P256_FINGERPRINT) + const verificationMethods = keyDidJsonWebKey.getVerificationMethods(TEST_P256_DID, key) + + expect(JsonTransformer.toJSON(verificationMethods)).toMatchObject([didKeyP256Fixture.verificationMethod[0]]) + }) + + it('supports no verification method type', () => { + expect(keyDidJsonWebKey.supportedVerificationMethodTypes).toMatchObject([ + VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, + ]) + }) + + it('returns key for JsonWebKey2020 verification method', () => { + const verificationMethod = JsonTransformer.fromJSON(didKeyP256Fixture.verificationMethod[0], VerificationMethod) + + const key = keyDidJsonWebKey.getKeyFromVerificationMethod(verificationMethod) + + expect(key.fingerprint).toBe(TEST_P256_FINGERPRINT) + }) + + it('throws an error if an invalid verification method is passed', () => { + const verificationMethod = JsonTransformer.fromJSON(didKeyP256Fixture.verificationMethod[0], VerificationMethod) + + verificationMethod.type = 'SomeRandomType' + + expect(() => keyDidJsonWebKey.getKeyFromVerificationMethod(verificationMethod)).toThrowError( + 'Invalid verification method passed' + ) + }) +}) diff --git a/packages/core/src/modules/dids/domain/key-type/ed25519.ts b/packages/core/src/modules/dids/domain/key-type/ed25519.ts index cee29095e6..4d96a43e6c 100644 --- a/packages/core/src/modules/dids/domain/key-type/ed25519.ts +++ b/packages/core/src/modules/dids/domain/key-type/ed25519.ts @@ -1,5 +1,4 @@ import type { KeyDidMapping } from './keyDidMapping' -import type { Jwk } from '../../../../crypto' import type { VerificationMethod } from '../verificationMethod' import { convertPublicKeyToX25519 } from '@stablelib/ed25519' @@ -8,7 +7,6 @@ import { Key, KeyType } from '../../../../crypto' export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018 = 'Ed25519VerificationKey2018' export const VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 = 'Ed25519VerificationKey2020' -export const VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 = 'JsonWebKey2020' export function getEd25519VerificationMethod({ key, id, controller }: { id: string; key: Key; controller: string }) { return { @@ -23,7 +21,6 @@ export const keyDidEd25519: KeyDidMapping = { supportedVerificationMethodTypes: [ VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2018, VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020, - VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, ], getVerificationMethods: (did, key) => [ getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }), @@ -35,15 +32,13 @@ export const keyDidEd25519: KeyDidMapping = { ) { return Key.fromPublicKeyBase58(verificationMethod.publicKeyBase58, KeyType.Ed25519) } + if ( verificationMethod.type === VERIFICATION_METHOD_TYPE_ED25519_VERIFICATION_KEY_2020 && verificationMethod.publicKeyMultibase ) { return Key.fromFingerprint(verificationMethod.publicKeyMultibase) } - if (verificationMethod.type === VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 && verificationMethod.publicKeyJwk) { - return Key.fromJwk(verificationMethod.publicKeyJwk as unknown as Jwk) - } throw new Error('Invalid verification method passed') }, diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts b/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts new file mode 100644 index 0000000000..3dd7c19d05 --- /dev/null +++ b/packages/core/src/modules/dids/domain/key-type/keyDidJsonWebKey.ts @@ -0,0 +1,20 @@ +import type { KeyDidMapping } from './keyDidMapping' +import type { VerificationMethod } from '../verificationMethod' + +import { Key } from '../../../../crypto' +import { AriesFrameworkError } from '../../../../error' +import { getJsonWebKey2020VerificationMethod } from '../verificationMethod' +import { VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, isJsonWebKey2020 } from '../verificationMethod/JsonWebKey2020' + +export const keyDidJsonWebKey: KeyDidMapping = { + supportedVerificationMethodTypes: [VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020], + getVerificationMethods: (did, key) => [getJsonWebKey2020VerificationMethod({ did, key })], + + getKeyFromVerificationMethod: (verificationMethod: VerificationMethod) => { + if (!isJsonWebKey2020(verificationMethod) || !verificationMethod.publicKeyJwk) { + throw new AriesFrameworkError('Invalid verification method passed') + } + + return Key.fromJwk(verificationMethod.publicKeyJwk) + }, +} diff --git a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts index bb788c8532..1449892e9b 100644 --- a/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts +++ b/packages/core/src/modules/dids/domain/key-type/keyDidMapping.ts @@ -1,12 +1,15 @@ -import type { Key } from '../../../../crypto/Key' import type { VerificationMethod } from '../verificationMethod' import { KeyType } from '../../../../crypto' +import { Key } from '../../../../crypto/Key' +import { AriesFrameworkError } from '../../../../error' +import { isJsonWebKey2020, VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 } from '../verificationMethod/JsonWebKey2020' import { keyDidBls12381g1 } from './bls12381g1' import { keyDidBls12381g1g2 } from './bls12381g1g2' import { keyDidBls12381g2 } from './bls12381g2' import { keyDidEd25519 } from './ed25519' +import { keyDidJsonWebKey } from './keyDidJsonWebKey' import { keyDidX25519 } from './x25519' export interface KeyDidMapping { @@ -22,6 +25,9 @@ const keyDidMapping: Record = { [KeyType.Bls12381g1]: keyDidBls12381g1, [KeyType.Bls12381g2]: keyDidBls12381g2, [KeyType.Bls12381g1g2]: keyDidBls12381g1g2, + [KeyType.P256]: keyDidJsonWebKey, + [KeyType.P384]: keyDidJsonWebKey, + [KeyType.P521]: keyDidJsonWebKey, } /** @@ -61,8 +67,19 @@ export function getKeyDidMappingByKeyType(keyType: KeyType) { } export function getKeyFromVerificationMethod(verificationMethod: VerificationMethod) { - const keyDid = verificationMethodKeyDidMapping[verificationMethod.type] + // This is a special verification method, as it supports basically all key types. + if (isJsonWebKey2020(verificationMethod)) { + // TODO: move this validation to another place + if (!verificationMethod.publicKeyJwk) { + throw new AriesFrameworkError( + `Missing publicKeyJwk on verification method with type ${VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020}` + ) + } + return Key.fromJwk(verificationMethod.publicKeyJwk) + } + + const keyDid = verificationMethodKeyDidMapping[verificationMethod.type] if (!keyDid) { throw new Error(`Unsupported key did from verification method type '${verificationMethod.type}'`) } diff --git a/packages/core/src/modules/dids/domain/keyDidDocument.ts b/packages/core/src/modules/dids/domain/keyDidDocument.ts index 537cb97d3d..af97546202 100644 --- a/packages/core/src/modules/dids/domain/keyDidDocument.ts +++ b/packages/core/src/modules/dids/domain/keyDidDocument.ts @@ -1,7 +1,9 @@ +import type { DidDocument } from './DidDocument' import type { VerificationMethod } from './verificationMethod/VerificationMethod' import { KeyType, Key } from '../../../crypto' -import { SECURITY_CONTEXT_BBS_URL, SECURITY_X25519_CONTEXT_URL } from '../../vc/constants' +import { AriesFrameworkError } from '../../../error' +import { SECURITY_CONTEXT_BBS_URL, SECURITY_JWS_CONTEXT_URL, SECURITY_X25519_CONTEXT_URL } from '../../vc/constants' import { ED25519_SUITE_CONTEXT_URL_2018 } from '../../vc/signature-suites/ed25519/constants' import { DidDocumentBuilder } from './DidDocumentBuilder' @@ -10,13 +12,17 @@ import { getBls12381g1g2VerificationMethod } from './key-type/bls12381g1g2' import { getBls12381g2VerificationMethod } from './key-type/bls12381g2' import { convertPublicKeyToX25519, getEd25519VerificationMethod } from './key-type/ed25519' import { getX25519VerificationMethod } from './key-type/x25519' +import { getJsonWebKey2020VerificationMethod } from './verificationMethod' -const didDocumentKeyTypeMapping = { +const didDocumentKeyTypeMapping: Record DidDocument> = { [KeyType.Ed25519]: getEd25519DidDoc, [KeyType.X25519]: getX25519DidDoc, [KeyType.Bls12381g1]: getBls12381g1DidDoc, [KeyType.Bls12381g2]: getBls12381g2DidDoc, [KeyType.Bls12381g1g2]: getBls12381g1g2DidDoc, + [KeyType.P256]: getJsonWebKey2020DidDocument, + [KeyType.P384]: getJsonWebKey2020DidDocument, + [KeyType.P521]: getJsonWebKey2020DidDocument, } export function getDidDocumentForKey(did: string, key: Key) { @@ -54,6 +60,31 @@ function getBls12381g1g2DidDoc(did: string, key: Key) { return didDocumentBuilder.addContext(SECURITY_CONTEXT_BBS_URL).build() } +export function getJsonWebKey2020DidDocument(did: string, key: Key) { + const verificationMethod = getJsonWebKey2020VerificationMethod({ did, key }) + + const didDocumentBuilder = new DidDocumentBuilder(did) + didDocumentBuilder.addContext(SECURITY_JWS_CONTEXT_URL).addVerificationMethod(verificationMethod) + + if (!key.supportsEncrypting && !key.supportsSigning) { + throw new AriesFrameworkError('Key must support at least signing or encrypting') + } + + if (key.supportsSigning) { + didDocumentBuilder + .addAuthentication(verificationMethod.id) + .addAssertionMethod(verificationMethod.id) + .addCapabilityDelegation(verificationMethod.id) + .addCapabilityInvocation(verificationMethod.id) + } + + if (key.supportsEncrypting) { + didDocumentBuilder.addKeyAgreement(verificationMethod.id) + } + + return didDocumentBuilder.build() +} + function getEd25519DidDoc(did: string, key: Key) { const verificationMethod = getEd25519VerificationMethod({ id: `${did}#${key.fingerprint}`, key, controller: did }) diff --git a/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts b/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts new file mode 100644 index 0000000000..a007f1d1fc --- /dev/null +++ b/packages/core/src/modules/dids/domain/verificationMethod/JsonWebKey2020.ts @@ -0,0 +1,36 @@ +import type { VerificationMethod } from './VerificationMethod' +import type { Jwk } from '../../../../crypto' + +import { Key } from '../../../../crypto' + +export const VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 = 'JsonWebKey2020' + +type JwkOrKey = { jwk: Jwk; key?: never } | { key: Key; jwk?: never } +type GetJsonWebKey2020VerificationMethodOptions = { + did: string + + verificationMethodId?: string +} & JwkOrKey + +export function getJsonWebKey2020VerificationMethod({ + did, + key, + jwk, + verificationMethodId, +}: GetJsonWebKey2020VerificationMethodOptions) { + if (!verificationMethodId) { + const k = key ?? Key.fromJwk(jwk) + verificationMethodId = `${did}#${k.fingerprint}` + } + + return { + id: verificationMethodId, + type: VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020, + controller: did, + publicKeyJwk: jwk ?? key.toJwk(), + } +} + +export function isJsonWebKey2020(verificationMethod: VerificationMethod) { + return verificationMethod.type === VERIFICATION_METHOD_TYPE_JSON_WEB_KEY_2020 +} diff --git a/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts b/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts index a86bd58978..632596fd57 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/VerificationMethod.ts @@ -1,3 +1,5 @@ +import type { Jwk } from '../../../../crypto' + import { IsString, IsOptional } from 'class-validator' export interface VerificationMethodOptions { @@ -6,7 +8,7 @@ export interface VerificationMethodOptions { controller: string publicKeyBase58?: string publicKeyBase64?: string - publicKeyJwk?: Record + publicKeyJwk?: Jwk publicKeyHex?: string publicKeyMultibase?: string publicKeyPem?: string @@ -48,8 +50,8 @@ export class VerificationMethod { @IsString() public publicKeyBase64?: string - // TODO: define JWK structure, we don't support JWK yet - public publicKeyJwk?: Record + // TODO: validation of JWK + public publicKeyJwk?: Jwk @IsOptional() @IsString() diff --git a/packages/core/src/modules/dids/domain/verificationMethod/index.ts b/packages/core/src/modules/dids/domain/verificationMethod/index.ts index 2bfdad4059..b263061277 100644 --- a/packages/core/src/modules/dids/domain/verificationMethod/index.ts +++ b/packages/core/src/modules/dids/domain/verificationMethod/index.ts @@ -1,4 +1,10 @@ +import { getJsonWebKey2020VerificationMethod } from './JsonWebKey2020' import { VerificationMethod } from './VerificationMethod' import { VerificationMethodTransformer, IsStringOrVerificationMethod } from './VerificationMethodTransformer' -export { VerificationMethod, VerificationMethodTransformer, IsStringOrVerificationMethod } +export { + VerificationMethod, + VerificationMethodTransformer, + IsStringOrVerificationMethod, + getJsonWebKey2020VerificationMethod, +} diff --git a/packages/core/src/modules/dids/methods/index.ts b/packages/core/src/modules/dids/methods/index.ts index 12f78247af..4faee9c44b 100644 --- a/packages/core/src/modules/dids/methods/index.ts +++ b/packages/core/src/modules/dids/methods/index.ts @@ -1,3 +1,4 @@ export * from './key' export * from './peer' export * from './web' +export * from './jwk' diff --git a/packages/core/src/modules/dids/methods/jwk/DidJwk.ts b/packages/core/src/modules/dids/methods/jwk/DidJwk.ts new file mode 100644 index 0000000000..f83b956e24 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/DidJwk.ts @@ -0,0 +1,55 @@ +import type { Jwk } from '../../../../crypto' + +import { Key } from '../../../../crypto/Key' +import { JsonEncoder } from '../../../../utils' +import { parseDid } from '../../domain/parse' + +import { getDidJwkDocument } from './didJwkDidDocument' + +export class DidJwk { + public readonly did: string + + private constructor(did: string) { + this.did = did + } + + public get allowsEncrypting() { + return this.jwk.use === 'enc' || this.key.supportsEncrypting + } + + public get allowsSigning() { + return this.jwk.use === 'sig' || this.key.supportsSigning + } + + public static fromDid(did: string) { + // We create a `Key` instance form the jwk, as that validates the jwk + const parsed = parseDid(did) + const jwk = JsonEncoder.fromBase64(parsed.id) as Jwk + Key.fromJwk(jwk) + + return new DidJwk(did) + } + + public static fromJwk(jwk: Jwk) { + // We create a `Key` instance form the jwk, as that validates the jwk + Key.fromJwk(jwk) + const did = `did:jwk:${JsonEncoder.toBase64URL(jwk)}` + + return new DidJwk(did) + } + + public get key() { + return Key.fromJwk(this.jwk) + } + + public get jwk() { + const parsed = parseDid(this.did) + const jwk = JsonEncoder.fromBase64(parsed.id) as Jwk + + return jwk + } + + public get didDocument() { + return getDidJwkDocument(this) + } +} diff --git a/packages/core/src/modules/dids/methods/jwk/JwkDidRegistrar.ts b/packages/core/src/modules/dids/methods/jwk/JwkDidRegistrar.ts new file mode 100644 index 0000000000..c5fc9b01e0 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/JwkDidRegistrar.ts @@ -0,0 +1,118 @@ +import type { AgentContext } from '../../../../agent' +import type { KeyType } from '../../../../crypto' +import type { Buffer } from '../../../../utils' +import type { DidRegistrar } from '../../domain/DidRegistrar' +import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' + +import { DidDocumentRole } from '../../domain/DidDocumentRole' +import { DidRepository, DidRecord } from '../../repository' + +import { DidJwk } from './DidJwk' + +export class JwkDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['jwk'] + + public async create(agentContext: AgentContext, options: JwkDidCreateOptions): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + + const keyType = options.options.keyType + const seed = options.secret?.seed + const privateKey = options.secret?.privateKey + + if (!keyType) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Missing key type', + }, + } + } + + try { + const key = await agentContext.wallet.createKey({ + keyType, + seed, + privateKey, + }) + + const didJwk = DidJwk.fromJwk(key.toJwk()) + + // Save the did so we know we created it and can issue with it + const didRecord = new DidRecord({ + did: didJwk.did, + role: DidDocumentRole.Created, + }) + await didRepository.save(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: didJwk.did, + didDocument: didJwk.didDocument, + secret: { + // FIXME: the uni-registrar creates the seed in the registrar method + // if it doesn't exist so the seed can always be returned. Currently + // we can only return it if the seed was passed in by the user. Once + // we have a secure method for generating seeds we should use the same + // approach + seed: options.secret?.seed, + privateKey: options.secret?.privateKey, + }, + }, + } + } catch (error) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `unknownError: ${error.message}`, + }, + } + } + } + + public async update(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notSupported: cannot update did:jwk did`, + }, + } + } + + public async deactivate(): Promise { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notSupported: cannot deactivate did:jwk did`, + }, + } + } +} + +export interface JwkDidCreateOptions extends DidCreateOptions { + method: 'jwk' + // For now we don't support creating a did:jwk with a did or did document + did?: never + didDocument?: never + options: { + keyType: KeyType + } + secret?: { + seed?: Buffer + privateKey?: Buffer + } +} + +// Update and Deactivate not supported for did:jwk +export type JwkDidUpdateOptions = never +export type JwkDidDeactivateOptions = never diff --git a/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts b/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts new file mode 100644 index 0000000000..8207814895 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/JwkDidResolver.ts @@ -0,0 +1,32 @@ +import type { AgentContext } from '../../../../agent' +import type { DidResolver } from '../../domain/DidResolver' +import type { DidResolutionResult } from '../../types' + +import { DidJwk } from './DidJwk' + +export class JwkDidResolver implements DidResolver { + public readonly supportedMethods = ['jwk'] + + public async resolve(agentContext: AgentContext, did: string): Promise { + const didDocumentMetadata = {} + + try { + const didDocument = DidJwk.fromDid(did).didDocument + + return { + didDocument, + didDocumentMetadata, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + } + } catch (error) { + return { + didDocument: null, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: `resolver_error: Unable to resolve did '${did}': ${error}`, + }, + } + } + } +} diff --git a/packages/core/src/modules/dids/methods/jwk/__tests__/DidJwk.test.ts b/packages/core/src/modules/dids/methods/jwk/__tests__/DidJwk.test.ts new file mode 100644 index 0000000000..406abe86a8 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/__tests__/DidJwk.test.ts @@ -0,0 +1,23 @@ +import { DidJwk } from '../DidJwk' + +import { p256DidJwkEyJjcnYi0iFixture } from './__fixtures__/p256DidJwkEyJjcnYi0i' +import { x25519DidJwkEyJrdHkiOiJFixture } from './__fixtures__/x25519DidJwkEyJrdHkiOiJ' + +describe('DidJwk', () => { + it('creates a DidJwk instance from a did', async () => { + const documentTypes = [p256DidJwkEyJjcnYi0iFixture, x25519DidJwkEyJrdHkiOiJFixture] + + for (const documentType of documentTypes) { + const didKey = DidJwk.fromDid(documentType.id) + + expect(didKey.didDocument.toJSON()).toMatchObject(documentType) + } + }) + + it('creates a DidJwk instance from a jwk instance', async () => { + const didJwk = DidJwk.fromJwk(p256DidJwkEyJjcnYi0iFixture.verificationMethod[0].publicKeyJwk) + + expect(didJwk.did).toBe(p256DidJwkEyJjcnYi0iFixture.id) + expect(didJwk.didDocument.toJSON()).toMatchObject(p256DidJwkEyJjcnYi0iFixture) + }) +}) diff --git a/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidRegistrar.test.ts new file mode 100644 index 0000000000..dc4f246b99 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidRegistrar.test.ts @@ -0,0 +1,193 @@ +import type { Wallet } from '../../../../../wallet' + +import { getAgentContext, mockFunction } from '../../../../../../tests/helpers' +import { KeyType } from '../../../../../crypto' +import { Key } from '../../../../../crypto/Key' +import { TypedArrayEncoder } from '../../../../../utils' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { WalletError } from '../../../../../wallet/error' +import { DidDocumentRole } from '../../../domain/DidDocumentRole' +import { DidRepository } from '../../../repository/DidRepository' +import { JwkDidRegistrar } from '../JwkDidRegistrar' + +jest.mock('../../../repository/DidRepository') +const DidRepositoryMock = DidRepository as jest.Mock + +const walletMock = { + createKey: jest.fn(() => + Key.fromJwk({ + crv: 'P-256', + kty: 'EC', + x: 'acbIQiuMs3i8_uszEjJ2tpTtRM4EU3yz91PH6CdH2V0', + y: '_KcyLj9vWMptnmKtm46GqDz8wf74I5LKgrl2GzH3nSE', + }) + ), +} as unknown as Wallet + +const didRepositoryMock = new DidRepositoryMock() +const jwkDidRegistrar = new JwkDidRegistrar() + +const agentContext = getAgentContext({ + wallet: walletMock, + registerInstances: [[DidRepository, didRepositoryMock]], +}) + +describe('DidRegistrar', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + describe('JwkDidRegistrar', () => { + it('should correctly create a did:jwk document using P256 key type', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + + const result = await jwkDidRegistrar.create(agentContext, { + method: 'jwk', + options: { + keyType: KeyType.P256, + }, + secret: { + privateKey, + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did: 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', + didDocument: { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', + verificationMethod: [ + { + id: 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + type: 'JsonWebKey2020', + controller: + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', + publicKeyJwk: { + crv: 'P-256', + kty: 'EC', + x: 'acbIQiuMs3i8_uszEjJ2tpTtRM4EU3yz91PH6CdH2V0', + y: '_KcyLj9vWMptnmKtm46GqDz8wf74I5LKgrl2GzH3nSE', + }, + }, + ], + assertionMethod: [ + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + authentication: [ + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + capabilityInvocation: [ + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + capabilityDelegation: [ + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + keyAgreement: [ + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + }, + secret: { + privateKey, + }, + }, + }) + + expect(walletMock.createKey).toHaveBeenCalledWith({ keyType: KeyType.P256, privateKey }) + }) + + it('should return an error state if no key type is provided', async () => { + const result = await jwkDidRegistrar.create(agentContext, { + method: 'jwk', + // @ts-expect-error - key type is required in interface + options: {}, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Missing key type', + }, + }) + }) + + it('should return an error state if a key creation error is thrown', async () => { + mockFunction(walletMock.createKey).mockRejectedValueOnce(new WalletError('Invalid private key provided')) + const result = await jwkDidRegistrar.create(agentContext, { + method: 'jwk', + options: { + keyType: KeyType.P256, + }, + secret: { + privateKey: TypedArrayEncoder.fromString('invalid'), + }, + }) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: expect.stringContaining('Invalid private key provided'), + }, + }) + }) + + it('should store the did document', async () => { + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') + const did = + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9' + + await jwkDidRegistrar.create(agentContext, { + method: 'jwk', + + options: { + keyType: KeyType.P256, + }, + secret: { + privateKey, + }, + }) + + expect(didRepositoryMock.save).toHaveBeenCalledTimes(1) + const [, didRecord] = mockFunction(didRepositoryMock.save).mock.calls[0] + + expect(didRecord).toMatchObject({ + did, + role: DidDocumentRole.Created, + didDocument: undefined, + }) + }) + + it('should return an error state when calling update', async () => { + const result = await jwkDidRegistrar.update() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notSupported: cannot update did:jwk did`, + }, + }) + }) + + it('should return an error state when calling deactivate', async () => { + const result = await jwkDidRegistrar.deactivate() + + expect(result).toEqual({ + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notSupported: cannot deactivate did:jwk did`, + }, + }) + }) + }) +}) diff --git a/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidResolver.test.ts b/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidResolver.test.ts new file mode 100644 index 0000000000..28dc34d497 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/__tests__/JwkDidResolver.test.ts @@ -0,0 +1,34 @@ +import type { AgentContext } from '../../../../../agent' + +import { getAgentContext } from '../../../../../../tests/helpers' +import { JsonTransformer } from '../../../../../utils/JsonTransformer' +import { DidJwk } from '../DidJwk' +import { JwkDidResolver } from '../JwkDidResolver' + +import { p256DidJwkEyJjcnYi0iFixture } from './__fixtures__/p256DidJwkEyJjcnYi0i' + +describe('DidResolver', () => { + describe('JwkDidResolver', () => { + let keyDidResolver: JwkDidResolver + let agentContext: AgentContext + + beforeEach(() => { + keyDidResolver = new JwkDidResolver() + agentContext = getAgentContext() + }) + + it('should correctly resolve a did:jwk document', async () => { + const fromDidSpy = jest.spyOn(DidJwk, 'fromDid') + const result = await keyDidResolver.resolve(agentContext, p256DidJwkEyJjcnYi0iFixture.id) + + expect(JsonTransformer.toJSON(result)).toMatchObject({ + didDocument: p256DidJwkEyJjcnYi0iFixture, + didDocumentMetadata: {}, + didResolutionMetadata: { contentType: 'application/did+ld+json' }, + }) + expect(result.didDocument) + expect(fromDidSpy).toHaveBeenCalledTimes(1) + expect(fromDidSpy).toHaveBeenCalledWith(p256DidJwkEyJjcnYi0iFixture.id) + }) + }) +}) diff --git a/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/p256DidJwkEyJjcnYi0i.ts b/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/p256DidJwkEyJjcnYi0i.ts new file mode 100644 index 0000000000..d086154f38 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/p256DidJwkEyJjcnYi0i.ts @@ -0,0 +1,33 @@ +export const p256DidJwkEyJjcnYi0iFixture = { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', + verificationMethod: [ + { + id: 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + type: 'JsonWebKey2020', + controller: + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9', + publicKeyJwk: { + crv: 'P-256', + kty: 'EC', + x: 'acbIQiuMs3i8_uszEjJ2tpTtRM4EU3yz91PH6CdH2V0', + y: '_KcyLj9vWMptnmKtm46GqDz8wf74I5LKgrl2GzH3nSE', + }, + }, + ], + assertionMethod: [ + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + authentication: [ + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + capabilityInvocation: [ + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + capabilityDelegation: [ + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], + keyAgreement: [ + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImFjYklRaXVNczNpOF91c3pFakoydHBUdFJNNEVVM3l6OTFQSDZDZEgyVjAiLCJ5IjoiX0tjeUxqOXZXTXB0bm1LdG00NkdxRHo4d2Y3NEk1TEtncmwyR3pIM25TRSJ9#0', + ], +} as const diff --git a/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/x25519DidJwkEyJrdHkiOiJ.ts b/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/x25519DidJwkEyJrdHkiOiJ.ts new file mode 100644 index 0000000000..dba397342f --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/__tests__/__fixtures__/x25519DidJwkEyJrdHkiOiJ.ts @@ -0,0 +1,21 @@ +export const x25519DidJwkEyJrdHkiOiJFixture = { + '@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'], + id: 'did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJYMjU1MTkiLCJ1c2UiOiJlbmMiLCJ4IjoiM3A3YmZYdDl3YlRUVzJIQzdPUTFOei1EUThoYmVHZE5yZngtRkctSUswOCJ9', + verificationMethod: [ + { + id: 'did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJYMjU1MTkiLCJ1c2UiOiJlbmMiLCJ4IjoiM3A3YmZYdDl3YlRUVzJIQzdPUTFOei1EUThoYmVHZE5yZngtRkctSUswOCJ9#0', + type: 'JsonWebKey2020', + controller: + 'did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJYMjU1MTkiLCJ1c2UiOiJlbmMiLCJ4IjoiM3A3YmZYdDl3YlRUVzJIQzdPUTFOei1EUThoYmVHZE5yZngtRkctSUswOCJ9', + publicKeyJwk: { + kty: 'OKP', + crv: 'X25519', + use: 'enc', + x: '3p7bfXt9wbTTW2HC7OQ1Nz-DQ8hbeGdNrfx-FG-IK08', + }, + }, + ], + keyAgreement: [ + 'did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJYMjU1MTkiLCJ1c2UiOiJlbmMiLCJ4IjoiM3A3YmZYdDl3YlRUVzJIQzdPUTFOei1EUThoYmVHZE5yZngtRkctSUswOCJ9#0', + ], +} as const diff --git a/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts b/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts new file mode 100644 index 0000000000..3906929375 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/didJwkDidDocument.ts @@ -0,0 +1,35 @@ +import type { DidJwk } from './DidJwk' + +import { AriesFrameworkError } from '../../../../error' +import { SECURITY_JWS_CONTEXT_URL } from '../../../vc/constants' +import { getJsonWebKey2020VerificationMethod, DidDocumentBuilder } from '../../domain' + +export function getDidJwkDocument(didJwk: DidJwk) { + if (!didJwk.allowsEncrypting && !didJwk.allowsSigning) { + throw new AriesFrameworkError('At least one of allowsSigning or allowsEncrypting must be enabled') + } + + const verificationMethod = getJsonWebKey2020VerificationMethod({ + did: didJwk.did, + jwk: didJwk.jwk, + verificationMethodId: `${didJwk.did}#0`, + }) + + const didDocumentBuilder = new DidDocumentBuilder(didJwk.did) + .addContext(SECURITY_JWS_CONTEXT_URL) + .addVerificationMethod(verificationMethod) + + if (didJwk.allowsSigning) { + didDocumentBuilder + .addAuthentication(verificationMethod.id) + .addAssertionMethod(verificationMethod.id) + .addCapabilityDelegation(verificationMethod.id) + .addCapabilityInvocation(verificationMethod.id) + } + + if (didJwk.allowsEncrypting) { + didDocumentBuilder.addKeyAgreement(verificationMethod.id) + } + + return didDocumentBuilder.build() +} diff --git a/packages/core/src/modules/dids/methods/jwk/index.ts b/packages/core/src/modules/dids/methods/jwk/index.ts new file mode 100644 index 0000000000..e377f85f95 --- /dev/null +++ b/packages/core/src/modules/dids/methods/jwk/index.ts @@ -0,0 +1,3 @@ +export { DidJwk } from './DidJwk' +export * from './JwkDidRegistrar' +export * from './JwkDidResolver' diff --git a/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts index bacfb3f1a9..a9a854cb1a 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/DidKey.test.ts @@ -4,12 +4,24 @@ import didKeyBls12381g1 from '../../../__tests__/__fixtures__/didKeyBls12381g1.j import didKeyBls12381g1g2 from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json' import didKeyBls12381g2 from '../../../__tests__/__fixtures__/didKeyBls12381g2.json' import didKeyEd25519 from '../../../__tests__/__fixtures__/didKeyEd25519.json' +import didKeyP256 from '../../../__tests__/__fixtures__/didKeyP256.json' +import didKeyP384 from '../../../__tests__/__fixtures__/didKeyP384.json' +import didKeyP521 from '../../../__tests__/__fixtures__/didKeyP521.json' import didKeyX25519 from '../../../__tests__/__fixtures__/didKeyX25519.json' import { DidKey } from '../DidKey' describe('DidKey', () => { it('creates a DidKey instance from a did', async () => { - const documentTypes = [didKeyX25519, didKeyEd25519, didKeyBls12381g1, didKeyBls12381g2, didKeyBls12381g1g2] + const documentTypes = [ + didKeyX25519, + didKeyEd25519, + didKeyBls12381g1, + didKeyBls12381g2, + didKeyBls12381g1g2, + didKeyP256, + didKeyP384, + didKeyP521, + ] for (const documentType of documentTypes) { const didKey = DidKey.fromDid(documentType.id) diff --git a/packages/core/src/modules/vc/constants.ts b/packages/core/src/modules/vc/constants.ts index b166244ebf..a9636cf016 100644 --- a/packages/core/src/modules/vc/constants.ts +++ b/packages/core/src/modules/vc/constants.ts @@ -13,3 +13,4 @@ export const SECURITY_SIGNATURE_URL = 'https://w3id.org/security#signature' export const VERIFIABLE_CREDENTIAL_TYPE = 'VerifiableCredential' export const VERIFIABLE_PRESENTATION_TYPE = 'VerifiablePresentation' export const EXPANDED_TYPE_CREDENTIALS_CONTEXT_V1_VC_TYPE = 'https://www.w3.org/2018/credentials#VerifiableCredential' +export const SECURITY_JWS_CONTEXT_URL = 'https://w3id.org/security/suites/jws-2020/v1' diff --git a/yarn.lock b/yarn.lock index 5b6b66c9c0..87c7ee9ec5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3604,6 +3604,11 @@ before-after-hook@^2.2.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== +big-integer@^1.6.51: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + bignumber.js@^9.0.0: version "9.1.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" From 9208910695708eea009cd9a6cc4db04aff77f375 Mon Sep 17 00:00:00 2001 From: Artemkaaas Date: Thu, 11 May 2023 14:40:47 +0300 Subject: [PATCH 138/139] Fixed merge and linter errors. DIDComm V2 package is not integrated yet. Signed-off-by: Artemkaaas --- demo/src/Alice.ts | 2 +- demo/src/BaseAgent.ts | 13 +- demo/src/Listener.ts | 17 +- packages/anoncreds/src/AnonCredsModule.ts | 4 +- .../AnonCredsCredentialFormatService.ts | 17 +- .../formats/AnonCredsProofFormatService.ts | 18 +- .../LegacyIndyCredentialFormatService.ts | 17 +- .../formats/LegacyIndyProofFormatService.ts | 20 +- .../legacy-indy-format-services.test.ts | 4 +- .../credentials/v1/V1CredentialProtocol.ts | 30 +-- .../V1CredentialProtocolCred.test.ts | 18 +- .../V1CredentialProtocolProposeOffer.test.ts | 12 +- .../v1/messages/V1IssueCredentialMessage.ts | 6 +- .../v1/messages/V1OfferCredentialMessage.ts | 6 +- .../v1/messages/V1ProposeCredentialMessage.ts | 7 +- .../v1/messages/V1RequestCredentialMessage.ts | 6 +- .../protocols/proofs/v1/V1ProofProtocol.ts | 26 +-- .../v1/__tests__/V1ProofProtocol.test.ts | 4 +- .../v1-connectionless-proofs.e2e.test.ts | 16 +- .../v1/messages/V1PresentationMessage.ts | 10 +- .../messages/V1ProposePresentationMessage.ts | 4 +- .../messages/V1RequestPresentationMessage.ts | 14 +- .../updates/0.3.1-0.4/credentialDefinition.ts | 3 +- .../anoncreds/src/updates/0.3.1-0.4/schema.ts | 3 +- .../__tests__/AskarStorageService.test.ts | 4 +- packages/askar/src/wallet/AskarWallet.ts | 200 +++++++++--------- .../src/wallet/__tests__/AskarWallet.test.ts | 16 +- .../src/wallet/__tests__/packing.test.ts | 24 +-- .../askar/tests/askar-postgres.e2e.test.ts | 2 +- .../src/Bls12381g2KeyProvider.ts | 2 +- .../tests/bbs-key-provider.e2e.test.ts | 2 +- .../tests/bbs-signatures.e2e.test.ts | 4 +- .../tests/cheqd-did-registrar.e2e.test.ts | 2 +- packages/core/src/agent/Agent.ts | 6 - packages/core/src/agent/AgentMessage.ts | 6 +- packages/core/src/agent/EnvelopeService.ts | 143 ++++++++----- packages/core/src/agent/MessageHandler.ts | 2 +- .../core/src/agent/MessageHandlerRegistry.ts | 2 +- packages/core/src/agent/MessageReceiver.ts | 42 +--- packages/core/src/agent/MessageSender.ts | 49 +++-- packages/core/src/agent/TransportService.ts | 6 +- .../agent/__tests__/DidCommV1Message.test.ts | 4 +- .../src/agent/__tests__/Dispatcher.test.ts | 22 +- .../src/agent/__tests__/MessageSender.test.ts | 5 +- packages/core/src/agent/__tests__/stubs.ts | 5 +- .../key-provider/KeyProviderRegistry.ts | 2 +- .../SigningProviderRegistry.ts | 37 ---- .../service/ServiceDecoratorExtension.ts | 2 +- .../signature/SignatureDecoratorUtils.test.ts | 4 +- packages/core/src/didcomm/index.ts | 4 +- packages/core/src/didcomm/types.ts | 2 +- .../didcomm/versions/v1/DidCommV1Message.ts | 21 +- .../core/src/didcomm/versions/v1/helpers.ts | 4 +- .../core/src/didcomm/versions/v1/index.ts | 11 +- .../indy/DefaultDidCommV1EnvelopeService.ts | 78 ------- .../didcomm/versions/v2/DidCommV2Message.ts | 5 + .../core/src/didcomm/versions/v2/helpers.ts | 2 +- .../core/src/didcomm/versions/v2/index.ts | 16 +- packages/core/src/index.ts | 3 - .../modules/connections/ConnectionEvents.ts | 19 -- .../src/modules/connections/ConnectionsApi.ts | 88 ++++---- .../connections/DidExchangeProtocol.ts | 8 +- .../modules/connections/TrustPingEvents.ts | 24 --- .../__tests__/ConnectionService.test.ts | 5 +- .../core/src/modules/connections/index.ts | 2 +- .../protocols/trust-ping/TrustPingEvents.ts | 31 +++ .../trust-ping/v1/V1TrustPingService.ts | 67 +++--- .../v1/handlers/TrustPingMessageHandler.ts | 9 +- .../trust-ping/v2/V2TrustPingService.ts | 24 +-- .../connections/services/ConnectionService.ts | 6 +- .../connections/services/TrustPingService.ts | 51 ----- .../src/modules/credentials/CredentialsApi.ts | 6 +- .../formats/CredentialFormatService.ts | 2 - .../formats/CredentialFormatServiceOptions.ts | 2 +- .../jsonld/JsonLdCredentialFormatService.ts | 2 +- .../JsonLdCredentialFormatService.test.ts | 2 +- .../protocol/BaseCredentialProtocol.ts | 42 ++-- .../protocol/CredentialProtocol.ts | 6 +- .../protocol/CredentialProtocolOptions.ts | 5 +- .../V2CredentialProtocolCred.test.ts | 1 - .../V2CredentialProtocolOffer.test.ts | 4 +- .../v2/messages/V2ProposeCredentialMessage.ts | 2 +- .../dids/__tests__/dids-registrar.e2e.test.ts | 4 - .../modules/dids/__tests__/peer-did.test.ts | 3 - .../services/DiscoverFeaturesService.ts | 8 +- .../MessagePickupApiOptions.ts" | 2 +- .../protocol/BaseMessagePickupProtocol.ts" | 4 +- .../protocol/MessagePickupProtocol.ts" | 4 +- .../protocol/MessagePickupProtocolOptions.ts" | 4 +- .../protocol/v1/V1MessagePickupProtocol.ts" | 4 +- .../protocol/v1/messages/V1BatchMessage.ts" | 10 +- .../v1/messages/V1BatchPickupMessage.ts" | 4 +- .../protocol/v2/V2MessagePickupProtocol.ts" | 10 +- .../V2MessagePickupProtocol.test.ts" | 14 +- .../v2/messages/V2DeliveryRequestMessage.ts" | 4 +- .../v2/messages/V2MessageDeliveryMessage.ts" | 6 +- .../v2/messages/V2MessagesReceivedMessage.ts" | 4 +- .../protocol/v2/messages/V2StatusMessage.ts" | 2 +- .../v2/messages/V2StatusRequestMessage.ts" | 2 +- packages/core/src/modules/oob/OutOfBandApi.ts | 56 +++-- .../oob/__tests__/OutOfBandService.test.ts | 1 - .../oob/__tests__/implicit.e2e.test.ts | 2 +- .../oob/protocols/v1/OutOfBandService.ts | 38 ++-- .../oob/protocols/v2/V2OutOfBandService.ts | 4 +- .../modules/oob/repository/OutOfBandRecord.ts | 2 +- packages/core/src/modules/proofs/ProofsApi.ts | 12 +- .../formats/ProofFormatServiceOptions.ts | 2 +- .../proofs/protocol/BaseProofProtocol.ts | 38 ++-- .../modules/proofs/protocol/ProofProtocol.ts | 30 +-- .../proofs/protocol/ProofProtocolOptions.ts | 4 +- .../protocol/v2/ProofFormatCoordinator.ts | 12 +- .../proofs/protocol/v2/V2ProofProtocol.ts | 4 +- .../v2/__tests__/V2ProofProtocol.test.ts | 2 +- .../v2-indy-connectionless-proofs.e2e.test.ts | 14 +- .../v2/__tests__/v2-indy-proofs.e2e.test.ts | 10 +- .../v2/messages/V2PresentationMessage.ts | 3 +- .../messages/V2ProposePresentationMessage.ts | 16 +- .../messages/V2RequestPresentationMessage.ts | 3 +- .../core/src/modules/routing/MediatorApi.ts | 2 +- .../routing/__tests__/mediation.test.ts | 10 +- .../services/MediationRecipientService.ts | 2 - .../MediationRecipientService.test.ts | 4 - .../services/__tests__/RoutingService.test.ts | 2 +- .../src/storage/InMemoryMessageRepository.ts | 2 +- .../storage/didcomm/DidCommMessageRecord.ts | 2 +- .../__tests__/__snapshots__/0.1.test.ts.snap | 7 + .../core/src/transport/WsOutboundTransport.ts | 2 - packages/core/src/types.ts | 1 + packages/core/src/utils/attachment.ts | 2 +- packages/core/src/wallet/Wallet.ts | 23 +- packages/core/tests/connections.test.ts | 2 +- packages/core/tests/helpers.ts | 11 +- packages/core/tests/mocks/MockWallet.ts | 8 +- .../core/tests/multi-protocol-version.test.ts | 2 +- .../tests/oob-mediation-provision.test.ts | 5 +- packages/core/tests/oob.test.ts | 16 +- packages/didcomm-v2/README.md | 57 ----- packages/didcomm-v2/jest.config.ts | 1 - packages/didcomm-v2/package.json | 14 +- packages/didcomm-v2/src/DidCommV2Module.ts | 6 +- packages/didcomm-v2/src/index.ts | 2 +- .../src/services/DidCommV2DidResolver.ts | 100 +++------ ...iceImpl.ts => DidCommV2EnvelopeService.ts} | 7 +- .../src/services/DidCommV2SecretsResolver.ts | 4 +- packages/didcomm-v2/src/services/index.ts | 6 +- .../tests/DidCommV2DidResolver.test.ts | 58 ++--- .../tests/DidCommV2EnvelopeService.test.ts | 58 +++-- .../didcomm-v2/tests/DidCommV2Module.test.ts | 12 +- packages/didcomm-v2/tsconfig.build.json | 9 - .../__tests__/IndySdkIndyDidRegistrar.test.ts | 10 +- .../__tests__/IndySdkIndyDidResolver.test.ts | 6 +- .../__tests__/IndySdkSovDidResolver.test.ts | 6 +- .../didIndyPool1WJz9mHyW9BZksioQnRsrAo.json | 2 +- packages/indy-sdk/src/dids/didSovUtil.ts | 6 +- .../indy-sdk/src/ledger/IndySdkPoolService.ts | 2 +- .../__tests__/IndySdkPoolService.test.ts | 4 +- .../__tests__/IndySdkStorageService.test.ts | 4 +- packages/indy-sdk/src/wallet/IndySdkWallet.ts | 40 ++-- .../wallet/__tests__/IndySdkWallet.test.ts | 8 +- .../tests/indy-did-registrar.e2e.test.ts | 4 +- .../__tests__/IndyVdrIndyDidRegistrar.test.ts | 12 +- .../__tests__/IndyVdrIndyDidResolver.test.ts | 2 +- .../__tests__/IndyVdrSovDidResolver.test.ts | 2 +- .../didIndyWJz9mHyW9BZksioQnRsrAo.json | 2 +- .../didSovWJz9mHyW9BZksioQnRsrAo.json | 2 +- packages/indy-vdr/src/dids/didSovUtil.ts | 10 +- .../tests/indy-vdr-did-registrar.e2e.test.ts | 4 +- .../indy-vdr-indy-did-resolver.e2e.test.ts | 2 +- .../indy-vdr/tests/indy-vdr-pool.e2e.test.ts | 4 +- .../indy-vdr-sov-did-resolver.e2e.test.ts | 2 +- .../__tests__/QuestionAnswerService.test.ts | 4 +- txn.txt | 4 + yarn.lock | 10 + 173 files changed, 984 insertions(+), 1371 deletions(-) delete mode 100644 packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts delete mode 100644 packages/core/src/didcomm/versions/v1/indy/DefaultDidCommV1EnvelopeService.ts delete mode 100644 packages/core/src/modules/connections/TrustPingEvents.ts create mode 100644 packages/core/src/modules/connections/protocols/trust-ping/TrustPingEvents.ts delete mode 100644 packages/core/src/modules/connections/services/TrustPingService.ts rename packages/didcomm-v2/src/services/{DidCommV2EnvelopeServiceImpl.ts => DidCommV2EnvelopeService.ts} (94%) create mode 100644 txn.txt diff --git a/demo/src/Alice.ts b/demo/src/Alice.ts index 1e8bbbf6d0..86b09fc6a1 100644 --- a/demo/src/Alice.ts +++ b/demo/src/Alice.ts @@ -75,7 +75,7 @@ export class Alice extends BaseAgent { public async ping() { const connectionRecord = await this.getConnectionRecord() - await this.agent.connections.sendPing(connectionRecord.id) + await this.agent.connections.sendPing(connectionRecord.id, {}) } public async exit() { diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index 9951520bf4..c7b9902686 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -1,9 +1,5 @@ import type { InitConfig } from '@aries-framework/core' -import type { IndySdkPoolConfig } from '@aries-framework/indy-sdk' -import type { IndyVdrPoolConfig } from '@aries-framework/indy-vdr' -import { Agent, AutoAcceptCredential, AutoAcceptProof, HttpOutboundTransport } from '@aries-framework/core' -import { DidCommV2Module } from '@aries-framework/didcomm-v2' import { AnonCredsCredentialFormatService, AnonCredsModule, @@ -37,7 +33,6 @@ import { import { IndySdkAnonCredsRegistry, IndySdkModule, IndySdkSovDidResolver } from '@aries-framework/indy-sdk' import { IndyVdrIndyDidResolver, IndyVdrAnonCredsRegistry, IndyVdrModule } from '@aries-framework/indy-vdr' import { agentDependencies, HttpInboundTransport } from '@aries-framework/node' -import * as didcomm from 'didcomm-node' import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' @@ -58,7 +53,7 @@ export const indyNetworkConfig = { indyNamespace: 'bcovrin:test', isProduction: false, connectOnStartup: true, -} satisfies IndySdkPoolConfig | IndyVdrPoolConfig +} type DemoAgent = Agent> @@ -88,7 +83,7 @@ export class BaseAgent { key: name, }, endpoints: [`http://localhost:${this.port}`], - } satisfies InitConfig + } this.config = config @@ -114,9 +109,6 @@ function getAskarAnonCredsIndyModules() { const legacyIndyCredentialFormatService = new LegacyIndyCredentialFormatService() const legacyIndyProofFormatService = new LegacyIndyProofFormatService() - // Enable also didcomm-v2 module - const didCommV2Module = new DidCommV2Module({ didcomm }) - return { connections: new ConnectionsModule({ autoAcceptConnections: true, @@ -171,7 +163,6 @@ function getAskarAnonCredsIndyModules() { askar: new AskarModule({ ariesAskar, }), - didCommV2: didCommV2Module, } as const } diff --git a/demo/src/Listener.ts b/demo/src/Listener.ts index 251de5c5f1..703e9a5ce9 100644 --- a/demo/src/Listener.ts +++ b/demo/src/Listener.ts @@ -7,8 +7,8 @@ import type { BasicMessageStateChangedEvent, CredentialExchangeRecord, CredentialStateChangedEvent, - PingReceivedEvent, - PingResponseReceivedEvent, + TrustPingReceivedEvent, + TrustPingResponseReceivedEvent, ProofExchangeRecord, ProofStateChangedEvent, } from '@aries-framework/core' @@ -82,12 +82,15 @@ export class Listener { } public pingListener(agent: Agent, name: string) { - agent.events.on(TrustPingEventTypes.PingReceived, async (event: PingReceivedEvent) => { - this.ui.updateBottomBar(purpleText(`\n${name} received ping message from ${event.payload.from}\n`)) - }) - agent.events.on(TrustPingEventTypes.PingResponseReceived, async (event: PingResponseReceivedEvent) => { - this.ui.updateBottomBar(purpleText(`\n${name} received ping response message from ${event.payload.from}\n`)) + agent.events.on(TrustPingEventTypes.TrustPingReceivedEvent, async (event: TrustPingReceivedEvent) => { + this.ui.updateBottomBar(purpleText(`\n${name} received ping message from ${event.payload}\n`)) }) + agent.events.on( + TrustPingEventTypes.TrustPingResponseReceivedEvent, + async (event: TrustPingResponseReceivedEvent) => { + this.ui.updateBottomBar(purpleText(`\n${name} received ping response message from ${event.payload}\n`)) + } + ) } private async newProofRequestPrompt(proofRecord: ProofExchangeRecord, aliceInquirer: AliceInquirer) { diff --git a/packages/anoncreds/src/AnonCredsModule.ts b/packages/anoncreds/src/AnonCredsModule.ts index 873288348c..11b923e093 100644 --- a/packages/anoncreds/src/AnonCredsModule.ts +++ b/packages/anoncreds/src/AnonCredsModule.ts @@ -38,11 +38,11 @@ export class AnonCredsModule implements Module { dependencyManager.registerSingleton(AnonCredsLinkSecretRepository) } - public updates = [ + public updates: Update[] = [ { fromVersion: '0.3.1', toVersion: '0.4', doUpdate: updateAnonCredsModuleV0_3_1ToV0_4, }, - ] satisfies Update[] + ] } diff --git a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts index 28d7d47185..f9627cd0aa 100644 --- a/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsCredentialFormatService.ts @@ -34,11 +34,12 @@ import { MessageValidator, CredentialFormatSpec, AriesFrameworkError, - Attachment, JsonEncoder, utils, CredentialProblemReportReason, JsonTransformer, + V1Attachment, + V1AttachmentData, } from '@aries-framework/core' import { AnonCredsError } from '../error' @@ -450,7 +451,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService * @returns The Attachment if found or undefined * */ - public getAttachment(formats: CredentialFormatSpec[], messageAttachments: Attachment[]): Attachment | undefined { + public getAttachment(formats: CredentialFormatSpec[], messageAttachments: V1Attachment[]): V1Attachment | undefined { const supportedAttachmentIds = formats.filter((f) => this.supportsFormat(f.format)).map((f) => f.attachmentId) const supportedAttachment = messageAttachments.find((attachment) => supportedAttachmentIds.includes(attachment.id)) @@ -594,7 +595,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService attributes?: CredentialPreviewAttributeOptions[], linkedAttachments?: LinkedAttachment[] ): { - attachments?: Attachment[] + attachments?: V1Attachment[] previewAttributes?: CredentialPreviewAttributeOptions[] } { if (!linkedAttachments && !attributes) { @@ -602,7 +603,7 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService } let previewAttributes = attributes ?? [] - let attachments: Attachment[] | undefined + let attachments: V1Attachment[] | undefined if (linkedAttachments) { // there are linked attachments so transform into the attribute field of the CredentialPreview object for @@ -621,13 +622,13 @@ export class AnonCredsCredentialFormatService implements CredentialFormatService * @param data The data to include in the attach object * @param id the attach id from the formats component of the message */ - public getFormatData(data: unknown, id: string): Attachment { - const attachment = new Attachment({ + public getFormatData(data: unknown, id: string): V1Attachment { + const attachment = new V1Attachment({ id, mimeType: 'application/json', - data: { + data: new V1AttachmentData({ base64: JsonEncoder.toBase64(data), - }, + }), }) return attachment diff --git a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts index 001aebb340..138876195c 100644 --- a/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts +++ b/packages/anoncreds/src/formats/AnonCredsProofFormatService.ts @@ -8,9 +8,7 @@ import type { AnonCredsCredentialInfo, AnonCredsProof, AnonCredsRequestedAttribute, - AnonCredsRequestedAttributeMatch, AnonCredsRequestedPredicate, - AnonCredsRequestedPredicateMatch, AnonCredsSchema, AnonCredsSelectedCredentials, AnonCredsProofRequest, @@ -37,8 +35,8 @@ import type { import { AriesFrameworkError, - Attachment, - AttachmentData, + V1Attachment, + V1AttachmentData, JsonEncoder, ProofFormatSpec, JsonTransformer, @@ -145,7 +143,7 @@ export class AnonCredsProofFormatService implements ProofFormatService this.supportsFormat(f.format)).map((f) => f.attachmentId) const supportedAttachment = messageAttachments.find((attachment) => supportedAttachmentIds.includes(attachment.id)) @@ -607,7 +608,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic attributes?: CredentialPreviewAttributeOptions[], linkedAttachments?: LinkedAttachment[] ): { - attachments?: Attachment[] + attachments?: V1Attachment[] previewAttributes?: CredentialPreviewAttributeOptions[] } { if (!linkedAttachments && !attributes) { @@ -615,7 +616,7 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic } let previewAttributes = attributes ?? [] - let attachments: Attachment[] | undefined + let attachments: V1Attachment[] | undefined if (linkedAttachments) { // there are linked attachments so transform into the attribute field of the CredentialPreview object for @@ -634,13 +635,13 @@ export class LegacyIndyCredentialFormatService implements CredentialFormatServic * @param data The data to include in the attach object * @param id the attach id from the formats component of the message */ - public getFormatData(data: unknown, id: string): Attachment { - const attachment = new Attachment({ + public getFormatData(data: unknown, id: string): V1Attachment { + const attachment = new V1Attachment({ id, mimeType: 'application/json', - data: { + data: new V1AttachmentData({ base64: JsonEncoder.toBase64(data), - }, + }), }) return attachment diff --git a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts index c912f5f692..1890acc59e 100644 --- a/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts +++ b/packages/anoncreds/src/formats/LegacyIndyProofFormatService.ts @@ -8,9 +8,7 @@ import type { AnonCredsCredentialInfo, AnonCredsProof, AnonCredsRequestedAttribute, - AnonCredsRequestedAttributeMatch, AnonCredsRequestedPredicate, - AnonCredsRequestedPredicateMatch, AnonCredsSchema, AnonCredsSelectedCredentials, AnonCredsProofRequest, @@ -37,8 +35,8 @@ import type { import { AriesFrameworkError, - Attachment, - AttachmentData, + V1Attachment, + V1AttachmentData, JsonEncoder, ProofFormatSpec, JsonTransformer, @@ -55,8 +53,6 @@ import { checkValidCredentialValueEncoding, encodeCredentialValue, assertNoDuplicateGroupsNamesInProofRequest, - unqualifiedCredentialDefinitionIdRegex, - unqualifiedSchemaIdRegex, getRevocationRegistriesForRequest, getRevocationRegistriesForProof, } from '../utils' @@ -148,7 +144,7 @@ export class LegacyIndyProofFormatService implements ProofFormatService(indySdk) const eventEmitter = new EventEmitter(agentDependencies, new Subject()) const anonCredsLinkSecretRepository = new AnonCredsLinkSecretRepository(storageService, eventEmitter) diff --git a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts index 0ca1f05de5..ab7ba09cff 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/V1CredentialProtocol.ts @@ -1,9 +1,3 @@ -import type { AgentContext } from '../../../../agent' -import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' -import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' -import type { DidCommV1Message } from '../../../../didcomm' -import type { DependencyManager } from '../../../../plugins' -import type { ProblemReportMessage } from '../../../problem-reports' import type { LegacyIndyCredentialFormatService } from '../../../formats' import type { AgentContext, @@ -14,6 +8,7 @@ import type { ProblemReportMessage, ExtractCredentialFormats, CredentialProtocol, + DidCommV1Message, } from '@aries-framework/core' import { @@ -24,8 +19,8 @@ import { CredentialState, JsonTransformer, ConnectionService, - Attachment, - AttachmentData, + V1Attachment, + V1AttachmentData, AckStatus, CredentialProblemReportReason, CredentialsModuleConfig, @@ -39,25 +34,6 @@ import { import { AnonCredsCredentialProposal } from '../../../models/AnonCredsCredentialProposal' import { composeCredentialAutoAccept, areCredentialPreviewAttributesEqual } from '../../../utils' -import { Protocol } from '../../../../agent/models/features' -import { V1Attachment, V1AttachmentData } from '../../../../decorators/attachment/V1Attachment' -import { AriesFrameworkError } from '../../../../error' -import { injectable } from '../../../../plugins' -import { DidCommMessageRepository, DidCommMessageRole } from '../../../../storage' -import { JsonTransformer } from '../../../../utils' -import { isLinkedAttachment } from '../../../../utils/attachment' -import { uuid } from '../../../../utils/uuid' -import { AckStatus } from '../../../common' -import { ConnectionService } from '../../../connections/services' -import { CredentialsModuleConfig } from '../../CredentialsModuleConfig' -import { CredentialProblemReportReason } from '../../errors' -import { IndyCredPropose } from '../../formats/indy/models' -import { AutoAcceptCredential } from '../../models/CredentialAutoAcceptType' -import { CredentialState } from '../../models/CredentialState' -import { CredentialExchangeRecord, CredentialRepository } from '../../repository' -import { composeAutoAccept } from '../../util/composeAutoAccept' -import { arePreviewAttributesEqual } from '../../util/previewAttributes' -import { BaseCredentialProtocol } from '../BaseCredentialProtocol' import { V1ProposeCredentialHandler, diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts index eb255070cc..5bbd6a698f 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolCred.test.ts @@ -9,8 +9,6 @@ import type { import { EventEmitter, DidExchangeState, - Attachment, - AttachmentData, JsonEncoder, DidCommMessageRecord, DidCommMessageRole, @@ -24,6 +22,8 @@ import { CredentialEventTypes, AckStatus, CredentialProblemReportReason, + V1Attachment, + V1AttachmentData, } from '@aries-framework/core' import { Subject } from 'rxjs' @@ -79,27 +79,27 @@ const credentialPreview = V1CredentialPreview.fromRecord({ age: '99', }) -const offerAttachment = new Attachment({ +const offerAttachment = new V1Attachment({ id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, mimeType: 'application/json', - data: new AttachmentData({ + data: new V1AttachmentData({ base64: 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', }), }) -const requestAttachment = new Attachment({ +const requestAttachment = new V1Attachment({ id: INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID, mimeType: 'application/json', - data: new AttachmentData({ + data: new V1AttachmentData({ base64: JsonEncoder.toBase64({}), }), }) -const credentialAttachment = new Attachment({ +const credentialAttachment = new V1Attachment({ id: INDY_CREDENTIAL_ATTACHMENT_ID, mimeType: 'application/json', - data: new AttachmentData({ + data: new V1AttachmentData({ base64: JsonEncoder.toBase64({ values: convertAttributesToCredentialValues(credentialPreview.attributes), }), @@ -525,7 +525,7 @@ describe('V1CredentialProtocol', () => { expect(legacyIndyCredentialFormatService.processCredential).toHaveBeenNthCalledWith(1, agentContext, { attachment: credentialAttachment, credentialRecord, - requestAttachment: expect.any(Attachment), + requestAttachment: expect.any(V1Attachment), }) }) }) diff --git a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts index f4a72ce08c..836be6371c 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts @@ -3,8 +3,8 @@ import type { CredentialProtocolOptions, CredentialStateChangedEvent } from '@ar import { EventEmitter, DidExchangeState, - Attachment, - AttachmentData, + V1Attachment, + V1AttachmentData, CredentialState, CredentialFormatSpec, CredentialExchangeRecord, @@ -67,17 +67,17 @@ const credentialPreview = V1CredentialPreview.fromRecord({ age: '99', }) -const offerAttachment = new Attachment({ +const offerAttachment = new V1Attachment({ id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, mimeType: 'application/json', - data: new AttachmentData({ + data: new V1AttachmentData({ base64: 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', }), }) -const proposalAttachment = new Attachment({ - data: new AttachmentData({ +const proposalAttachment = new V1Attachment({ + data: new V1AttachmentData({ json: { cred_def_id: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG', schema_issuer_did: 'GMm4vMw8LLrLJjp81kRRLp', diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts index baa91d77c0..c50278f911 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1IssueCredentialMessage.ts @@ -1,12 +1,8 @@ import type { AnonCredsCredential } from '../../../../models' +import { DidCommV1Message, IsValidMessageType, parseMessageType, V1Attachment } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsString, IsOptional, IsArray, ValidateNested, IsInstance } from 'class-validator' -import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' - -import { V1Attachment } from '../../../../../decorators/attachment/V1Attachment' -import { DidCommV1Message } from '../../../../../didcomm' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export const INDY_CREDENTIAL_ATTACHMENT_ID = 'libindy-cred-0' diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts index ce1ac45a73..3145291321 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1OfferCredentialMessage.ts @@ -1,13 +1,9 @@ import type { AnonCredsCredentialOffer } from '../../../../models' -import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { DidCommV1Message, IsValidMessageType, parseMessageType, V1Attachment } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { V1Attachment } from '../../../../../decorators/attachment/V1Attachment' -import { DidCommV1Message } from '../../../../../didcomm' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' - import { V1CredentialPreview } from './V1CredentialPreview' export const INDY_CREDENTIAL_OFFER_ATTACHMENT_ID = 'libindy-cred-offer-0' diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts index 423270603d..62e8613a1e 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1ProposeCredentialMessage.ts @@ -1,12 +1,9 @@ -import type { V1Attachment } from '../../../../../decorators/attachment/V1Attachment' +import type { V1Attachment } from '@aries-framework/core' -import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { DidCommV1Message, IsValidMessageType, parseMessageType } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString, Matches, ValidateNested } from 'class-validator' -import { DidCommV1Message } from '../../../../../didcomm' -import { indyDidRegex, schemaIdRegex, schemaVersionRegex, credDefIdRegex } from '../../../../../utils' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { unqualifiedCredentialDefinitionIdRegex, unqualifiedIndyDidRegex, diff --git a/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts b/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts index 87e6188fc6..edc09a6049 100644 --- a/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts +++ b/packages/anoncreds/src/protocols/credentials/v1/messages/V1RequestCredentialMessage.ts @@ -1,13 +1,9 @@ import type { LegacyIndyCredentialRequest } from '../../../../formats' -import { IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { DidCommV1Message, IsValidMessageType, parseMessageType, V1Attachment } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { V1Attachment } from '../../../../../decorators/attachment/V1Attachment' -import { DidCommV1Message } from '../../../../../didcomm' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' - export const INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID = 'libindy-cred-request-0' export interface V1RequestCredentialMessageOptions { diff --git a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts index 5e27debbfb..c7c88c15ae 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/V1ProofProtocol.ts @@ -6,10 +6,10 @@ import type { AgentContext, ProofProtocolOptions, InboundMessageContext, - AgentMessage, ProblemReportMessage, GetProofFormatDataReturn, ProofFormat, + DidCommV1Message, } from '@aries-framework/core' import { @@ -23,7 +23,7 @@ import { ProofState, DidCommMessageRole, ConnectionService, - Attachment, + V1Attachment, JsonTransformer, PresentationProblemReportReason, AckStatus, @@ -261,7 +261,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< nonce: await agentContext.wallet.generateNonce(), }) - const proposalAttachment = new Attachment({ + const proposalAttachment = new V1Attachment({ data: { json: JsonTransformer.toJSON(requestFromPreview), }, @@ -305,7 +305,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< comment, autoAcceptProof, }: ProofProtocolOptions.NegotiateProofProposalOptions<[LegacyIndyProofFormatService]> - ): Promise> { + ): Promise> { // Assert proofRecord.assertProtocolVersion('v1') proofRecord.assertState(ProofState.ProposalReceived) @@ -349,7 +349,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< parentThreadId, autoAcceptProof, }: ProofProtocolOptions.CreateProofRequestOptions<[LegacyIndyProofFormatService]> - ): Promise> { + ): Promise> { this.assertOnlyIndyFormat(proofFormats) const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) @@ -493,7 +493,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< comment, autoAcceptProof, }: ProofProtocolOptions.NegotiateProofRequestOptions<[LegacyIndyProofFormatService]> - ): Promise> { + ): Promise> { // Assert proofRecord.assertProtocolVersion('v1') proofRecord.assertState(ProofState.RequestReceived) @@ -546,7 +546,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< autoAcceptProof, comment, }: ProofProtocolOptions.AcceptProofRequestOptions<[LegacyIndyProofFormatService]> - ): Promise> { + ): Promise> { // Assert proofRecord.assertProtocolVersion('v1') proofRecord.assertState(ProofState.RequestReceived) @@ -573,7 +573,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< } const proposalAttachment = proposalMessage - ? new Attachment({ + ? new V1Attachment({ data: { json: JsonTransformer.toJSON( createRequestFromPreview({ @@ -643,7 +643,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< } const proposalAttachment = proposalMessage - ? new Attachment({ + ? new V1Attachment({ data: { json: JsonTransformer.toJSON( createRequestFromPreview({ @@ -703,7 +703,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< } const proposalAttachment = proposalMessage - ? new Attachment({ + ? new V1Attachment({ data: { json: JsonTransformer.toJSON( createRequestFromPreview({ @@ -919,7 +919,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< return this.indyProofFormat.shouldAutoRespondToProposal(agentContext, { proofRecord, - proposalAttachment: new Attachment({ + proposalAttachment: new V1Attachment({ data: { json: rfc0592Proposal, }, @@ -962,7 +962,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< return this.indyProofFormat.shouldAutoRespondToRequest(agentContext, { proofRecord, - proposalAttachment: new Attachment({ + proposalAttachment: new V1Attachment({ data: { base64: JsonEncoder.toBase64(rfc0592Proposal), }, @@ -1015,7 +1015,7 @@ export class V1ProofProtocol extends BaseProofProtocol implements ProofProtocol< proofRecord, requestAttachment, presentationAttachment, - proposalAttachment: new Attachment({ + proposalAttachment: new V1Attachment({ data: { json: rfc0592Proposal, }, diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts index a4e39f3108..09297563a0 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/V1ProofProtocol.test.ts @@ -4,8 +4,8 @@ import { Subject } from 'rxjs' import { DidExchangeState, - Attachment, - AttachmentData, + V1Attachment, + V1AttachmentData, ProofState, ProofExchangeRecord, InboundMessageContext, diff --git a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts index 47bb04e2d4..2b95a0efbb 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-connectionless-proofs.e2e.test.ts @@ -13,8 +13,8 @@ import { HandshakeProtocol, MediatorPickupStrategy, LinkedAttachment, - Attachment, - AttachmentData, + V1Attachment, + V1AttachmentData, ProofEventTypes, MediatorModule, MediationRecipientModule, @@ -401,7 +401,7 @@ describe('V1 Proofs - Connectionless - Indy', () => { autoAcceptProofs: AutoAcceptProof.Always, }), mediationRecipient: new MediationRecipientModule({ - mediatorInvitationUrl: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + mediatorInvitationUrl: faberMediationOutOfBandRecord.getOutOfBandInvitation().toUrl({ domain: 'https://example.com', }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, @@ -417,7 +417,7 @@ describe('V1 Proofs - Connectionless - Indy', () => { autoAcceptProofs: AutoAcceptProof.Always, }), mediationRecipient: new MediationRecipientModule({ - mediatorInvitationUrl: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + mediatorInvitationUrl: aliceMediationOutOfBandRecord.getOutOfBandInvitation().toUrl({ domain: 'https://example.com', }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, @@ -465,16 +465,16 @@ describe('V1 Proofs - Connectionless - Indy', () => { linkedAttachments: [ new LinkedAttachment({ name: 'image_0', - attachment: new Attachment({ + attachment: new V1Attachment({ filename: 'picture-of-a-cat.png', - data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + data: new V1AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), }), }), new LinkedAttachment({ name: 'image_1', - attachment: new Attachment({ + attachment: new V1Attachment({ filename: 'picture-of-a-dog.png', - data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + data: new V1AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), }), }), ], diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts index a5bbd4a9dd..628362cc13 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1PresentationMessage.ts @@ -1,15 +1,9 @@ import type { AnonCredsProof } from '../../../../models' +import { IsValidMessageType, parseMessageType, DidCommV1Message, V1Attachment } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' -import { V1Attachment } from '../../../../../decorators/attachment/V1Attachment' -import { DidCommV1Message } from '../../../../../didcomm' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' -import { V2_INDY_PRESENTATION } from '../../../formats/ProofFormatConstants' -import { ProofFormatSpec } from '../../../models/ProofFormatSpec' - export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0' export interface V1PresentationMessageOptions { @@ -71,7 +65,7 @@ export class V1PresentationMessage extends DidCommV1Message { return proofJson } - public getPresentationAttachmentById(id: string): Attachment | undefined { + public getPresentationAttachmentById(id: string): V1Attachment | undefined { return this.presentationAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts index 66803508fa..1078e93aa4 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1ProposePresentationMessage.ts @@ -1,9 +1,7 @@ -import { IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { IsValidMessageType, parseMessageType, DidCommV1Message } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { DidCommV1Message } from '../../../../../didcomm' -import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { V1PresentationPreview } from '../models/V1PresentationPreview' export interface V1ProposePresentationMessageOptions { diff --git a/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts b/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts index 7486a469f4..0232f336ed 100644 --- a/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts +++ b/packages/anoncreds/src/protocols/proofs/v1/messages/V1RequestPresentationMessage.ts @@ -1,13 +1,13 @@ import type { LegacyIndyProofRequest } from '../../../../formats' -import { Attachment, AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core' +import { V1Attachment, IsValidMessageType, parseMessageType, DidCommV1Message } from '@aries-framework/core' import { Expose, Type } from 'class-transformer' import { IsArray, IsString, ValidateNested, IsOptional, IsInstance } from 'class-validator' export interface V1RequestPresentationMessageOptions { id?: string comment?: string - requestAttachments: Attachment[] + requestAttachments: V1Attachment[] } export const INDY_PROOF_REQUEST_ATTACHMENT_ID = 'libindy-request-presentation-0' @@ -17,7 +17,7 @@ export const INDY_PROOF_REQUEST_ATTACHMENT_ID = 'libindy-request-presentation-0' * * @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0037-present-proof/README.md#request-presentation */ -export class V1RequestPresentationMessage extends AgentMessage { +export class V1RequestPresentationMessage extends DidCommV1Message { public readonly allowDidSovPrefix = true public constructor(options: V1RequestPresentationMessageOptions) { @@ -45,13 +45,13 @@ export class V1RequestPresentationMessage extends AgentMessage { * An array of attachments defining the acceptable formats for the presentation. */ @Expose({ name: 'request_presentations~attach' }) - @Type(() => Attachment) + @Type(() => V1Attachment) @IsArray() @ValidateNested({ each: true, }) - @IsInstance(Attachment, { each: true }) - public requestAttachments!: Attachment[] + @IsInstance(V1Attachment, { each: true }) + public requestAttachments!: V1Attachment[] public get indyProofRequest(): LegacyIndyProofRequest | null { const attachment = this.requestAttachments.find((attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID) @@ -59,7 +59,7 @@ export class V1RequestPresentationMessage extends AgentMessage { return attachment?.getDataAsJson() ?? null } - public getRequestAttachmentById(id: string): Attachment | undefined { + public getRequestAttachmentById(id: string): V1Attachment | undefined { return this.requestAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts index bcbde09c36..cff9842fb6 100644 --- a/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts +++ b/packages/anoncreds/src/updates/0.3.1-0.4/credentialDefinition.ts @@ -1,4 +1,3 @@ -import type { AnonCredsCredentialDefinition } from '../../models' import type { BaseAgent } from '@aries-framework/core' import { AriesFrameworkError } from '@aries-framework/core' @@ -66,7 +65,7 @@ export async function migrateAnonCredsCredentialDefinitionRecordToV0_4 { beforeEach(async () => { const agentConfig = getAgentConfig('AskarStorageServiceTest') - wallet = new AskarWallet(agentConfig.logger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + wallet = new AskarWallet(agentConfig.logger, new agentDependencies.FileSystem(), new KeyProviderRegistry([])) agentContext = getAgentContext({ wallet, agentConfig, diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 62aa371b5a..2ab8b9ef62 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -9,6 +9,7 @@ import type { WalletConfigRekey, KeyPair, WalletExportImportConfig, + WalletPackOptions, } from '@aries-framework/core' import type { KeyEntryObject, Session } from '@hyperledger/aries-askar-shared' @@ -28,21 +29,13 @@ import { WalletError, InjectionSymbols, Key, - SigningProviderRegistry, + KeyProviderRegistry, TypedArrayEncoder, FileSystem, WalletNotFoundError, KeyDerivationMethod, } from '@aries-framework/core' -import { - KdfMethod, - StoreKeyMethod, - KeyAlgs, - CryptoBox, - Store, - Key as AskarKey, - keyAlgFromString, -} from '@hyperledger/aries-askar-shared' +import { KeyAlgs, CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-shared' // eslint-disable-next-line import/order import BigNumber from 'bn.js' @@ -70,16 +63,16 @@ export class AskarWallet implements Wallet { private logger: Logger private fileSystem: FileSystem - private signingKeyProviderRegistry: SigningProviderRegistry + private keyProviderRegistry: KeyProviderRegistry public constructor( @inject(InjectionSymbols.Logger) logger: Logger, @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, - signingKeyProviderRegistry: SigningProviderRegistry + signingKeyProviderRegistry: KeyProviderRegistry ) { this.logger = logger this.fileSystem = fileSystem - this.signingKeyProviderRegistry = signingKeyProviderRegistry + this.keyProviderRegistry = signingKeyProviderRegistry } public get isProvisioned() { @@ -459,8 +452,8 @@ export class AskarWallet implements Wallet { } } else { // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) + if (this.keyProviderRegistry.hasProviderForKeyType(keyType)) { + const signingKeyProvider = this.keyProviderRegistry.getProviderForKeyType(keyType) const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) await this.storeKeyPair(keyPair) @@ -507,8 +500,8 @@ export class AskarWallet implements Wallet { return Buffer.from(signed) } else { // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + if (this.keyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.keyProviderRegistry.getProviderForKeyType(key.keyType) const keyPair = await this.retrieveKeyPair(key.publicKeyBase58) const signed = await signingKeyProvider.sign({ @@ -559,8 +552,8 @@ export class AskarWallet implements Wallet { return verified } else { // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + if (this.keyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.keyProviderRegistry.getProviderForKeyType(key.keyType) const signed = await signingKeyProvider.verify({ data, @@ -587,105 +580,106 @@ export class AskarWallet implements Wallet { * Pack a message using DIDComm V1 algorithm * * @param payload message to send - * @param recipientKeys array containing recipient keys in base58 - * @param senderVerkey sender key in base58 + * @param params packing options specific for envelop version * @returns JWE Envelope to send */ - public async pack( - payload: Record, - recipientKeys: string[], - senderVerkey?: string // in base58 - ): Promise { - let cek: AskarKey | undefined - let senderKey: KeyEntryObject | null | undefined - let senderExchangeKey: AskarKey | undefined + public async pack(payload: Record, params: WalletPackOptions): Promise { + if (params.version === 'v1') { + let cek: AskarKey | undefined + let senderKey: KeyEntryObject | null | undefined + let senderExchangeKey: AskarKey | undefined - try { - cek = AskarKey.generate(KeyAlgs.Chacha20C20P) + const { senderKey: senderVerkey, recipientKeys } = params - senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined - if (senderVerkey && !senderKey) { - throw new WalletError(`Unable to pack message. Sender key ${senderVerkey} not found in wallet.`) - } + try { + cek = AskarKey.generate(KeyAlgs.Chacha20C20P) - senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined + senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined + if (senderVerkey && !senderKey) { + throw new WalletError(`Unable to pack message. Sender key ${senderVerkey} not found in wallet.`) + } - const recipients: JweRecipient[] = [] + senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined - for (const recipientKey of recipientKeys) { - let targetExchangeKey: AskarKey | undefined - try { - targetExchangeKey = AskarKey.fromPublicBytes({ - publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, - algorithm: KeyAlgs.Ed25519, - }).convertkey({ algorithm: KeyAlgs.X25519 }) - - if (senderVerkey && senderExchangeKey) { - const encryptedSender = CryptoBox.seal({ - recipientKey: targetExchangeKey, - message: Buffer.from(senderVerkey), - }) - const nonce = CryptoBox.randomNonce() - const encryptedCek = CryptoBox.cryptoBox({ - recipientKey: targetExchangeKey, - senderKey: senderExchangeKey, - message: cek.secretBytes, - nonce, - }) + const recipients: JweRecipient[] = [] + + for (const recipientKey of recipientKeys) { + let targetExchangeKey: AskarKey | undefined + try { + targetExchangeKey = AskarKey.fromPublicBytes({ + publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, + algorithm: KeyAlgs.Ed25519, + }).convertkey({ algorithm: KeyAlgs.X25519 }) - recipients.push( - new JweRecipient({ - encryptedKey: encryptedCek, - header: { - kid: recipientKey, - sender: TypedArrayEncoder.toBase64URL(encryptedSender), - iv: TypedArrayEncoder.toBase64URL(nonce), - }, + if (senderVerkey && senderExchangeKey) { + const encryptedSender = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: Buffer.from(senderVerkey), }) - ) - } else { - const encryptedCek = CryptoBox.seal({ - recipientKey: targetExchangeKey, - message: cek.secretBytes, - }) - recipients.push( - new JweRecipient({ - encryptedKey: encryptedCek, - header: { - kid: recipientKey, - }, + const nonce = CryptoBox.randomNonce() + const encryptedCek = CryptoBox.cryptoBox({ + recipientKey: targetExchangeKey, + senderKey: senderExchangeKey, + message: cek.secretBytes, + nonce, }) - ) + + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + sender: TypedArrayEncoder.toBase64URL(encryptedSender), + iv: TypedArrayEncoder.toBase64URL(nonce), + }, + }) + ) + } else { + const encryptedCek = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: cek.secretBytes, + }) + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKey, + }, + }) + ) + } + } finally { + targetExchangeKey?.handle.free() } - } finally { - targetExchangeKey?.handle.free() } - } - const protectedJson = { - enc: 'xchacha20poly1305_ietf', - typ: 'JWM/1.0', - alg: senderVerkey ? 'Authcrypt' : 'Anoncrypt', - recipients: recipients.map((item) => JsonTransformer.toJSON(item)), - } + const protectedJson = { + enc: 'xchacha20poly1305_ietf', + typ: 'JWM/1.0', + alg: senderVerkey ? 'Authcrypt' : 'Anoncrypt', + recipients: recipients.map((item) => JsonTransformer.toJSON(item)), + } - const { ciphertext, tag, nonce } = cek.aeadEncrypt({ - message: Buffer.from(JSON.stringify(payload)), - aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), - }).parts + const { ciphertext, tag, nonce } = cek.aeadEncrypt({ + message: Buffer.from(JSON.stringify(payload)), + aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), + }).parts - const envelope = new JweEnvelope({ - ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), - iv: TypedArrayEncoder.toBase64URL(nonce), - protected: JsonEncoder.toBase64URL(protectedJson), - tag: TypedArrayEncoder.toBase64URL(tag), - }).toJson() + const envelope = new JweEnvelope({ + ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), + iv: TypedArrayEncoder.toBase64URL(nonce), + protected: JsonEncoder.toBase64URL(protectedJson), + tag: TypedArrayEncoder.toBase64URL(tag), + }).toJson() - return envelope as EncryptedMessage - } finally { - cek?.handle.free() - senderKey?.key.handle.free() - senderExchangeKey?.handle.free() + return envelope as EncryptedMessage + } finally { + cek?.handle.free() + senderKey?.key.handle.free() + senderExchangeKey?.handle.free() + } + } else { + throw new WalletError(`DIDComm V2 message packing is not supported`) } } @@ -834,7 +828,7 @@ export class AskarWallet implements Wallet { } } - private async retrieveKeyPair(publicKeyBase58: string): Promise { + public async retrieveKeyPair(publicKeyBase58: string): Promise { try { const entryObject = await this.session.fetch({ category: 'KeyPairRecord', name: `key-${publicKeyBase58}` }) diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index efeccd9fcd..43ec06759c 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -1,5 +1,5 @@ import type { - SigningProvider, + KeyProvider, WalletConfig, CreateKeyPairOptions, KeyPair, @@ -15,7 +15,7 @@ import { WalletNotFoundError, WalletInvalidKeyError, KeyType, - SigningProviderRegistry, + KeyProviderRegistry, TypedArrayEncoder, KeyDerivationMethod, Buffer, @@ -44,7 +44,7 @@ describeRunInNodeVersion([18], 'AskarWallet basic operations', () => { const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { - askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new KeyProviderRegistry([])) await askarWallet.createAndOpen(walletConfig) }) @@ -140,7 +140,7 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { const seed = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') - class DummySigningProvider implements SigningProvider { + class DummySigningProvider implements KeyProvider { public keyType: KeyType = KeyType.Bls12381g1g2 public async createKeyPair(options: CreateKeyPairOptions): Promise { @@ -166,7 +166,7 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { askarWallet = new AskarWallet( testLogger, new agentDependencies.FileSystem(), - new SigningProviderRegistry([new DummySigningProvider()]) + new KeyProviderRegistry([new DummySigningProvider()]) ) await askarWallet.createAndOpen(walletConfig) }) @@ -222,7 +222,7 @@ describeRunInNodeVersion([18], 'AskarWallet management', () => { }) test('Create', async () => { - askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new KeyProviderRegistry([])) const initialKey = Store.generateRawKey() const anotherKey = Store.generateRawKey() @@ -238,7 +238,7 @@ describeRunInNodeVersion([18], 'AskarWallet management', () => { }) test('Open', async () => { - askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new KeyProviderRegistry([])) const initialKey = Store.generateRawKey() const wrongKey = Store.generateRawKey() @@ -259,7 +259,7 @@ describeRunInNodeVersion([18], 'AskarWallet management', () => { }) test('Rotate key', async () => { - askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new KeyProviderRegistry([])) const initialKey = Store.generateRawKey() await askarWallet.createAndOpen({ ...walletConfig, id: 'AskarWallet Key Rotation', key: initialKey }) diff --git a/packages/askar/src/wallet/__tests__/packing.test.ts b/packages/askar/src/wallet/__tests__/packing.test.ts index 5dcb8c1b58..215faff9ca 100644 --- a/packages/askar/src/wallet/__tests__/packing.test.ts +++ b/packages/askar/src/wallet/__tests__/packing.test.ts @@ -1,12 +1,6 @@ -import type { WalletConfig } from '@aries-framework/core' +import type { WalletConfig, WalletPackOptions } from '@aries-framework/core' -import { - JsonTransformer, - BasicMessage, - KeyType, - SigningProviderRegistry, - KeyDerivationMethod, -} from '@aries-framework/core' +import { JsonTransformer, BasicMessage, KeyType, KeyProviderRegistry, KeyDerivationMethod } from '@aries-framework/core' import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { agentDependencies } from '../../../../core/tests/helpers' @@ -25,7 +19,7 @@ describeRunInNodeVersion([18], 'askarWallet packing', () => { let askarWallet: AskarWallet beforeEach(async () => { - askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new SigningProviderRegistry([])) + askarWallet = new AskarWallet(testLogger, new agentDependencies.FileSystem(), new KeyProviderRegistry([])) await askarWallet.createAndOpen(walletConfig) }) @@ -40,11 +34,13 @@ describeRunInNodeVersion([18], 'askarWallet packing', () => { const message = new BasicMessage({ content: 'hello' }) - const encryptedMessage = await askarWallet.pack( - message.toJSON(), - [recipientKey.publicKeyBase58], - senderKey.publicKeyBase58 - ) + const params: WalletPackOptions = { + version: 'v1', + recipientKeys: [recipientKey.publicKeyBase58], + senderKey: senderKey.publicKeyBase58, + } + + const encryptedMessage = await askarWallet.pack(message.toJSON(), params) const plainTextMessage = await askarWallet.unpack(encryptedMessage) diff --git a/packages/askar/tests/askar-postgres.e2e.test.ts b/packages/askar/tests/askar-postgres.e2e.test.ts index c25f6d16c3..e7577a85cd 100644 --- a/packages/askar/tests/askar-postgres.e2e.test.ts +++ b/packages/askar/tests/askar-postgres.e2e.test.ts @@ -74,7 +74,7 @@ describeRunInNodeVersion([18], 'Askar Postgres agents', () => { }) const { connectionRecord: bobConnectionAtBobAlice } = await bobAgent.oob.receiveInvitation( - aliceBobOutOfBandRecord.outOfBandInvitation + aliceBobOutOfBandRecord.getOutOfBandInvitation() ) bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice!.id) diff --git a/packages/bbs-signatures/src/Bls12381g2KeyProvider.ts b/packages/bbs-signatures/src/Bls12381g2KeyProvider.ts index 4f360ce566..3de02ed426 100644 --- a/packages/bbs-signatures/src/Bls12381g2KeyProvider.ts +++ b/packages/bbs-signatures/src/Bls12381g2KeyProvider.ts @@ -17,7 +17,7 @@ export class Bls12381g2KeyProvider implements KeyProvider { */ public async createKeyPair({ seed, privateKey }: CreateKeyPairOptions): Promise { if (privateKey) { - throw new SigningProviderError('Cannot create keypair from private key') + throw new KeyProviderError('Cannot create keypair from private key') } const blsKeyPair = await generateBls12381G2KeyPair(seed) diff --git a/packages/bbs-signatures/tests/bbs-key-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-key-provider.e2e.test.ts index 1c901c82d4..734e2e4ca5 100644 --- a/packages/bbs-signatures/tests/bbs-key-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-key-provider.e2e.test.ts @@ -30,7 +30,7 @@ describeSkipNode17And18('BBS Signing Provider', () => { const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { - wallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([new Bls12381g2SigningProvider()])) + wallet = new IndySdkWallet(indySdk, testLogger, new KeyProviderRegistry([new Bls12381g2KeyProvider()])) await wallet.createAndOpen(walletConfig) }) diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index 8a78628d7c..9673547add 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -27,7 +27,7 @@ import { customDocumentLoader } from '../../core/src/modules/vc/__tests__/docume import { getAgentConfig, getAgentContext } from '../../core/tests/helpers' import { IndySdkWallet } from '../../indy-sdk/src' import { indySdk } from '../../indy-sdk/tests/setupIndySdkModule' -import { BbsBlsSignature2020, BbsBlsSignatureProof2020, Bls12381g2SigningProvider } from '../src' +import { BbsBlsSignature2020, BbsBlsSignatureProof2020, Bls12381g2KeyProvider } from '../src' import { BbsBlsSignature2020Fixtures } from './fixtures' import { describeSkipNode17And18 } from './util' @@ -68,7 +68,7 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { - wallet = new IndySdkWallet(indySdk, agentConfig.logger, signingProviderRegistry) + wallet = new IndySdkWallet(indySdk, agentConfig.logger, keyProviderRegistry) await wallet.createAndOpen(agentConfig.walletConfig) agentContext = getAgentContext({ agentConfig, diff --git a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts index 2bf12aeb95..d47463ba7e 100644 --- a/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts +++ b/packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts @@ -1,7 +1,7 @@ import type { CheqdDidCreateOptions } from '../src' import type { DidDocument } from '@aries-framework/core' -import { Agent, TypedArrayEncoder, JsonTransformer } from '@aries-framework/core' +import { Agent, TypedArrayEncoder } from '@aries-framework/core' import { generateKeyPairFromSeed } from '@stablelib/ed25519' import { getAgentOptions } from '../../core/tests/helpers' diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index c62b261959..8f4929f298 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -14,9 +14,6 @@ import { InjectionSymbols } from '../constants' import { KeyProviderToken } from '../crypto' import { JwsService } from '../crypto/JwsService' import { X25519KeyProvider } from '../crypto/key-provider/X25519KeyProvider' -import { DidCommV1EnvelopeServiceToken } from '../didcomm/versions/v1' -import { DefaultDidCommV1EnvelopeService } from '../didcomm/versions/v1/indy/DefaultDidCommV1EnvelopeService' -import { DefaultDidCommV2EnvelopeService, DidCommV2EnvelopeServiceToken } from '../didcomm/versions/v2' import { AriesFrameworkError } from '../error' import { DependencyManager } from '../plugins' import { DidCommMessageRepository, StorageUpdateService, StorageVersionRepository } from '../storage' @@ -65,9 +62,6 @@ export class Agent extends BaseAge dependencyManager.registerSingleton(StorageVersionRepository) dependencyManager.registerSingleton(StorageUpdateService) - dependencyManager.registerSingleton(DidCommV1EnvelopeServiceToken, DefaultDidCommV1EnvelopeService) - dependencyManager.registerInstance(DidCommV2EnvelopeServiceToken, DefaultDidCommV2EnvelopeService) - dependencyManager.registerInstance(KeyProviderToken, new X25519KeyProvider()) dependencyManager.registerInstance(AgentConfig, agentConfig) diff --git a/packages/core/src/agent/AgentMessage.ts b/packages/core/src/agent/AgentMessage.ts index c7293c7ea0..e60a556141 100644 --- a/packages/core/src/agent/AgentMessage.ts +++ b/packages/core/src/agent/AgentMessage.ts @@ -1,3 +1,4 @@ +import type { ServiceDecorator } from '../decorators/service/ServiceDecorator' import type { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' import type { DidCommMessageVersion } from '../didcomm/types' @@ -8,9 +9,12 @@ export interface AgentMessage { get id(): string get threadId(): string | undefined + // setServiceDecorator(): ServiceDecorator | undefined + serviceDecorator(): ServiceDecorator | undefined + hasAnyReturnRoute(): boolean hasReturnRouting(threadId?: string): boolean setReturnRouting(type: ReturnRouteTypes, thread?: string): void - toJSON(params?: { useLegacyDidSovPrefix?: boolean }): Record + toJSON(params?: { useDidSovPrefixWhereAllowed?: boolean }): Record } diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index 6f77ca9065..fb15215956 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -1,88 +1,121 @@ -import type { DecryptedMessageContext, EncryptedMessage, SignedMessage } from '../didcomm/types' -import type { DidCommV1Message, PackMessageParams as DidCommV1PackMessageParams } from '../didcomm/versions/v1' -import type { - DidCommV2EnvelopeService, - DidCommV2Message, - V2PackMessageParams as DidCommV2PackMessageParams, -} from '../didcomm/versions/v2' import type { AgentMessage } from './AgentMessage' import type { AgentContext } from './context' +import type { DecryptedMessageContext, EncryptedMessage } from '../didcomm/types' +import type { PackMessageParams as DidCommV1PackMessageParams } from '../didcomm/versions/v1' +import type { V2PackMessageParams as DidCommV2PackMessageParams } from '../didcomm/versions/v2' +import type { WalletPackV1Options, WalletPackV2Options } from '../wallet/Wallet' -import { isEncryptedMessage, isSignedMessage } from '../didcomm' +import { InjectionSymbols } from '../constants' +import { Key, KeyType } from '../crypto' import { DidCommMessageVersion } from '../didcomm/types' -import { DidCommV1EnvelopeService, DidCommV1EnvelopeServiceToken } from '../didcomm/versions/v1' -import { isDidCommV1EncryptedEnvelope } from '../didcomm/versions/v1/helpers' -import { DefaultDidCommV2EnvelopeService, DidCommV2EnvelopeServiceToken } from '../didcomm/versions/v2' import { AriesFrameworkError } from '../error' +import { Logger } from '../logger' +import { ForwardMessage } from '../modules/routing/messages' import { inject, injectable } from '../plugins' export type PackMessageParams = DidCommV1PackMessageParams | DidCommV2PackMessageParams @injectable() export class EnvelopeService { - private didCommV1EnvelopeService: DidCommV1EnvelopeService - private didCommV2EnvelopeService: DidCommV2EnvelopeService | typeof DefaultDidCommV2EnvelopeService + private logger: Logger - public constructor( - @inject(DidCommV1EnvelopeServiceToken) didCommV1EnvelopeService: DidCommV1EnvelopeService, - @inject(DidCommV2EnvelopeServiceToken) - didCommV2EnvelopeService: DidCommV2EnvelopeService | typeof DefaultDidCommV2EnvelopeService - ) { - this.didCommV1EnvelopeService = didCommV1EnvelopeService - this.didCommV2EnvelopeService = didCommV2EnvelopeService - } - - private getDidCommV2EnvelopeService(): DidCommV2EnvelopeService { - if (this.didCommV2EnvelopeService === DefaultDidCommV2EnvelopeService) { - throw new AriesFrameworkError('Unable to resolve DidCommV2EnvelopeService') - } - return this.didCommV2EnvelopeService + public constructor(@inject(InjectionSymbols.Logger) logger: Logger) { + this.logger = logger } public async packMessage( agentContext: AgentContext, message: AgentMessage, - params: PackMessageParams + params: DidCommV1PackMessageParams | DidCommV2PackMessageParams ): Promise { + // pass whether we want to use legacy did sov prefix + const unboundMessage = message.toJSON({ + useDidSovPrefixWhereAllowed: agentContext.config.useDidSovPrefixWhereAllowed, + }) + if (message.didCommVersion === DidCommMessageVersion.V1) { - return this.didCommV1EnvelopeService.packMessage( - agentContext, - message as DidCommV1Message, - params as DidCommV1PackMessageParams - ) + const { recipientKeys, routingKeys, senderKey } = params as DidCommV1PackMessageParams + let recipientKeysBase58 = recipientKeys.map((key) => key.publicKeyBase58) + const routingKeysBase58 = routingKeys.map((key) => key.publicKeyBase58) + const senderKeyBase58 = senderKey && senderKey.publicKeyBase58 + + this.logger.debug(`Pack outbound message ${unboundMessage['@type']}`) + + // Forward messages are anon packed + const packParams: WalletPackV1Options = { + version: 'v1', + senderKey: senderKeyBase58 ?? null, + recipientKeys: recipientKeysBase58, + } + let encryptedMessage = await agentContext.wallet.pack(unboundMessage, packParams) + + // If the message has routing keys (mediator) pack for each mediator + for (const routingKey of routingKeysBase58) { + const forwardMessage = new ForwardMessage({ + // Forward to first recipient key + to: recipientKeysBase58[0], + message: encryptedMessage, + }) + recipientKeysBase58 = [routingKey] + this.logger.debug('Forward message created', forwardMessage) + + const forwardJson = forwardMessage.toJSON({ + useDidSovPrefixWhereAllowed: agentContext.config.useDidSovPrefixWhereAllowed, + }) + + // Forward messages are anon packed + const forwardParams: WalletPackV1Options = { + version: 'v1', + senderKey: null, + recipientKeys: recipientKeysBase58, + } + encryptedMessage = await agentContext.wallet.pack(forwardJson, forwardParams) + } + + return encryptedMessage } if (message.didCommVersion === DidCommMessageVersion.V2) { - return this.getDidCommV2EnvelopeService().packMessage( - agentContext, - message as DidCommV2Message, - params as DidCommV2PackMessageParams - ) + const { fromDid, toDid } = params as DidCommV2PackMessageParams + const packParams: WalletPackV2Options = { + version: 'v2', + fromDid, + toDid, + } + return await agentContext.wallet.pack(unboundMessage, packParams) } throw new AriesFrameworkError(`Unexpected pack DIDComm message params: ${params}`) } public async unpackMessage( agentContext: AgentContext, - message: EncryptedMessage | SignedMessage + encryptedMessage: EncryptedMessage ): Promise { - if (isEncryptedMessage(message)) { - return this.unpackJwe(agentContext, message) - } - if (isSignedMessage(message)) { - return this.unpackJws(agentContext, message) + const decryptedMessage = await agentContext.wallet.unpack(encryptedMessage) + const { recipientKey, senderKey, plaintextMessage } = decryptedMessage + return { + recipientKey: recipientKey ? Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519) : undefined, + senderKey: senderKey ? Key.fromPublicKeyBase58(senderKey, KeyType.Ed25519) : undefined, + plaintextMessage, } - throw new AriesFrameworkError(`Unexpected message!`) - } - private async unpackJwe(agentContext: AgentContext, message: EncryptedMessage): Promise { - if (isDidCommV1EncryptedEnvelope(message)) { - return this.didCommV1EnvelopeService.unpackMessage(agentContext, message) - } else { - return this.getDidCommV2EnvelopeService().unpackMessage(agentContext, message) - } + // if (isEncryptedMessage(message)) { + // return this.unpackJwe(agentContext, message) + // } + // if (isSignedMessage(message)) { + // return this.unpackJws(agentContext, message) + // } + // throw new AriesFrameworkError(`Unexpected message!`) } - private async unpackJws(agentContext: AgentContext, message: SignedMessage): Promise { - return this.getDidCommV2EnvelopeService().unpackMessage(agentContext, message) - } + // private async unpackJwe(agentContext: AgentContext, message: EncryptedMessage): Promise { + // if (isDidCommV1EncryptedEnvelope(message)) { + // return this.didCommV1EnvelopeService.unpackMessage(agentContext, message) + // } else { + // return this.getDidCommV2EnvelopeService().unpackMessage(agentContext, message) + // } + // } + // + // private async unpackJws(agentContext: AgentContext, message: SignedMessage): Promise { + // return this.getDidCommV2EnvelopeService().unpackMessage(agentContext, message) + // } } diff --git a/packages/core/src/agent/MessageHandler.ts b/packages/core/src/agent/MessageHandler.ts index 191a775ac1..30b5347ae0 100644 --- a/packages/core/src/agent/MessageHandler.ts +++ b/packages/core/src/agent/MessageHandler.ts @@ -1,5 +1,5 @@ -import type { ConstructableDidCommMessage } from '../didcomm' import type { InboundMessageContext, OutboundMessageContext } from './models' +import type { ConstructableDidCommMessage } from '../didcomm' export interface MessageHandler { readonly supportedMessages: readonly ConstructableDidCommMessage[] diff --git a/packages/core/src/agent/MessageHandlerRegistry.ts b/packages/core/src/agent/MessageHandlerRegistry.ts index b8348e0782..f050d4ea9e 100644 --- a/packages/core/src/agent/MessageHandlerRegistry.ts +++ b/packages/core/src/agent/MessageHandlerRegistry.ts @@ -1,5 +1,5 @@ -import type { ConstructableDidCommMessage } from '../didcomm' import type { MessageHandler } from './MessageHandler' +import type { ConstructableDidCommMessage } from '../didcomm' import { injectable } from 'tsyringe' diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 4973f39bba..7bae2af42c 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -1,10 +1,8 @@ -import type { EncryptedMessage, PlaintextMessage, SignedMessage } from '../didcomm' -import type { DecryptedMessageContext } from '../didcomm/types' -import type { ConnectionRecord } from '../modules/connections' -import type { InboundTransport } from '../transport' import type { AgentMessage } from './AgentMessage' import type { TransportSession } from './TransportService' import type { AgentContext } from './context' +import type { EncryptedMessage, PlaintextMessage, SignedMessage } from '../didcomm' +import type { DecryptedMessageContext } from '../didcomm/types' import type { ConnectionRecord } from '../modules/connections' import type { InboundTransport } from '../transport' @@ -141,8 +139,9 @@ export class MessageReceiver { packedMessage: SignedMessage, session?: TransportSession ) { - const unpackedMessage = await this.envelopeService.unpackMessage(agentContext, packedMessage) - return this.processUnpackedMessage(agentContext, unpackedMessage, session) + // FIXME + // const unpackedMessage = await this.envelopeService.unpackMessage(agentContext, packedMessage) + // return this.processUnpackedMessage(agentContext, undefined, session) } private async processUnpackedMessage( @@ -293,35 +292,4 @@ export class MessageReceiver { } return messageTransformed } - - /** - * Send the problem report message (https://didcomm.org/notification/1.0/problem-report) to the recipient. - * @param message error message to send - * @param connection connection to send the message to - * @param plaintextMessage received inbound message - */ - private async sendProblemReportMessage( - agentContext: AgentContext, - message: string, - connection: ConnectionRecord, - plaintextMessage: PlaintextMessage - ) { - const messageType = parseMessageType(plaintextMessage['@type']) - if (canHandleMessageType(ProblemReportMessage, messageType)) { - throw new AriesFrameworkError(`Not sending problem report in response to problem report: ${message}`) - } - const problemReportMessage = new ProblemReportMessage({ - description: { - en: message, - code: ProblemReportReason.MessageParseFailure, - }, - }) - problemReportMessage.setThread({ - parentThreadId: plaintextMessage['@id'], - }) - const outboundMessageContext = new OutboundMessageContext(problemReportMessage, { agentContext, connection }) - if (outboundMessageContext) { - await this.messageSender.sendMessage(outboundMessageContext) - } - } } diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 9fb7d9ec7b..1f8a051aa8 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -1,16 +1,16 @@ +import type { AgentMessage } from './AgentMessage' +import type { AgentMessageSentEvent } from './Events' +import type { TransportSession } from './TransportService' +import type { AgentContext } from './context' import type { EncryptedMessage, OutboundPackage, OutboundPackagePayload } from '../didcomm/types' -import type { DidCommV1Message } from '../didcomm/versions/v1' +import type { DidCommV1Message, PackMessageParams as DidCommV1PackMessageParams } from '../didcomm/versions/v1' +import type { V2PackMessageParams as DidCommV2PackMessageParams } from '../didcomm/versions/v2' import type { DidCommV2Message } from '../didcomm/versions/v2/DidCommV2Message' import type { ConnectionRecord } from '../modules/connections' import type { ResolvedDidCommService } from '../modules/didcomm' import type { DidCommV2Service, DidDocument, DidDocumentService } from '../modules/dids' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundTransport } from '../transport/OutboundTransport' -import type { AgentMessage } from './AgentMessage' -import type { PackMessageParams } from './EnvelopeService' -import type { AgentMessageSentEvent } from './Events' -import type { TransportSession } from './TransportService' -import type { AgentContext } from './context' import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' @@ -20,7 +20,6 @@ import { isDidCommV2Message } from '../didcomm/versions/v2' import { AriesFrameworkError, MessageSendingError } from '../error' import { Logger } from '../logger' import { DidCommDocumentService } from '../modules/didcomm/services/DidCommDocumentService' -import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type' import { getKeyFromVerificationMethod } from '../modules/dids/domain/key-type' import { didKeyToInstanceOfKey } from '../modules/dids/helpers' import { DidResolverService } from '../modules/dids/services/DidResolverService' @@ -86,16 +85,16 @@ export class MessageSender { public async packMessage( agentContext: AgentContext, { - keys, + params, message, endpoint, }: { - keys: PackMessageParams + params: DidCommV1PackMessageParams message: AgentMessage endpoint: string } ): Promise { - const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, keys) + const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, params) return { payload: encryptedMessage, @@ -201,23 +200,24 @@ export class MessageSender { } ) { if (isDidCommV1Message(outboundMessage.message)) { - return this.sendDIDCommV1Message(outboundMessage.message, outboundMessage, options) + return this.sendDIDCommV1Message(outboundMessage, options) } if (isDidCommV2Message(outboundMessage.message)) { - return this.sendDIDCommV2Message(outboundMessage.message, outboundMessage, options) + return this.sendDIDCommV2Message(outboundMessage, options) } throw new AriesFrameworkError(`Unexpected case`) } private async sendDIDCommV1Message( - message: DidCommV1Message, outboundMessageContext: OutboundMessageContext, options?: { envelopeType?: EnvelopeType transportPriority?: TransportPriorityOptions } ) { - const { agentContext, connection, outOfBand, message } = outboundMessageContext + const { agentContext, connection, outOfBand } = outboundMessageContext + const message = outboundMessageContext.message as DidCommV1Message + const errors: Error[] = [] if (!connection) { @@ -338,13 +338,13 @@ export class MessageSender { if (queueService) { this.logger.debug(`Queue message for connection ${connection.id} (${connection.theirLabel})`) - const keys = { + const params: DidCommV1PackMessageParams = { recipientKeys: queueService.recipientKeys, routingKeys: queueService.routingKeys, senderKey: firstOurAuthenticationKey, } - const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, keys) + const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, params) await this.messageRepository.add(connection.id, encryptedMessage) this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.QueuedForPickup) @@ -418,7 +418,7 @@ export class MessageSender { service: { ...service, recipientKeys: 'omitted...', routingKeys: 'omitted...' }, }) - const keys = { + const params: DidCommV1PackMessageParams = { recipientKeys: service.recipientKeys, routingKeys: service.routingKeys, senderKey, @@ -443,7 +443,7 @@ export class MessageSender { throw error } - const outboundPackage = await this.packMessage(agentContext, { message, keys, endpoint: service.serviceEndpoint }) + const outboundPackage = await this.packMessage(agentContext, { message, params, endpoint: service.serviceEndpoint }) outboundPackage.endpoint = service.serviceEndpoint outboundPackage.connectionId = connection?.id for (const transport of this.outboundTransports) { @@ -498,7 +498,8 @@ export class MessageSender { } else if (outOfBand) { this.logger.debug(`Resolving services from out-of-band record ${outOfBand.id}.`) if (connection.isRequester) { - for (const service of outOfBand.outOfBandInvitation.getServices()) { + const services = outOfBand.outOfBandInvitation?.getServices() || [] + for (const service of services) { // Resolve dids to DIDDocs to retrieve services if (typeof service === 'string') { this.logger.debug(`Resolving services for did ${service}.`) @@ -556,7 +557,6 @@ export class MessageSender { } private async sendDIDCommV2Message( - message: DidCommV2Message, outboundMessageContext: OutboundMessageContext, options?: { envelopeType?: EnvelopeType @@ -564,6 +564,7 @@ export class MessageSender { } ) { const { agentContext } = outboundMessageContext + const message = outboundMessageContext.message as DidCommV2Message const envelopeType = options?.envelopeType || EnvelopeType.Encrypted // recipient is not specified -> send to defaultTransport @@ -662,7 +663,11 @@ export class MessageSender { if (!message.from) { throw new AriesFrameworkError(`Unable to send message signed. Message doesn't contain sender DID.`) } - const params = { signByDID: message.from, serviceId: service?.id, type: EnvelopeType.Signed } + const params: DidCommV2PackMessageParams = { + signByDid: message.from, + serviceId: service?.id, + envelopeType: EnvelopeType.Signed, + } return this.envelopeService.packMessage(agentContext, message, params) } @@ -698,7 +703,7 @@ export class MessageSender { throw new AriesFrameworkError(`Unable to send message encrypted. Message doesn't contain recipient DID.`) } - const params: PackMessageParams = { + const params: DidCommV2PackMessageParams = { toDid: recipientDid, fromDid: message.from, signByDid: undefined, diff --git a/packages/core/src/agent/TransportService.ts b/packages/core/src/agent/TransportService.ts index e81ab680bb..6ea20d4aaa 100644 --- a/packages/core/src/agent/TransportService.ts +++ b/packages/core/src/agent/TransportService.ts @@ -1,11 +1,7 @@ -import type { EncryptedMessage } from '../didcomm' -import type { ConnectionRecord } from '../modules/connections/repository' -import type { DidDocument } from '../modules/dids' import type { AgentMessage } from './AgentMessage' import type { PackMessageParams } from './EnvelopeService' -import type { EnvelopeKeys } from './EnvelopeService' +import type { EncryptedMessage } from '../didcomm' import type { DidDocument } from '../modules/dids' -import type { EncryptedMessage } from '../types' import { DID_COMM_TRANSPORT_QUEUE } from '../constants' import { AriesFrameworkError } from '../error' diff --git a/packages/core/src/agent/__tests__/DidCommV1Message.test.ts b/packages/core/src/agent/__tests__/DidCommV1Message.test.ts index c2c526d848..1eeaee2bd7 100644 --- a/packages/core/src/agent/__tests__/DidCommV1Message.test.ts +++ b/packages/core/src/agent/__tests__/DidCommV1Message.test.ts @@ -10,7 +10,7 @@ class CustomProtocolMessage extends DidCommV1Message { public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/message') } -class LegacyDidSovPrefixMessage extends AgentMessage { +class LegacyDidSovPrefixMessage extends DidCommV1Message { public readonly allowDidSovPrefix = true @IsValidMessageType(LegacyDidSovPrefixMessage.type) @@ -18,7 +18,7 @@ class LegacyDidSovPrefixMessage extends AgentMessage { public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/another-message') } -describe('AgentMessage', () => { +describe('DIDCommV1Message', () => { describe('toJSON', () => { it('should only use did:sov message prefix if useDidSovPrefixWhereAllowed and allowDidSovPrefix are both true', () => { const message = new TestMessage() diff --git a/packages/core/src/agent/__tests__/Dispatcher.test.ts b/packages/core/src/agent/__tests__/Dispatcher.test.ts index d5eca48d72..3bd968440c 100644 --- a/packages/core/src/agent/__tests__/Dispatcher.test.ts +++ b/packages/core/src/agent/__tests__/Dispatcher.test.ts @@ -1,7 +1,7 @@ import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext } from '../../../tests/helpers' -import { DidCommV1Message, DidCommV2Message } from '../../didcomm' +import { DidCommV1Message } from '../../didcomm' import { parseMessageType } from '../../utils/messageType' import { Dispatcher } from '../Dispatcher' import { EventEmitter } from '../EventEmitter' @@ -9,16 +9,9 @@ import { MessageHandlerRegistry } from '../MessageHandlerRegistry' import { MessageSender } from '../MessageSender' import { InboundMessageContext } from '../models/InboundMessageContext' -const testMessageType = `https://didcomm.org/fake-protocol/1.5/message` - -class V1CustomProtocolMessage extends DidCommV1Message { - public readonly type = V1CustomProtocolMessage.type.messageTypeUri - public static readonly type = parseMessageType(testMessageType) -} - -class V2CustomProtocolMessage extends DidCommV2Message { - public readonly type = V2CustomProtocolMessage.type.messageTypeUri - public static readonly type = parseMessageType(testMessageType) +class CustomProtocolMessage extends DidCommV1Message { + public readonly type = CustomProtocolMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/fake-protocol/1.5/message') } describe('Dispatcher', () => { @@ -36,12 +29,11 @@ describe('Dispatcher', () => { messageHandlerRegistry, agentConfig.logger ) - const customProtocolMessage = new V2CustomProtocolMessage() + const customProtocolMessage = new CustomProtocolMessage() const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) const mockHandle = jest.fn() messageHandlerRegistry.registerMessageHandler({ supportedMessages: [CustomProtocolMessage], handle: mockHandle }) - dispatcher.registerMessageHandler({ supportedMessages: [V2CustomProtocolMessage], handle: mockHandle }) await dispatcher.dispatch(inboundMessageContext) @@ -56,14 +48,14 @@ describe('Dispatcher', () => { new MessageHandlerRegistry(), agentConfig.logger ) - const customProtocolMessage = new V2CustomProtocolMessage() + const customProtocolMessage = new CustomProtocolMessage() const inboundMessageContext = new InboundMessageContext(customProtocolMessage, { agentContext }) const mockHandle = jest.fn() messageHandlerRegistry.registerMessageHandler({ supportedMessages: [], handle: mockHandle }) await expect(dispatcher.dispatch(inboundMessageContext)).rejects.toThrow( - 'No handler for message type "https://didcomm.org/fake-protocol/1.5/message" found' + 'No handler for message type "https://didcomm.org/fake-protocol/1.5/message" found' ) }) }) diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 963153a7e8..4bf4ba595d 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -6,6 +6,7 @@ import type { DidDocumentService } from '../../modules/dids' import type { MessageRepository } from '../../storage/MessageRepository' import type { OutboundTransport } from '../../transport' import type { AgentMessageSentEvent } from '../Events' +import type { PackMessageParams } from '@aries-framework/core' import { Subject } from 'rxjs' @@ -659,12 +660,12 @@ describe('MessageSender', () => { const message = new TestMessage() const endpoint = 'https://example.com' - const keys = { + const params: PackMessageParams = { recipientKeys: [recipientKey], routingKeys: [], senderKey: senderKey, } - const result = await messageSender.packMessage(agentContext, { message, keys, endpoint }) + const result = await messageSender.packMessage(agentContext, { message, params, endpoint }) expect(result).toEqual({ payload: encryptedMessage, diff --git a/packages/core/src/agent/__tests__/stubs.ts b/packages/core/src/agent/__tests__/stubs.ts index a55c6e1b3e..b7f0d6ee6a 100644 --- a/packages/core/src/agent/__tests__/stubs.ts +++ b/packages/core/src/agent/__tests__/stubs.ts @@ -1,11 +1,12 @@ +import type { PackMessageParams as DidCommV1PackMessageParams } from '../../didcomm/versions/v1' +import type { V2PackMessageParams as DidCommV2PackMessageParams } from '../../didcomm/versions/v2' import type { AgentMessage } from '../AgentMessage' -import type { PackMessageParams } from '../EnvelopeService' import type { TransportSession } from '../TransportService' export class DummyTransportSession implements TransportSession { public id: string public readonly type = 'http' - public keys?: PackMessageParams + public keys?: DidCommV1PackMessageParams | DidCommV2PackMessageParams public inboundMessage?: AgentMessage public connectionId?: string diff --git a/packages/core/src/crypto/key-provider/KeyProviderRegistry.ts b/packages/core/src/crypto/key-provider/KeyProviderRegistry.ts index c817ddfc57..5612c734e3 100644 --- a/packages/core/src/crypto/key-provider/KeyProviderRegistry.ts +++ b/packages/core/src/crypto/key-provider/KeyProviderRegistry.ts @@ -1,5 +1,5 @@ -import type { KeyType } from '..' import type { KeyProvider } from './KeyProvider' +import type { KeyType } from '..' import { AriesFrameworkError } from '../../error' import { injectable, injectAll } from '../../plugins' diff --git a/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts b/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts deleted file mode 100644 index db71348ef6..0000000000 --- a/packages/core/src/crypto/signing-provider/SigningProviderRegistry.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { SigningProvider } from './SigningProvider' -import type { KeyType } from '../KeyType' - -import { AriesFrameworkError } from '../../error' -import { injectable, injectAll } from '../../plugins' - -export const SigningProviderToken = Symbol('SigningProviderToken') - -@injectable() -export class SigningProviderRegistry { - private signingKeyProviders: SigningProvider[] - - public constructor(@injectAll(SigningProviderToken) signingKeyProviders: Array<'default' | SigningProvider>) { - // This is a really ugly hack to make tsyringe work without any SigningProviders registered - // It is currently impossible to use @injectAll if there are no instances registered for the - // token. We register a value of `default` by default and will filter that out in the registry. - // Once we have a signing provider that should always be registered we can remove this. We can make an ed25519 - // signer using the @stablelib/ed25519 library. - this.signingKeyProviders = signingKeyProviders.filter((provider) => provider !== 'default') as SigningProvider[] - } - - public hasProviderForKeyType(keyType: KeyType): boolean { - const signingKeyProvider = this.signingKeyProviders.find((x) => x.keyType === keyType) - - return signingKeyProvider !== undefined - } - - public getProviderForKeyType(keyType: KeyType): SigningProvider { - const signingKeyProvider = this.signingKeyProviders.find((x) => x.keyType === keyType) - - if (!signingKeyProvider) { - throw new AriesFrameworkError(`No signing key provider for key type: ${keyType}`) - } - - return signingKeyProvider - } -} diff --git a/packages/core/src/decorators/service/ServiceDecoratorExtension.ts b/packages/core/src/decorators/service/ServiceDecoratorExtension.ts index 51bed49fd6..b05ba52a8c 100644 --- a/packages/core/src/decorators/service/ServiceDecoratorExtension.ts +++ b/packages/core/src/decorators/service/ServiceDecoratorExtension.ts @@ -1,5 +1,5 @@ -import type { DidComV1BaseMessageConstructor } from '../../didcomm/' import type { ServiceDecoratorOptions } from './ServiceDecorator' +import type { DidComV1BaseMessageConstructor } from '../../didcomm/' import { Expose, Type } from 'class-transformer' import { IsOptional, ValidateNested } from 'class-validator' diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index 71bc99dbf1..322c0014a0 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -4,7 +4,7 @@ import { IndySdkWallet } from '../../../../indy-sdk/src' import { indySdk } from '../../../../indy-sdk/tests/setupIndySdkModule' import { getAgentConfig } from '../../../tests/helpers' import { KeyType } from '../../crypto' -import { KeyProviderRegistry } from '../../crypto/signing-provider' +import { KeyProviderRegistry } from '../../crypto/key-provider' import { TypedArrayEncoder } from '../../utils' import { SignatureDecorator } from './SignatureDecorator' @@ -47,7 +47,7 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { beforeAll(async () => { const config = getAgentConfig('SignatureDecoratorUtilsTest') - wallet = new IndySdkWallet(config.agentDependencies, config.logger, new KeyProviderRegistry([])) + wallet = new IndySdkWallet(indySdk, config.logger, new KeyProviderRegistry([])) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(config.walletConfig!) }) diff --git a/packages/core/src/didcomm/index.ts b/packages/core/src/didcomm/index.ts index d6a9daab29..ed7273c784 100644 --- a/packages/core/src/didcomm/index.ts +++ b/packages/core/src/didcomm/index.ts @@ -1,7 +1,7 @@ -import type { ParsedMessageType } from '../utils/messageType' -import type { Constructor } from '../utils/mixins' import type { DidCommV1Message } from './versions/v1' import type { DidCommV2Message } from './versions/v2' +import type { ParsedMessageType } from '../utils/messageType' +import type { Constructor } from '../utils/mixins' export * from './versions/v1' export * from './versions/v2' diff --git a/packages/core/src/didcomm/types.ts b/packages/core/src/didcomm/types.ts index 1d16214791..86a874b095 100644 --- a/packages/core/src/didcomm/types.ts +++ b/packages/core/src/didcomm/types.ts @@ -1,6 +1,6 @@ -import type { Key } from '../crypto' import type { PlaintextDidCommV1Message } from './versions/v1' import type { PlaintextDidCommV2Message } from './versions/v2' +import type { Key } from '../crypto' export enum EnvelopeType { Plain = 'plain', diff --git a/packages/core/src/didcomm/versions/v1/DidCommV1Message.ts b/packages/core/src/didcomm/versions/v1/DidCommV1Message.ts index aa8f2a7e51..b49470517c 100644 --- a/packages/core/src/didcomm/versions/v1/DidCommV1Message.ts +++ b/packages/core/src/didcomm/versions/v1/DidCommV1Message.ts @@ -1,6 +1,8 @@ import type { AgentMessage } from '../../../agent/AgentMessage' import type { ServiceDecorator } from '../../../decorators/service/ServiceDecorator' +import { Exclude } from 'class-transformer' + import { AckDecorated } from '../../../decorators/ack/AckDecoratorExtension' import { V1AttachmentDecorated } from '../../../decorators/attachment/V1AttachmentExtension' import { L10nDecorated } from '../../../decorators/l10n/L10nDecoratorExtension' @@ -21,6 +23,15 @@ const Decorated = ThreadDecorated( ) export class DidCommV1Message extends Decorated implements AgentMessage { + /** + * Whether the protocol RFC was initially written using the legacy did:prefix instead of the + * new https://didcomm.org message type prefix. + * + * @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0348-transition-msg-type-to-https/README.md + */ + @Exclude() + public readonly allowDidSovPrefix: boolean = false + public get didCommVersion(): DidCommMessageVersion { return DidCommMessageVersion.V1 } @@ -29,10 +40,16 @@ export class DidCommV1Message extends Decorated implements AgentMessage { return this.service } - public toJSON({ useLegacyDidSovPrefix = false }: { useLegacyDidSovPrefix?: boolean } = {}): Record { + public toJSON({ useDidSovPrefixWhereAllowed = false }: { useDidSovPrefixWhereAllowed?: boolean } = {}): Record< + string, + unknown + > { const json = JsonTransformer.toJSON(this) - if (useLegacyDidSovPrefix) { + // If we have `useDidSovPrefixWhereAllowed` enabled, we want to replace the new https://didcomm.org prefix with the legacy did:sov prefix. + // However, we only do this if the protocol RFC was initially written with the did:sov message type prefix + // See https://github.com/hyperledger/aries-rfcs/blob/main/features/0348-transition-msg-type-to-https/README.md + if (this.allowDidSovPrefix && useDidSovPrefixWhereAllowed) { replaceNewDidCommPrefixWithLegacyDidSovOnMessage(json) } diff --git a/packages/core/src/didcomm/versions/v1/helpers.ts b/packages/core/src/didcomm/versions/v1/helpers.ts index 3335d4b74d..8750820154 100644 --- a/packages/core/src/didcomm/versions/v1/helpers.ts +++ b/packages/core/src/didcomm/versions/v1/helpers.ts @@ -1,7 +1,7 @@ -import type { AgentMessage } from '../../../agent/AgentMessage' -import type { EncryptedMessage, ProtectedMessage } from '../../types' import type { DidCommV1Message } from './DidCommV1Message' import type { PlaintextDidCommV1Message } from './types' +import type { AgentMessage } from '../../../agent/AgentMessage' +import type { EncryptedMessage, ProtectedMessage } from '../../types' import { AriesFrameworkError } from '../../../error' import { JsonEncoder } from '../../../utils' diff --git a/packages/core/src/didcomm/versions/v1/index.ts b/packages/core/src/didcomm/versions/v1/index.ts index e1283f963d..766336bc99 100644 --- a/packages/core/src/didcomm/versions/v1/index.ts +++ b/packages/core/src/didcomm/versions/v1/index.ts @@ -1,7 +1,5 @@ -import type { AgentContext } from '../../../agent/context' import type { Key } from '../../../crypto' -import type { DecryptedMessageContext, EncryptedMessage, SignedMessage, EnvelopeType } from '../../types' -import type { DidCommV1Message } from './DidCommV1Message' +import type { EnvelopeType } from '../../types' export { DidCommV1Message } from './DidCommV1Message' export { DidCommV1BaseMessage, DidComV1BaseMessageConstructor } from './DidCommV1BaseMessage' @@ -13,13 +11,6 @@ export interface PackMessageParams { envelopeType?: EnvelopeType } -export const DidCommV1EnvelopeServiceToken = Symbol('DidCommV1EnvelopeService') - -export interface DidCommV1EnvelopeService { - packMessage(agentContext: AgentContext, payload: DidCommV1Message, keys: PackMessageParams): Promise - - unpackMessage(agentContext: AgentContext, message: EncryptedMessage | SignedMessage): Promise -} export { isPlaintextMessageV1 } from './helpers' export { isDidCommV1Message } from './helpers' export { DidCommV1Algorithms } from './types' diff --git a/packages/core/src/didcomm/versions/v1/indy/DefaultDidCommV1EnvelopeService.ts b/packages/core/src/didcomm/versions/v1/indy/DefaultDidCommV1EnvelopeService.ts deleted file mode 100644 index 9c4b4dd8b9..0000000000 --- a/packages/core/src/didcomm/versions/v1/indy/DefaultDidCommV1EnvelopeService.ts +++ /dev/null @@ -1,78 +0,0 @@ -import type { AgentContext } from '../../../../agent/context' -import type { DecryptedMessageContext, EncryptedMessage, SignedMessage } from '../../../types' -import type { DidCommV1Message } from '../DidCommV1Message' -import type { PackMessageParams } from '../index' - -import { InjectionSymbols } from '../../../../constants' -import { Key, KeyType } from '../../../../crypto' -import { AriesFrameworkError } from '../../../../error/AriesFrameworkError' -import { Logger } from '../../../../logger' -import { ForwardMessage } from '../../../../modules/routing/messages' -import { inject, injectable } from '../../../../plugins' -import { DidCommMessageVersion, EnvelopeType } from '../../../types' -import { DidCommV1EnvelopeService } from '../index' - -@injectable() -export class DefaultDidCommV1EnvelopeService implements DidCommV1EnvelopeService { - private logger: Logger - - public constructor(@inject(InjectionSymbols.Logger) logger: Logger) { - this.logger = logger - } - - public async packMessage( - agentContext: AgentContext, - payload: DidCommV1Message, - params: PackMessageParams - ): Promise { - if (params.envelopeType === EnvelopeType.Signed) { - throw new AriesFrameworkError('JWS messages are not supported by DIDComm V1 service') - } - - const { recipientKeys, routingKeys, senderKey } = params - let recipientKeysBase58 = recipientKeys.map((key) => key.publicKeyBase58) - const routingKeysBase58 = routingKeys.map((key) => key.publicKeyBase58) - const senderKeyBase58 = senderKey && senderKey.publicKeyBase58 - - // pass whether we want to use legacy did sov prefix - const message = payload.toJSON({ useLegacyDidSovPrefix: agentContext.config.useLegacyDidSovPrefix }) - - this.logger.debug(`Pack outbound message ${message['@type']}`) - - let encryptedMessage = await agentContext.wallet.pack(message, recipientKeysBase58, senderKeyBase58 ?? undefined) - - // If the message has routing keys (mediator) pack for each mediator - for (const routingKeyBase58 of routingKeysBase58) { - const forwardMessage = new ForwardMessage({ - // Forward to first recipient key - to: recipientKeysBase58[0], - message: encryptedMessage, - }) - recipientKeysBase58 = [routingKeyBase58] - this.logger.debug('Forward message created', forwardMessage) - - const forwardJson = forwardMessage.toJSON({ useLegacyDidSovPrefix: agentContext.config.useLegacyDidSovPrefix }) - - // Forward messages are anon packed - encryptedMessage = await agentContext.wallet.pack(forwardJson, [routingKeyBase58], undefined) - } - - return encryptedMessage - } - - public async unpackMessage( - agentContext: AgentContext, - encryptedMessage: EncryptedMessage | SignedMessage - ): Promise { - const decryptedMessage = await agentContext.wallet.unpack(encryptedMessage as EncryptedMessage) - const { recipientKey, senderKey, plaintextMessage } = decryptedMessage - return { - recipientKey: recipientKey ? Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519) : undefined, - senderKey: senderKey ? Key.fromPublicKeyBase58(senderKey, KeyType.Ed25519) : undefined, - plaintextMessage, - didCommVersion: DidCommMessageVersion.V1, - } - } -} - -export { DidCommV1EnvelopeService } diff --git a/packages/core/src/didcomm/versions/v2/DidCommV2Message.ts b/packages/core/src/didcomm/versions/v2/DidCommV2Message.ts index aed80cd0d7..929a664931 100644 --- a/packages/core/src/didcomm/versions/v2/DidCommV2Message.ts +++ b/packages/core/src/didcomm/versions/v2/DidCommV2Message.ts @@ -1,4 +1,5 @@ import type { AgentMessage } from '../../../agent/AgentMessage' +import type { ServiceDecorator } from '../../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../../error' import { JsonTransformer } from '../../../utils/JsonTransformer' @@ -11,6 +12,10 @@ export class DidCommV2Message extends DidCommV2BaseMessage implements AgentMessa return JsonTransformer.toJSON(this) } + public serviceDecorator(): ServiceDecorator | undefined { + return undefined + } + public get didCommVersion(): DidCommMessageVersion { return DidCommMessageVersion.V2 } diff --git a/packages/core/src/didcomm/versions/v2/helpers.ts b/packages/core/src/didcomm/versions/v2/helpers.ts index 79294a87dd..1ae3c3282e 100644 --- a/packages/core/src/didcomm/versions/v2/helpers.ts +++ b/packages/core/src/didcomm/versions/v2/helpers.ts @@ -1,6 +1,6 @@ -import type { AgentMessage } from '../../../agent/AgentMessage' import type { DidCommV2Message } from './DidCommV2Message' import type { PlaintextDidCommV2Message } from './types' +import type { AgentMessage } from '../../../agent/AgentMessage' import { DidCommMessageVersion } from '../../types' diff --git a/packages/core/src/didcomm/versions/v2/index.ts b/packages/core/src/didcomm/versions/v2/index.ts index eb4314eebc..16cf7d1359 100644 --- a/packages/core/src/didcomm/versions/v2/index.ts +++ b/packages/core/src/didcomm/versions/v2/index.ts @@ -1,6 +1,4 @@ -import type { AgentContext } from '../../../agent/context/AgentContext' -import type { DecryptedMessageContext, EncryptedMessage, SignedMessage, EnvelopeType } from '../../types' -import type { DidCommV2Message } from './DidCommV2Message' +import type { EnvelopeType } from '../../types' export { DidCommV2Message } from './DidCommV2Message' export { DidCommV2BaseMessage, DidComV2BaseMessageConstructor, DidCommV2MessageParams } from './DidCommV2BaseMessage' @@ -14,18 +12,6 @@ export interface V2PackMessageParams { envelopeType?: EnvelopeType } -export const DidCommV2EnvelopeServiceToken = Symbol('DidCommV2EnvelopeService') -export const DefaultDidCommV2EnvelopeService = 'default' - -export interface DidCommV2EnvelopeService { - packMessage( - agentContext: AgentContext, - payload: DidCommV2Message, - params: V2PackMessageParams - ): Promise - - unpackMessage(agentContext: AgentContext, message: EncryptedMessage | SignedMessage): Promise -} export { isPlaintextMessageV2 } from './helpers' export { isDidCommV2Message } from './helpers' export { PlaintextDidCommV2Message } from './types' diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index fbb9a5c5be..1562100f31 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -16,11 +16,8 @@ export { AgentMessage } from './agent/AgentMessage' export { Dispatcher } from './agent/Dispatcher' export { MessageSender } from './agent/MessageSender' export type { AgentDependencies } from './agent/AgentDependencies' -export type { InitConfig, WalletConfig, JsonArray, JsonObject, JsonValue } from './types' export type { InitConfig, - OutboundPackage, - EncryptedMessage, WalletConfig, JsonArray, JsonObject, diff --git a/packages/core/src/modules/connections/ConnectionEvents.ts b/packages/core/src/modules/connections/ConnectionEvents.ts index 472710f996..c9f1064bab 100644 --- a/packages/core/src/modules/connections/ConnectionEvents.ts +++ b/packages/core/src/modules/connections/ConnectionEvents.ts @@ -13,22 +13,3 @@ export interface ConnectionStateChangedEvent extends BaseEvent { previousState: DidExchangeState | null } } - -export enum TrustPingEventTypes { - PingReceived = 'PingReceived', - PingResponseReceived = 'PingResponseReceived', -} - -export interface PingReceivedEvent extends BaseEvent { - type: typeof TrustPingEventTypes.PingReceived - payload: { - from: string - } -} - -export interface PingResponseReceivedEvent extends BaseEvent { - type: typeof TrustPingEventTypes.PingResponseReceived - payload: { - from: string - } -} diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index 1d4b3eb4cc..ca6581fc16 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -27,7 +27,15 @@ import { DidExchangeResponseHandler, } from './handlers' import { HandshakeProtocol } from './models' +import { + TrustPingMessageHandler as V1TrustPingMessageHandler, + TrustPingResponseMessageHandler as V1TrustPingResponseMessageHandler, +} from './protocols/trust-ping/v1' import { V1TrustPingService } from './protocols/trust-ping/v1/V1TrustPingService' +import { + TrustPingMessageHandler as V2TrustPingMessageHandler, + TrustPingResponseMessageHandler as V2TrustPingResponseMessageHandler, +} from './protocols/trust-ping/v2' import { V2TrustPingService } from './protocols/trust-ping/v2/V2TrustPingService' import { ConnectionService } from './services/ConnectionService' @@ -285,19 +293,6 @@ export class ConnectionsApi { return record } - /** - * Send Ping message to remote party - */ - public async sendPing(connectionId: string) { - const connection = await this.getById(connectionId) - const message = connection.isDidCommV1Connection - ? await this.v1trustPingService.createPing() - : await this.v2TrustPingService.createPing(connection) - - await this.messageSender.sendMessage( - new OutboundMessageContext(message, { agentContext: this.agentContext, connection }) - ) - } /** * Send a trust ping to an established connection * @@ -306,32 +301,41 @@ export class ConnectionsApi { * @param withReturnRouting do we want a response at the time of posting * @returns TurstPingMessage */ - // public async sendPing( - // connectionId: string, - // { responseRequested = true, withReturnRouting = undefined }: SendPingOptions - // ) { - // const connection = await this.getById(connectionId) - // - // const { message } = await this.connectionService.createTrustPing(this.agentContext, connection, { - // responseRequested: responseRequested, - // }) - // - // if (withReturnRouting === true) { - // message.setReturnRouting(ReturnRouteTypes.all) - // } - // - // // Disable return routing as we don't want to receive a response for this message over the same channel - // // This has led to long timeouts as not all clients actually close an http socket if there is no response message - // if (withReturnRouting === false) { - // message.setReturnRouting(ReturnRouteTypes.none) - // } - // - // await this.messageSender.sendMessage( - // new OutboundMessageContext(message, { agentContext: this.agentContext, connection }) - // ) - // - // return message - // } + public async sendPing( + connectionId: string, + { responseRequested = true, withReturnRouting = undefined }: SendPingOptions + ) { + const connection = await this.getById(connectionId) + + if (connection.isDidCommV1Connection) { + const { message } = await this.connectionService.createTrustPing(this.agentContext, connection, { + responseRequested: responseRequested, + }) + + if (withReturnRouting === true) { + message.setReturnRouting(ReturnRouteTypes.all) + } + + // Disable return routing as we don't want to receive a response for this message over the same channel + // This has led to long timeouts as not all clients actually close an http socket if there is no response message + if (withReturnRouting === false) { + message.setReturnRouting(ReturnRouteTypes.none) + } + + await this.messageSender.sendMessage( + new OutboundMessageContext(message, { agentContext: this.agentContext, connection }) + ) + + return message + } else { + const message = await this.v2TrustPingService.createPing(connection) + + await this.messageSender.sendMessage( + new OutboundMessageContext(message, { agentContext: this.agentContext, connection }) + ) + return message + } + } /** * Gets the known connection types for the record matching the given connectionId @@ -437,9 +441,11 @@ export class ConnectionsApi { ) messageHandlerRegistry.registerMessageHandler(new AckMessageHandler(this.connectionService)) messageHandlerRegistry.registerMessageHandler( - new TrustPingMessageHandler(this.trustPingService, this.connectionService) + new V1TrustPingMessageHandler(this.v1trustPingService, this.connectionService) ) - messageHandlerRegistry.registerMessageHandler(new TrustPingResponseMessageHandler(this.trustPingService)) + messageHandlerRegistry.registerMessageHandler(new V1TrustPingResponseMessageHandler(this.v1trustPingService)) + messageHandlerRegistry.registerMessageHandler(new V2TrustPingMessageHandler(this.v2TrustPingService)) + messageHandlerRegistry.registerMessageHandler(new V2TrustPingResponseMessageHandler(this.v2TrustPingService)) messageHandlerRegistry.registerMessageHandler( new DidExchangeRequestHandler( diff --git a/packages/core/src/modules/connections/DidExchangeProtocol.ts b/packages/core/src/modules/connections/DidExchangeProtocol.ts index 8aa560f0ef..e08f3b0878 100644 --- a/packages/core/src/modules/connections/DidExchangeProtocol.ts +++ b/packages/core/src/modules/connections/DidExchangeProtocol.ts @@ -110,7 +110,7 @@ export class DidExchangeProtocol { // Create message const label = params.label ?? agentContext.config.label const didDocument = await this.createPeerDidDoc(agentContext, this.routingToServices(routing)) - const parentThreadId = outOfBandRecord.outOfBandInvitation.id + const parentThreadId = outOfBandRecord.getOutOfBandInvitation().id const message = new DidExchangeRequestMessage({ label, parentThreadId, did: didDocument.id, goal, goalCode }) @@ -246,7 +246,7 @@ export class DidExchangeProtocol { if (routing) { services = this.routingToServices(routing) } else if (outOfBandRecord) { - const inlineServices = outOfBandRecord.outOfBandInvitation.getInlineServices() + const inlineServices = outOfBandRecord.getOutOfBandInvitation().getInlineServices() services = inlineServices.map((service) => ({ id: service.id, serviceEndpoint: service.serviceEndpoint, @@ -368,7 +368,7 @@ export class DidExchangeProtocol { DidExchangeStateMachine.assertCreateMessageState(DidExchangeCompleteMessage.type, connectionRecord) const threadId = connectionRecord.threadId - const parentThreadId = outOfBandRecord.outOfBandInvitation.id + const parentThreadId = outOfBandRecord.getOutOfBandInvitation().id if (!threadId) { throw new AriesFrameworkError(`Connection record ${connectionRecord.id} does not have 'threadId' attribute.`) @@ -412,7 +412,7 @@ export class DidExchangeProtocol { }) } const pthid = message.thread?.parentThreadId - if (!pthid || pthid !== outOfBandRecord.outOfBandInvitation.id) { + if (!pthid || pthid !== outOfBandRecord.getOutOfBandInvitation().id) { throw new DidExchangeProblemReportError('Invalid or missing parent thread ID referencing to the invitation.', { problemCode: DidExchangeProblemReportReason.CompleteRejected, }) diff --git a/packages/core/src/modules/connections/TrustPingEvents.ts b/packages/core/src/modules/connections/TrustPingEvents.ts deleted file mode 100644 index 2b0fcf66c9..0000000000 --- a/packages/core/src/modules/connections/TrustPingEvents.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { TrustPingMessage, TrustPingResponseMessage } from './messages' -import type { ConnectionRecord } from './repository/ConnectionRecord' -import type { BaseEvent } from '../../agent/Events' - -export enum TrustPingEventTypes { - TrustPingReceivedEvent = 'TrustPingReceivedEvent', - TrustPingResponseReceivedEvent = 'TrustPingResponseReceivedEvent', -} - -export interface TrustPingReceivedEvent extends BaseEvent { - type: typeof TrustPingEventTypes.TrustPingReceivedEvent - payload: { - connectionRecord: ConnectionRecord - message: TrustPingMessage - } -} - -export interface TrustPingResponseReceivedEvent extends BaseEvent { - type: typeof TrustPingEventTypes.TrustPingResponseReceivedEvent - payload: { - connectionRecord: ConnectionRecord - message: TrustPingResponseMessage - } -} diff --git a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts index ac298e03a9..05ee1c9c2f 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionService.test.ts @@ -16,8 +16,7 @@ import { } from '../../../../tests/helpers' import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import { Key, KeyType } from '../../../crypto' -import { KeyProviderRegistry } from '../../../crypto/key-provider' +import { Key, KeyProviderRegistry, KeyType } from '../../../crypto' import { signData, unpackAndVerifySignatureDecorator } from '../../../decorators/signature/SignatureDecoratorUtils' import { DidCommV1Message } from '../../../didcomm' import { JsonTransformer } from '../../../utils/JsonTransformer' @@ -83,7 +82,7 @@ describe('ConnectionService', () => { let agentContext: AgentContext beforeAll(async () => { - wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + wallet = new IndySdkWallet(indySdk, agentConfig.logger, new KeyProviderRegistry([])) agentContext = getAgentContext({ wallet, agentConfig }) await wallet.createAndOpen(agentConfig.walletConfig) }) diff --git a/packages/core/src/modules/connections/index.ts b/packages/core/src/modules/connections/index.ts index e9dd5862d9..afc1de2ee0 100644 --- a/packages/core/src/modules/connections/index.ts +++ b/packages/core/src/modules/connections/index.ts @@ -3,7 +3,7 @@ export * from './models' export * from './repository' export * from './services' export * from './ConnectionEvents' -export * from './TrustPingEvents' +export * from './protocols/trust-ping/TrustPingEvents' export * from './ConnectionsApi' export * from './DidExchangeProtocol' export * from './ConnectionsModuleConfig' diff --git a/packages/core/src/modules/connections/protocols/trust-ping/TrustPingEvents.ts b/packages/core/src/modules/connections/protocols/trust-ping/TrustPingEvents.ts new file mode 100644 index 0000000000..2430dcc382 --- /dev/null +++ b/packages/core/src/modules/connections/protocols/trust-ping/TrustPingEvents.ts @@ -0,0 +1,31 @@ +import type { + TrustPingMessage as V1TrustPingMessage, + TrustPingResponseMessage as V1TrustPingResponseMessage, +} from './v1' +import type { + TrustPingMessage as V2TrustPingMessage, + TrustPingResponseMessage as V2TrustPingResponseMessage, +} from './v2' +import type { BaseEvent } from '../../../../agent/Events' +import type { ConnectionRecord } from '../../repository/ConnectionRecord' + +export enum TrustPingEventTypes { + TrustPingReceivedEvent = 'TrustPingReceivedEvent', + TrustPingResponseReceivedEvent = 'TrustPingResponseReceivedEvent', +} + +export interface TrustPingReceivedEvent extends BaseEvent { + type: typeof TrustPingEventTypes.TrustPingReceivedEvent + payload: { + connectionRecord?: ConnectionRecord | null + message: V1TrustPingMessage | V2TrustPingMessage + } +} + +export interface TrustPingResponseReceivedEvent extends BaseEvent { + type: typeof TrustPingEventTypes.TrustPingResponseReceivedEvent + payload: { + connectionRecord?: ConnectionRecord | null + message: V1TrustPingResponseMessage | V2TrustPingResponseMessage + } +} diff --git a/packages/core/src/modules/connections/protocols/trust-ping/v1/V1TrustPingService.ts b/packages/core/src/modules/connections/protocols/trust-ping/v1/V1TrustPingService.ts index aef8ca6996..5668a8299a 100644 --- a/packages/core/src/modules/connections/protocols/trust-ping/v1/V1TrustPingService.ts +++ b/packages/core/src/modules/connections/protocols/trust-ping/v1/V1TrustPingService.ts @@ -1,59 +1,52 @@ +import type { TrustPingMessage } from './messages' import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { ConnectionRecord } from '../../../repository/ConnectionRecord' +import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../TrustPingEvents' -import { Dispatcher } from '../../../../../agent/Dispatcher' -import { InjectionSymbols } from '../../../../../constants' -import { Logger } from '../../../../../logger' -import { inject, injectable } from '../../../../../plugins' -import { ConnectionService } from '../../../services/ConnectionService' +import { EventEmitter } from '../../../../../agent/EventEmitter' +import { OutboundMessageContext } from '../../../../../agent/models' +import { injectable } from '../../../../../plugins' +import { TrustPingEventTypes } from '../TrustPingEvents' -import { TrustPingMessageHandler, TrustPingResponseMessageHandler } from './handlers' -import { TrustPingMessage } from './messages' -import { TrustPingResponseMessage } from './messages/TrustPingResponseMessage' +import { TrustPingResponseMessage } from './messages' @injectable() export class V1TrustPingService { - private logger: Logger - private dispatcher: Dispatcher - private connectionService: ConnectionService - - public constructor( - @inject(InjectionSymbols.Logger) logger: Logger, - dispatcher: Dispatcher, - connectionService: ConnectionService - ) { - this.logger = logger - this.dispatcher = dispatcher - this.connectionService = connectionService - - this.registerHandlers() + private eventEmitter: EventEmitter + + public constructor(eventEmitter: EventEmitter) { + this.eventEmitter = eventEmitter } - public createPing(): TrustPingMessage { - return new TrustPingMessage({ - responseRequested: true, + public processPing({ message, agentContext }: InboundMessageContext, connection: ConnectionRecord) { + this.eventEmitter.emit(agentContext, { + type: TrustPingEventTypes.TrustPingReceivedEvent, + payload: { + connectionRecord: connection, + message: message, + }, }) - } - public processPing({ message }: InboundMessageContext, connection: ConnectionRecord) { - this.logger.info(`Send Trust Ping message to connection ${connection.id}.`) if (message.responseRequested) { const response = new TrustPingResponseMessage({ - threadId: message.id, + threadId: message.threadId, }) - return response + return new OutboundMessageContext(response, { agentContext, connection }) } } - // eslint-disable-next-line @typescript-eslint/no-unused-vars public processPingResponse(inboundMessage: InboundMessageContext) { - this.logger.info('Trust Ping Response message received.') - // TODO: handle ping response message - } + const { agentContext, message } = inboundMessage - protected registerHandlers() { - this.dispatcher.registerMessageHandler(new TrustPingMessageHandler(this, this.connectionService)) - this.dispatcher.registerMessageHandler(new TrustPingResponseMessageHandler(this)) + const connection = inboundMessage.assertReadyConnection() + + this.eventEmitter.emit(agentContext, { + type: TrustPingEventTypes.TrustPingResponseReceivedEvent, + payload: { + connectionRecord: connection, + message: message, + }, + }) } } diff --git a/packages/core/src/modules/connections/protocols/trust-ping/v1/handlers/TrustPingMessageHandler.ts b/packages/core/src/modules/connections/protocols/trust-ping/v1/handlers/TrustPingMessageHandler.ts index 4e676823ff..7e55f8d684 100644 --- a/packages/core/src/modules/connections/protocols/trust-ping/v1/handlers/TrustPingMessageHandler.ts +++ b/packages/core/src/modules/connections/protocols/trust-ping/v1/handlers/TrustPingMessageHandler.ts @@ -2,7 +2,6 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../. import type { ConnectionService } from '../../../../services/ConnectionService' import type { V1TrustPingService } from '../V1TrustPingService' -import { OutboundMessageContext } from '../../../../../../agent/models' import { AriesFrameworkError } from '../../../../../../error' import { DidExchangeState } from '../../../../models' import { TrustPingMessage } from '../messages' @@ -29,12 +28,6 @@ export class TrustPingMessageHandler implements MessageHandler { await this.connectionService.updateState(messageContext.agentContext, connection, DidExchangeState.Completed) } - const message = await this.trustPingService.processPing(messageContext, connection) - if (message) { - return new OutboundMessageContext(message, { - agentContext: messageContext.agentContext, - connection, - }) - } + return this.trustPingService.processPing(messageContext, connection) } } diff --git a/packages/core/src/modules/connections/protocols/trust-ping/v2/V2TrustPingService.ts b/packages/core/src/modules/connections/protocols/trust-ping/v2/V2TrustPingService.ts index c4495bd11f..c5b149c6e0 100644 --- a/packages/core/src/modules/connections/protocols/trust-ping/v2/V2TrustPingService.ts +++ b/packages/core/src/modules/connections/protocols/trust-ping/v2/V2TrustPingService.ts @@ -1,15 +1,14 @@ import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import type { PingReceivedEvent, PingResponseReceivedEvent } from '../../../ConnectionEvents' import type { ConnectionRecord } from '../../../repository' +import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../TrustPingEvents' import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' import { InjectionSymbols } from '../../../../../constants' import { Logger } from '../../../../../logger' import { inject, injectable } from '../../../../../plugins' -import { TrustPingEventTypes } from '../../../ConnectionEvents' +import { TrustPingEventTypes } from '../TrustPingEvents' -import { TrustPingMessageHandler, TrustPingResponseMessageHandler } from './handlers' import { TrustPingMessage } from './messages/TrustPingMessage' import { TrustPingResponseMessage } from './messages/TrustPingResponseMessage' @@ -27,8 +26,6 @@ export class V2TrustPingService { this.logger = logger this.dispatcher = dispatcher this.eventEmitter = eventEmitter - - this.registerHandlers() } public createPing(connection: ConnectionRecord): TrustPingMessage { @@ -45,10 +42,10 @@ export class V2TrustPingService { public processPing({ agentContext, message }: InboundMessageContext) { this.logger.info('Trust Ping message received.', message) - this.eventEmitter.emit(agentContext, { - type: TrustPingEventTypes.PingReceived, + this.eventEmitter.emit(agentContext, { + type: TrustPingEventTypes.TrustPingReceivedEvent, payload: { - from: message.from, + message: message, }, }) @@ -65,16 +62,11 @@ export class V2TrustPingService { public processPingResponse({ agentContext, message }: InboundMessageContext) { this.logger.info('Trust Ping Response message received.', message) - this.eventEmitter.emit(agentContext, { - type: TrustPingEventTypes.PingResponseReceived, + this.eventEmitter.emit(agentContext, { + type: TrustPingEventTypes.TrustPingResponseReceivedEvent, payload: { - from: message.from, + message: message, }, }) } - - protected registerHandlers() { - this.dispatcher.registerMessageHandler(new TrustPingMessageHandler(this)) - this.dispatcher.registerMessageHandler(new TrustPingResponseMessageHandler(this)) - } } diff --git a/packages/core/src/modules/connections/services/ConnectionService.ts b/packages/core/src/modules/connections/services/ConnectionService.ts index e922d432f0..4f3e628fd0 100644 --- a/packages/core/src/modules/connections/services/ConnectionService.ts +++ b/packages/core/src/modules/connections/services/ConnectionService.ts @@ -98,7 +98,7 @@ export class ConnectionService { // TODO check there is no connection record for particular oob record - const { outOfBandInvitation } = outOfBandRecord + const outOfBandInvitation = outOfBandRecord.getOutOfBandInvitation() const { mediatorId } = config.routing const didDoc = this.createDidDoc(config.routing) @@ -122,7 +122,7 @@ export class ConnectionService { connectionRequest.setThread({ threadId: connectionRequest.threadId, - parentThreadId: outOfBandRecord.outOfBandInvitation.id, + parentThreadId: outOfBandInvitation.id, }) const connectionRecord = await this.createConnection(agentContext, { @@ -209,7 +209,7 @@ export class ConnectionService { connectionRecord.assertState(DidExchangeState.RequestReceived) connectionRecord.assertRole(DidExchangeRole.Responder) - const { outOfBandInvitation } = outOfBandRecord.getOutOfBandInvitation() + const outOfBandInvitation = outOfBandRecord.getOutOfBandInvitation() const didDoc = routing ? this.createDidDoc(routing) diff --git a/packages/core/src/modules/connections/services/TrustPingService.ts b/packages/core/src/modules/connections/services/TrustPingService.ts deleted file mode 100644 index 5236a2c83d..0000000000 --- a/packages/core/src/modules/connections/services/TrustPingService.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../TrustPingEvents' -import type { TrustPingMessage } from '../messages' -import type { ConnectionRecord } from '../repository/ConnectionRecord' - -import { EventEmitter } from '../../../agent/EventEmitter' -import { OutboundMessageContext } from '../../../agent/models' -import { injectable } from '../../../plugins' -import { TrustPingEventTypes } from '../TrustPingEvents' -import { TrustPingResponseMessage } from '../messages' - -@injectable() -export class TrustPingService { - private eventEmitter: EventEmitter - - public constructor(eventEmitter: EventEmitter) { - this.eventEmitter = eventEmitter - } - - public processPing({ message, agentContext }: InboundMessageContext, connection: ConnectionRecord) { - this.eventEmitter.emit(agentContext, { - type: TrustPingEventTypes.TrustPingReceivedEvent, - payload: { - connectionRecord: connection, - message: message, - }, - }) - - if (message.responseRequested) { - const response = new TrustPingResponseMessage({ - threadId: message.threadId, - }) - - return new OutboundMessageContext(response, { agentContext, connection }) - } - } - - public processPingResponse(inboundMessage: InboundMessageContext) { - const { agentContext, message } = inboundMessage - - const connection = inboundMessage.assertReadyConnection() - - this.eventEmitter.emit(agentContext, { - type: TrustPingEventTypes.TrustPingResponseReceivedEvent, - payload: { - connectionRecord: connection, - message: message, - }, - }) - } -} diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index a166a4f33f..2da13ceb69 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -1,7 +1,4 @@ import type { -import type { DidCommV1Message } from '../../didcomm/versions/v1' -import type { Query } from '../../storage/StorageService' -import type { CFsFromCPs, DeleteCredentialOptions } from './CredentialProtocolOptions' AcceptCredentialOptions, AcceptCredentialOfferOptions, AcceptCredentialProposalOptions, @@ -22,6 +19,7 @@ import type { CFsFromCPs, DeleteCredentialOptions } from './CredentialProtocolOp import type { CredentialProtocol } from './protocol/CredentialProtocol' import type { CredentialFormatsFromProtocols } from './protocol/CredentialProtocolOptions' import type { CredentialExchangeRecord } from './repository/CredentialExchangeRecord' +import type { DidCommV1Message } from '../../didcomm/versions/v1' import type { Query } from '../../storage/StorageService' import { AgentContext } from '../../agent' @@ -65,7 +63,7 @@ export interface CredentialsApi { acceptCredential(options: AcceptCredentialOptions): Promise // out of band - createOffer(options: CreateOfferOptions): Promise<{ + createOffer(options: CreateCredentialOfferOptions): Promise<{ message: DidCommV1Message credentialRecord: CredentialExchangeRecord }> diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts index 42b19594bf..ac1ffde0a9 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatService.ts @@ -1,5 +1,3 @@ -import type { AgentContext } from '../../../agent' -import type { V1Attachment } from '../../../decorators/attachment/V1Attachment' import type { CredentialFormat } from './CredentialFormat' import type { CredentialFormatCreateProposalOptions, diff --git a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts index 68d30f72d1..4be6646ffc 100644 --- a/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts +++ b/packages/core/src/modules/credentials/formats/CredentialFormatServiceOptions.ts @@ -1,6 +1,6 @@ -import type { V1Attachment } from '../../../decorators/attachment/V1Attachment' import type { CredentialFormat, CredentialFormatPayload } from './CredentialFormat' import type { CredentialFormatService } from './CredentialFormatService' +import type { V1Attachment } from '../../../decorators/attachment/V1Attachment' import type { CredentialFormatSpec } from '../models/CredentialFormatSpec' import type { CredentialPreviewAttributeOptions } from '../models/CredentialPreviewAttribute' import type { CredentialExchangeRecord } from '../repository/CredentialExchangeRecord' diff --git a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts index 68a280eab1..ace6416ee1 100644 --- a/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts +++ b/packages/core/src/modules/credentials/formats/jsonld/JsonLdCredentialFormatService.ts @@ -438,7 +438,7 @@ export class JsonLdCredentialFormatService implements CredentialFormatService - ): Promise> + ): Promise> public abstract processProposal( - messageContext: InboundMessageContext + messageContext: InboundMessageContext ): Promise public abstract acceptProposal( agentContext: AgentContext, options: AcceptCredentialProposalOptions - ): Promise> + ): Promise> public abstract negotiateProposal( agentContext: AgentContext, options: NegotiateCredentialProposalOptions - ): Promise> + ): Promise> // methods for offer public abstract createOffer( agentContext: AgentContext, options: CreateCredentialOfferOptions - ): Promise> - public abstract processOffer(messageContext: InboundMessageContext): Promise + ): Promise> + public abstract processOffer( + messageContext: InboundMessageContext + ): Promise public abstract acceptOffer( agentContext: AgentContext, options: AcceptCredentialOfferOptions - ): Promise> + ): Promise> public abstract negotiateOffer( agentContext: AgentContext, options: NegotiateCredentialOfferOptions - ): Promise> + ): Promise> // methods for request public abstract createRequest( agentContext: AgentContext, options: CreateCredentialRequestOptions - ): Promise> - public abstract processRequest(messageContext: InboundMessageContext): Promise + ): Promise> + public abstract processRequest( + messageContext: InboundMessageContext + ): Promise public abstract acceptRequest( agentContext: AgentContext, options: AcceptCredentialRequestOptions - ): Promise> + ): Promise> // methods for issue public abstract processCredential( - messageContext: InboundMessageContext + messageContext: InboundMessageContext ): Promise public abstract acceptCredential( agentContext: AgentContext, options: AcceptCredentialOptions - ): Promise> + ): Promise> // methods for ack - public abstract processAck(messageContext: InboundMessageContext): Promise + public abstract processAck(messageContext: InboundMessageContext): Promise // methods for problem-report public abstract createProblemReport( @@ -107,19 +111,19 @@ export abstract class BaseCredentialProtocol + ): Promise public abstract findOfferMessage( agentContext: AgentContext, credentialExchangeId: string - ): Promise + ): Promise public abstract findRequestMessage( agentContext: AgentContext, credentialExchangeId: string - ): Promise + ): Promise public abstract findCredentialMessage( agentContext: AgentContext, credentialExchangeId: string - ): Promise + ): Promise public abstract getFormatData( agentContext: AgentContext, credentialExchangeId: string diff --git a/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts b/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts index 52b05f861b..f547d9f3de 100644 --- a/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts +++ b/packages/core/src/modules/credentials/protocol/CredentialProtocol.ts @@ -32,7 +32,7 @@ export interface CredentialProtocol ): Promise> - processProposal(messageContext: InboundMessageContext): Promise + processProposal(messageContext: InboundMessageContext): Promise acceptProposal( agentContext: AgentContext, options: AcceptCredentialProposalOptions @@ -61,12 +61,12 @@ export interface CredentialProtocol - ): Promise> + ): Promise> processRequest(messageContext: InboundMessageContext): Promise acceptRequest( agentContext: AgentContext, options: AcceptCredentialRequestOptions - ): Promise> + ): Promise> // methods for issue processCredential(messageContext: InboundMessageContext): Promise diff --git a/packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts b/packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts index 146655267c..b8ba5b9761 100644 --- a/packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts +++ b/packages/core/src/modules/credentials/protocol/CredentialProtocolOptions.ts @@ -1,7 +1,6 @@ import type { CredentialProtocol } from './CredentialProtocol' -import type { DidCommV1Message } from '../../didcomm' -import type { FlatArray } from '../../types' -import type { ConnectionRecord } from '../connections/repository/ConnectionRecord' +import type { DidCommV1Message } from '../../../didcomm' +import type { ConnectionRecord } from '../../connections/repository/ConnectionRecord' import type { CredentialFormat, CredentialFormatPayload, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts index 54197b255b..57af30f5f9 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolCred.test.ts @@ -72,7 +72,6 @@ const connection = getMockConnection({ state: DidExchangeState.Completed, }) - const offerAttachment = new V1Attachment({ id: 'offer-attachment-id', mimeType: 'application/json', diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts index b36d7016a1..8e37ba3ea5 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/V2CredentialProtocolOffer.test.ts @@ -30,10 +30,10 @@ const offerFormat = new CredentialFormatSpec({ format: 'hlindy/cred-abstract@v2.0', }) -const offerAttachment = new Attachment({ +const offerAttachment = new V1Attachment({ id: 'offer-attachment-id', mimeType: 'application/json', - data: new AttachmentData({ + data: new V1AttachmentData({ base64: 'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', }), diff --git a/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts index 40d28fe238..2837b36f7b 100644 --- a/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts +++ b/packages/core/src/modules/credentials/protocol/v2/messages/V2ProposeCredentialMessage.ts @@ -18,7 +18,7 @@ export interface V2ProposeCredentialMessageOptions { } export class V2ProposeCredentialMessage extends DidCommV1Message { - public constructor(props: V2ProposeCredentialMessageOptions) { + public constructor(options: V2ProposeCredentialMessageOptions) { super() if (options) { this.id = options.id ?? this.generateId() diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index b0f2cff013..8b512f26eb 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -5,14 +5,10 @@ import { IndySdkModule } from '../../../../../indy-sdk/src' import { indySdk } from '../../../../tests' import { getAgentOptions } from '../../../../tests/helpers' import { Agent } from '../../../agent/Agent' -import { InjectionSymbols } from '../../../constants' import { KeyType } from '../../../crypto' import { JsonTransformer, TypedArrayEncoder } from '../../../utils' -import { indyDidFromPublicKeyBase58 } from '../../../utils/did' import { PeerDidNumAlgo } from '../methods/peer/didPeer' -import { JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' - const agentOptions = getAgentOptions( 'Faber Dids Registrar', {}, diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index f21de18f2c..3bf77d9d7f 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -11,9 +11,6 @@ import { EventEmitter } from '../../../agent/EventEmitter' import { InjectionSymbols } from '../../../constants' import { Key, KeyType } from '../../../crypto' import { KeyProviderRegistry } from '../../../crypto/key-provider' -import { IndyStorageService } from '../../../storage/IndyStorageService' -import { JsonTransformer } from '../../../utils' -import { IndyWallet } from '../../../wallet/IndyWallet' import { JsonTransformer, TypedArrayEncoder } from '../../../utils' import { DidsModuleConfig } from '../DidsModuleConfig' import { DidCommV1Service, DidDocument, DidDocumentBuilder } from '../domain' diff --git a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts index 151668d547..dfe4bd0f32 100644 --- a/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts +++ b/packages/core/src/modules/discover-features/services/DiscoverFeaturesService.ts @@ -30,13 +30,15 @@ export abstract class DiscoverFeaturesService { public abstract readonly version: string - public abstract createQuery(options: CreateQueryOptions): Promise> + public abstract createQuery( + options: CreateQueryOptions + ): Promise> public abstract processQuery( messageContext: InboundMessageContext - ): Promise | void> + ): Promise | void> public abstract createDisclosure( options: CreateDisclosureOptions ): Promise> - public abstract processDisclosure(messageContext: InboundMessageContext): Promise + public abstract processDisclosure(messageContext: InboundMessageContext): Promise } diff --git "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" index 1f8d54e264..6ed2074cd3 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/MessagePickupApiOptions.ts" @@ -1,5 +1,5 @@ import type { MessagePickupProtocol } from './protocol/MessagePickupProtocol' -import type { EncryptedMessage } from '../../types' +import type { EncryptedMessage } from '../../didcomm/types' /** * Get the supported protocol versions based on the provided discover features services. diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" index ebbd6fde39..eaa0d71bfd 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/BaseMessagePickupProtocol.ts" @@ -1,8 +1,8 @@ import type { MessagePickupProtocol } from './MessagePickupProtocol' import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from './MessagePickupProtocolOptions' import type { AgentContext } from '../../../agent' -import type { AgentMessage } from '../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' +import type { DidCommV1Message } from '../../../didcomm' import type { DependencyManager } from '../../../plugins' /** @@ -15,7 +15,7 @@ export abstract class BaseMessagePickupProtocol implements MessagePickupProtocol public abstract pickupMessages( agentContext: AgentContext, options: PickupMessagesProtocolOptions - ): Promise> + ): Promise> public abstract register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void } diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" index 9acdcf5e4d..469a27cac8 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocol.ts" @@ -1,7 +1,7 @@ import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from './MessagePickupProtocolOptions' import type { AgentContext } from '../../../agent' -import type { AgentMessage } from '../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' +import type { DidCommV1Message } from '../../../didcomm' import type { DependencyManager } from '../../../plugins' export interface MessagePickupProtocol { @@ -10,7 +10,7 @@ export interface MessagePickupProtocol { pickupMessages( agentContext: AgentContext, options: PickupMessagesProtocolOptions - ): Promise> + ): Promise> register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry): void } diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" index 9f3f252c6a..cf2034c356 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/MessagePickupProtocolOptions.ts" @@ -1,4 +1,4 @@ -import type { AgentMessage } from '../../../agent/AgentMessage' +import type { DidCommV1Message } from '../../../didcomm' import type { ConnectionRecord } from '../../connections' export interface PickupMessagesProtocolOptions { @@ -7,6 +7,6 @@ export interface PickupMessagesProtocolOptions { batchSize?: number } -export type PickupMessagesProtocolReturnType = { +export type PickupMessagesProtocolReturnType = { message: MessageType } diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" index 581d0d31a7..6271c7dceb 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/V1MessagePickupProtocol.ts" @@ -1,7 +1,7 @@ import type { AgentContext } from '../../../../agent' -import type { AgentMessage } from '../../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { DidCommV1Message } from '../../../../didcomm' import type { DependencyManager } from '../../../../plugins' import type { MessageRepository } from '../../../../storage/MessageRepository' import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from '../MessagePickupProtocolOptions' @@ -43,7 +43,7 @@ export class V1MessagePickupProtocol extends BaseMessagePickupProtocol { public async pickupMessages( agentContext: AgentContext, options: PickupMessagesProtocolOptions - ): Promise> { + ): Promise> { const { connectionRecord, batchSize } = options connectionRecord.assertReady() diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" index fbb3b369b8..02a1131e8f 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchMessage.ts" @@ -1,11 +1,11 @@ import { Type, Expose } from 'class-transformer' import { Matches, IsArray, ValidateNested, IsObject, IsInstance } from 'class-validator' -import { DidCommV1Message } from '../../../../../../didcomm' -import { EncryptedMessage } from '../../../../../../didcomm/types' -import { MessageIdRegExp } from '../../../../../../didcomm/validation' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' -import { uuid } from '../../../../../../utils/uuid' +import { DidCommV1Message } from '../../../../../didcomm' +import { EncryptedMessage } from '../../../../../didcomm/types' +import { MessageIdRegExp } from '../../../../../didcomm/validation' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' +import { uuid } from '../../../../../utils/uuid' export class BatchMessageMessage { public constructor(options: { id?: string; message: EncryptedMessage }) { diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" index 219896b4aa..0a846ea2dc 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v1/messages/V1BatchPickupMessage.ts" @@ -1,8 +1,8 @@ import { Expose } from 'class-transformer' import { IsInt } from 'class-validator' -import { DidCommV1Message } from '../../../../../../didcomm' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { DidCommV1Message } from '../../../../../didcomm' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export interface BatchPickupMessageOptions { id?: string diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" index b9dc22ae7e..4c581f96b3 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/V2MessagePickupProtocol.ts" @@ -1,11 +1,11 @@ import type { AgentContext } from '../../../../agent' -import type { AgentMessage } from '../../../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../../../agent/Events' import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { DidCommV1Message } from '../../../../didcomm' +import type { EncryptedMessage } from '../../../../didcomm/types' import type { DependencyManager } from '../../../../plugins' import type { MessageRepository } from '../../../../storage/MessageRepository' -import type { EncryptedMessage } from '../../../../types' import type { PickupMessagesProtocolOptions, PickupMessagesProtocolReturnType } from '../MessagePickupProtocolOptions' import { EventEmitter } from '../../../../agent/EventEmitter' @@ -13,7 +13,7 @@ import { AgentEventTypes } from '../../../../agent/Events' import { MessageSender } from '../../../../agent/MessageSender' import { OutboundMessageContext, Protocol } from '../../../../agent/models' import { InjectionSymbols } from '../../../../constants' -import { Attachment } from '../../../../decorators/attachment/Attachment' +import { V1Attachment } from '../../../../decorators/attachment/V1Attachment' import { AriesFrameworkError } from '../../../../error' import { injectable } from '../../../../plugins' import { ConnectionService } from '../../../connections' @@ -71,7 +71,7 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { public async pickupMessages( agentContext: AgentContext, options: PickupMessagesProtocolOptions - ): Promise> { + ): Promise> { const { connectionRecord, recipientKey } = options connectionRecord.assertReady() @@ -123,7 +123,7 @@ export class V2MessagePickupProtocol extends BaseMessagePickupProtocol { // of delivery message const attachments = messages.map( (msg) => - new Attachment({ + new V1Attachment({ data: { json: msg, }, diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" index 50476217f9..46fe53bfb9 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/__tests__/V2MessagePickupProtocol.test.ts" @@ -1,4 +1,4 @@ -import type { EncryptedMessage } from '../../../../../types' +import type { EncryptedMessage } from '../../../../../didcomm' import { getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' import { EventEmitter } from '../../../../../agent/EventEmitter' @@ -6,11 +6,12 @@ import { AgentEventTypes } from '../../../../../agent/Events' import { MessageSender } from '../../../../../agent/MessageSender' import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import { InjectionSymbols } from '../../../../../constants' -import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { V1Attachment } from '../../../../../decorators/attachment/V1Attachment' import { AriesFrameworkError } from '../../../../../error' import { InMemoryMessageRepository } from '../../../../../storage/InMemoryMessageRepository' import { uuid } from '../../../../../utils/uuid' -import { DidExchangeState, TrustPingMessage } from '../../../../connections' +import { DidExchangeState } from '../../../../connections' +import { TrustPingMessage } from '../../../../connections/protocols/trust-ping/v1/messages/TrustPingMessage' import { ConnectionService } from '../../../../connections/services/ConnectionService' import { MessagePickupModuleConfig } from '../../../MessagePickupModuleConfig' import { V1MessagePickupProtocol } from '../../v1' @@ -63,6 +64,7 @@ const encryptedMessage: EncryptedMessage = { iv: 'base64url', ciphertext: 'base64url', tag: 'base64url', + recipients: [], } const queuedMessages = [encryptedMessage, encryptedMessage, encryptedMessage] @@ -330,7 +332,7 @@ describe('V2MessagePickupService', () => { const messageDeliveryMessage = new V2MessageDeliveryMessage({ threadId: uuid(), attachments: [ - new Attachment({ + new V1Attachment({ id: '1', data: { json: { @@ -362,7 +364,7 @@ describe('V2MessagePickupService', () => { const messageDeliveryMessage = new V2MessageDeliveryMessage({ threadId: uuid(), attachments: [ - new Attachment({ + new V1Attachment({ id: '1', data: { json: { @@ -370,7 +372,7 @@ describe('V2MessagePickupService', () => { }, }, }), - new Attachment({ + new V1Attachment({ id: '2', data: { json: { diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" index 94cf34698f..bf2e1e4194 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2DeliveryRequestMessage.ts" @@ -2,8 +2,8 @@ import { Expose } from 'class-transformer' import { IsInt, IsOptional, IsString } from 'class-validator' import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' -import { DidCommV1Message } from '../../../../../../didcomm' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { DidCommV1Message } from '../../../../../didcomm' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export interface V2DeliveryRequestMessageOptions { id?: string diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" index e9548fa07f..4cb803fae2 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessageDeliveryMessage.ts" @@ -1,11 +1,11 @@ -import type { V1Attachment } from '../../../../../../decorators/attachment/V1Attachment' +import type { V1Attachment } from '../../../../../decorators/attachment/V1Attachment' import { Expose } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' -import { DidCommV1Message } from '../../../../../../didcomm' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { DidCommV1Message } from '../../../../../didcomm' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export interface V2MessageDeliveryMessageOptions { id?: string diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" index ef430ae69d..849aeeb7b1 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2MessagesReceivedMessage.ts" @@ -2,8 +2,8 @@ import { Expose } from 'class-transformer' import { IsArray, IsOptional } from 'class-validator' import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' -import { DidCommV1Message } from '../../../../../../didcomm' -import { IsValidMessageType, parseMessageType } from '../../../../../../utils/messageType' +import { DidCommV1Message } from '../../../../../didcomm' +import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export interface V2MessagesReceivedMessageOptions { id?: string diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" index f1ac704881..f9df0b4090 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusMessage.ts" @@ -1,8 +1,8 @@ import { Expose, Transform } from 'class-transformer' import { IsBoolean, IsDate, IsInt, IsOptional, IsString } from 'class-validator' -import { DidCommV1Message } from '../../../../../../didcomm' import { ReturnRouteTypes } from '../../../../../decorators/transport/TransportDecorator' +import { DidCommV1Message } from '../../../../../didcomm' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { DateParser } from '../../../../../utils/transformers' diff --git "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" index 2b9df92400..9a94f099ff 100644 --- "a/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" +++ "b/packages/core/src/modules/message-p\303\254ckup/protocol/v2/messages/V2StatusRequestMessage.ts" @@ -1,7 +1,7 @@ import { Expose } from 'class-transformer' import { IsOptional, IsString } from 'class-validator' -import { AgentMessage } from '../../../../../agent/AgentMessage' +import { DidCommV1Message } from '../../../../../didcomm' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' export interface V2StatusRequestMessageOptions { diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index 1fe966789a..dd988ee4ad 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -23,7 +23,7 @@ import { inject, injectable } from '../../plugins' import { DidCommMessageRepository, DidCommMessageRole } from '../../storage' import { JsonEncoder, JsonTransformer } from '../../utils' import { parseMessageType, supportsIncomingMessageType } from '../../utils/messageType' -import { parseInvitationUrl, parseInvitationShortUrl } from '../../utils/parseInvitation' +import { parseInvitationShortUrl } from '../../utils/parseInvitation' import { ConnectionsApi, DidExchangeState, HandshakeProtocol } from '../connections' import { DidCommDocumentService } from '../didcomm' import { DidKey } from '../dids' @@ -347,7 +347,7 @@ export class OutOfBandApi { public async receiveInvitation( invitation: OutOfBandInvitation | ConnectionInvitationMessage | V2OutOfBandInvitation, config: ReceiveOutOfBandInvitationConfig = {} - ): Promise<{ outOfBandRecord: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { + ): Promise<{ outOfBandRecord?: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { return this._receiveInvitation(invitation, config) } @@ -383,24 +383,17 @@ export class OutOfBandApi { * Internal receive invitation method, for both explicit and implicit OOB invitations */ private async _receiveInvitation( - invitation: OutOfBandInvitation | ConnectionInvitationMessage, + invitation: OutOfBandInvitation | ConnectionInvitationMessage | V2OutOfBandInvitation, config: BaseReceiveOutOfBandInvitationConfig = {} - ): Promise<{ outOfBandRecord: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { - // Convert to out of band invitation if needed - const outOfBandInvitation = - invitation instanceof OutOfBandInvitation ? invitation : convertToNewInvitation(invitation) - - const { handshakeProtocols } = outOfBandInvitation + ): Promise<{ outOfBandRecord?: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { const { routing } = config - ): Promise<{ outOfBandRecord?: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { const autoAcceptInvitation = config.autoAcceptInvitation ?? true const autoAcceptConnection = config.autoAcceptConnection ?? true const reuseConnection = config.reuseConnection ?? false const label = config.label ?? this.agentContext.config.label const alias = config.alias const imageUrl = config.imageUrl ?? this.agentContext.config.connectionImageUrl - const { routing } = config let outOfBandRecord: OutOfBandRecord | null @@ -428,20 +421,20 @@ export class OutOfBandApi { ) } - // Make sure we haven't received this invitation before - // It's fine if we created it (means that we are connnecting to ourselves) or if it's an implicit - // invitation (it allows to connect multiple times to the same public did) - if (!config.isImplicit) { - const existingOobRecordsFromThisId = await this.outOfBandService.findAllByQuery(this.agentContext, { - invitationId: outOfBandInvitation.id, - role: OutOfBandRole.Receiver, - }) - if (existingOobRecordsFromThisId.length > 0) { - throw new AriesFrameworkError( - `An out of band record with invitation ${outOfBandInvitation.id} has already been received. Invitations should have a unique id.` - ) + // Make sure we haven't received this invitation before + // It's fine if we created it (means that we are connnecting to ourselves) or if it's an implicit + // invitation (it allows to connect multiple times to the same public did) + if (!config.isImplicit) { + const existingOobRecordsFromThisId = await this.outOfBandService.findAllByQuery(this.agentContext, { + invitationId: outOfBandInvitation.id, + role: OutOfBandRole.Receiver, + }) + if (existingOobRecordsFromThisId.length > 0) { + throw new AriesFrameworkError( + `An out of band record with invitation ${outOfBandInvitation.id} has already been received. Invitations should have a unique id.` + ) + } } - } const recipientKeyFingerprints: string[] = [] for (const service of outOfBandInvitation.getServices()) { @@ -464,13 +457,14 @@ export class OutOfBandApi { } } - const outOfBandRecord = new OutOfBandRecord({ - role: OutOfBandRole.Receiver, - state: OutOfBandState.Initial, - outOfBandInvitation: outOfBandInvitation, - autoAcceptConnection, - tags: { recipientKeyFingerprints }, - }) + outOfBandRecord = new OutOfBandRecord({ + role: OutOfBandRole.Receiver, + state: OutOfBandState.Initial, + outOfBandInvitation: outOfBandInvitation, + autoAcceptConnection, + tags: { recipientKeyFingerprints }, + }) + } if (!outOfBandRecord) { throw new AriesFrameworkError('Unable to receive Out-of-Band invitation.') diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts index 263c08df6c..1710421615 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandService.test.ts @@ -10,7 +10,6 @@ import { import { EventEmitter } from '../../../agent/EventEmitter' import { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import { KeyType, Key } from '../../../crypto' -import { KeyProviderRegistry } from '../../../crypto/key-provider' import { AriesFrameworkError } from '../../../error' import { DidExchangeState } from '../../connections/models' import { OutOfBandEventTypes } from '../domain/OutOfBandEvents' diff --git a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts index 846a139a87..626eb9573d 100644 --- a/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts +++ b/packages/core/src/modules/oob/__tests__/implicit.e2e.test.ts @@ -205,7 +205,7 @@ async function createPublicDid(agent: Agent, unqualifiedSubmitterDid: string, en alias: 'Alias', endpoints: { endpoint, - types: ['DIDComm', 'did-communication', 'endpoint'], + types: ['DIDCommMessaging', 'did-communication', 'endpoint'], }, }, }) diff --git a/packages/core/src/modules/oob/protocols/v1/OutOfBandService.ts b/packages/core/src/modules/oob/protocols/v1/OutOfBandService.ts index a171d12679..d781d7e253 100644 --- a/packages/core/src/modules/oob/protocols/v1/OutOfBandService.ts +++ b/packages/core/src/modules/oob/protocols/v1/OutOfBandService.ts @@ -1,24 +1,24 @@ -import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from './domain/OutOfBandEvents' -import type { AgentContext } from '../../agent' -import type { InboundMessageContext } from '../../agent/models/InboundMessageContext' -import type { Key } from '../../crypto' -import type { Query } from '../../storage/StorageService' -import type { ConnectionRecord } from '../connections' -import type { HandshakeProtocol } from '../connections/models' - -import { EventEmitter } from '../../agent/EventEmitter' -import { AriesFrameworkError } from '../../error' -import { injectable } from '../../plugins' -import { JsonTransformer } from '../../utils' -import { DidsApi } from '../dids' -import { parseDid } from '../dids/domain/parse' - -import { OutOfBandEventTypes } from './domain/OutOfBandEvents' -import { OutOfBandRole } from './domain/OutOfBandRole' -import { OutOfBandState } from './domain/OutOfBandState' +import type { AgentContext } from '../../../../agent' +import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { Key } from '../../../../crypto' +import type { Query } from '../../../../storage/StorageService' +import type { ConnectionRecord } from '../../../connections' +import type { HandshakeProtocol } from '../../../connections/models' +import type { HandshakeReusedEvent, OutOfBandStateChangedEvent } from '../../domain/OutOfBandEvents' + +import { EventEmitter } from '../../../../agent/EventEmitter' +import { AriesFrameworkError } from '../../../../error' +import { injectable } from '../../../../plugins' +import { JsonTransformer } from '../../../../utils' +import { DidsApi } from '../../../dids' +import { parseDid } from '../../../dids/domain/parse' +import { OutOfBandEventTypes } from '../../domain/OutOfBandEvents' +import { OutOfBandRole } from '../../domain/OutOfBandRole' +import { OutOfBandState } from '../../domain/OutOfBandState' +import { OutOfBandRecord, OutOfBandRepository } from '../../repository' + import { HandshakeReuseMessage, OutOfBandInvitation } from './messages' import { HandshakeReuseAcceptedMessage } from './messages/HandshakeReuseAcceptedMessage' -import { OutOfBandRecord, OutOfBandRepository } from './repository' export interface CreateFromImplicitInvitationConfig { did: string diff --git a/packages/core/src/modules/oob/protocols/v2/V2OutOfBandService.ts b/packages/core/src/modules/oob/protocols/v2/V2OutOfBandService.ts index 351a417f1b..eaf28fb727 100644 --- a/packages/core/src/modules/oob/protocols/v2/V2OutOfBandService.ts +++ b/packages/core/src/modules/oob/protocols/v2/V2OutOfBandService.ts @@ -87,8 +87,8 @@ export class V2OutOfBandService { private async createDid(agentContext: AgentContext, params: CreateDidParams = {}): Promise { // Create keys - const authentication = await agentContext.wallet.createKey({ seed: params.seed, keyType: KeyType.Ed25519 }) - const keyAgreement = await agentContext.wallet.createKey({ seed: params.seed, keyType: KeyType.X25519 }) + const authentication = await agentContext.wallet.createKey({ keyType: KeyType.Ed25519 }) + const keyAgreement = await agentContext.wallet.createKey({ keyType: KeyType.X25519 }) // Build services const services = this.prepareServices(params.routing) diff --git a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts index 4b2e1866cc..8fe666ec56 100644 --- a/packages/core/src/modules/oob/repository/OutOfBandRecord.ts +++ b/packages/core/src/modules/oob/repository/OutOfBandRecord.ts @@ -83,7 +83,7 @@ export class OutOfBandRecord extends BaseRecord implements ProofsApi { const requestMessage = await protocol.findRequestMessage(this.agentContext, proofRecord.id) + const recipientService = requestMessage?.serviceDecorator() + // Use connection if present if (proofRecord.connectionId) { const connectionRecord = await this.connectionService.getById(this.agentContext, proofRecord.connectionId) @@ -328,7 +327,7 @@ export class ProofsApi implements ProofsApi { } // Use ~service decorator otherwise - else if (requestMessage?.service) { + else if (recipientService) { // Create ~service decorator const routing = await this.routingService.getRouting(this.agentContext) const ourService = new ServiceDecorator({ @@ -336,7 +335,6 @@ export class ProofsApi implements ProofsApi { recipientKeys: [routing.recipientKey.publicKeyBase58], routingKeys: routing.routingKeys.map((key) => key.publicKeyBase58), }) - const recipientService = requestMessage.service const { message } = await protocol.acceptRequest(this.agentContext, { proofFormats: options.proofFormats, @@ -435,7 +433,7 @@ export class ProofsApi implements ProofsApi { * @returns the message itself and the proof record associated with the sent request message */ public async createRequest(options: CreateProofRequestOptions): Promise<{ - message: AgentMessage + message: DidCommV1Message proofRecord: ProofExchangeRecord }> { const protocol = this.getProtocol(options.protocolVersion) diff --git a/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts index 1b1cf6044a..5a844bd44a 100644 --- a/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts +++ b/packages/core/src/modules/proofs/formats/ProofFormatServiceOptions.ts @@ -1,6 +1,6 @@ -import type { V1Attachment } from '../../../decorators/attachment/V1Attachment' import type { ProofFormat, ProofFormatCredentialForRequestPayload, ProofFormatPayload } from './ProofFormat' import type { ProofFormatService } from './ProofFormatService' +import type { V1Attachment } from '../../../decorators/attachment/V1Attachment' import type { ProofFormatSpec } from '../models/ProofFormatSpec' import type { ProofExchangeRecord } from '../repository/ProofExchangeRecord' diff --git a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts index ce2a30df05..7a1ac4d372 100644 --- a/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/BaseProofProtocol.ts @@ -16,10 +16,10 @@ import type { SelectCredentialsForRequestOptions, SelectCredentialsForRequestReturn, } from './ProofProtocolOptions' -import type { AgentMessage } from '../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' import type { AgentContext } from '../../../agent/context/AgentContext' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { DidCommV1Message } from '../../../didcomm' import type { DependencyManager } from '../../../plugins' import type { Query } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' @@ -45,31 +45,31 @@ export abstract class BaseProofProtocol - ): Promise> - public abstract processProposal(messageContext: InboundMessageContext): Promise + ): Promise> + public abstract processProposal(messageContext: InboundMessageContext): Promise public abstract acceptProposal( agentContext: AgentContext, options: AcceptProofProposalOptions - ): Promise> + ): Promise> public abstract negotiateProposal( agentContext: AgentContext, options: NegotiateProofProposalOptions - ): Promise> + ): Promise> // methods for request public abstract createRequest( agentContext: AgentContext, options: CreateProofRequestOptions - ): Promise> - public abstract processRequest(messageContext: InboundMessageContext): Promise + ): Promise> + public abstract processRequest(messageContext: InboundMessageContext): Promise public abstract acceptRequest( agentContext: AgentContext, options: AcceptProofRequestOptions - ): Promise> + ): Promise> public abstract negotiateRequest( agentContext: AgentContext, options: NegotiateProofRequestOptions - ): Promise> + ): Promise> // retrieving credentials for request public abstract getCredentialsForRequest( @@ -82,26 +82,34 @@ export abstract class BaseProofProtocol> // methods for presentation - public abstract processPresentation(messageContext: InboundMessageContext): Promise + public abstract processPresentation( + messageContext: InboundMessageContext + ): Promise public abstract acceptPresentation( agentContext: AgentContext, options: AcceptPresentationOptions - ): Promise> + ): Promise> // methods for ack - public abstract processAck(messageContext: InboundMessageContext): Promise + public abstract processAck(messageContext: InboundMessageContext): Promise // method for problem report public abstract createProblemReport( agentContext: AgentContext, options: CreateProofProblemReportOptions ): Promise> - public abstract findProposalMessage(agentContext: AgentContext, proofExchangeId: string): Promise - public abstract findRequestMessage(agentContext: AgentContext, proofExchangeId: string): Promise + public abstract findProposalMessage( + agentContext: AgentContext, + proofExchangeId: string + ): Promise + public abstract findRequestMessage( + agentContext: AgentContext, + proofExchangeId: string + ): Promise public abstract findPresentationMessage( agentContext: AgentContext, proofExchangeId: string - ): Promise + ): Promise public abstract getFormatData( agentContext: AgentContext, proofExchangeId: string diff --git a/packages/core/src/modules/proofs/protocol/ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/ProofProtocol.ts index 2065d97b35..7436ef6493 100644 --- a/packages/core/src/modules/proofs/protocol/ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/ProofProtocol.ts @@ -15,10 +15,10 @@ import type { SelectCredentialsForRequestOptions, SelectCredentialsForRequestReturn, } from './ProofProtocolOptions' -import type { AgentMessage } from '../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../agent/FeatureRegistry' import type { AgentContext } from '../../../agent/context/AgentContext' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { DidCommV1Message } from '../../../didcomm' import type { DependencyManager } from '../../../plugins' import type { Query } from '../../../storage/StorageService' import type { ProblemReportMessage } from '../../problem-reports' @@ -33,31 +33,31 @@ export interface ProofProtocol - ): Promise> - processProposal(messageContext: InboundMessageContext): Promise + ): Promise> + processProposal(messageContext: InboundMessageContext): Promise acceptProposal( agentContext: AgentContext, options: AcceptProofProposalOptions - ): Promise> + ): Promise> negotiateProposal( agentContext: AgentContext, options: NegotiateProofProposalOptions - ): Promise> + ): Promise> // methods for request createRequest( agentContext: AgentContext, options: CreateProofRequestOptions - ): Promise> - processRequest(messageContext: InboundMessageContext): Promise + ): Promise> + processRequest(messageContext: InboundMessageContext): Promise acceptRequest( agentContext: AgentContext, options: AcceptProofRequestOptions - ): Promise> + ): Promise> negotiateRequest( agentContext: AgentContext, options: NegotiateProofRequestOptions - ): Promise> + ): Promise> // retrieving credentials for request getCredentialsForRequest( @@ -70,14 +70,14 @@ export interface ProofProtocol> // methods for presentation - processPresentation(messageContext: InboundMessageContext): Promise + processPresentation(messageContext: InboundMessageContext): Promise acceptPresentation( agentContext: AgentContext, options: AcceptPresentationOptions - ): Promise> + ): Promise> // methods for ack - processAck(messageContext: InboundMessageContext): Promise + processAck(messageContext: InboundMessageContext): Promise // method for problem report createProblemReport( @@ -86,9 +86,9 @@ export interface ProofProtocol> processProblemReport(messageContext: InboundMessageContext): Promise - findProposalMessage(agentContext: AgentContext, proofExchangeId: string): Promise - findRequestMessage(agentContext: AgentContext, proofExchangeId: string): Promise - findPresentationMessage(agentContext: AgentContext, proofExchangeId: string): Promise + findProposalMessage(agentContext: AgentContext, proofExchangeId: string): Promise + findRequestMessage(agentContext: AgentContext, proofExchangeId: string): Promise + findPresentationMessage(agentContext: AgentContext, proofExchangeId: string): Promise getFormatData( agentContext: AgentContext, proofExchangeId: string diff --git a/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts b/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts index ff752beb29..e84881f32a 100644 --- a/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts +++ b/packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts @@ -1,5 +1,5 @@ import type { ProofProtocol } from './ProofProtocol' -import type { AgentMessage } from '../../../agent/AgentMessage' +import type { DidCommV1Message } from '../../../didcomm' import type { ConnectionRecord } from '../../connections' import type { ExtractProofFormats, @@ -155,7 +155,7 @@ export interface CreateProofProblemReportOptions { description: string } -export interface ProofProtocolMsgReturnType { +export interface ProofProtocolMsgReturnType { message: MessageType proofRecord: ProofExchangeRecord } diff --git a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts index 54615d7034..0f60cbbee4 100644 --- a/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts +++ b/packages/core/src/modules/proofs/protocol/v2/ProofFormatCoordinator.ts @@ -1,5 +1,5 @@ import type { AgentContext } from '../../../../agent' -import type { Attachment } from '../../../../decorators/attachment/Attachment' +import type { V1Attachment } from '../../../../decorators/attachment/V1Attachment' import type { ExtractProofFormats, ProofFormatCredentialForRequestPayload, @@ -42,7 +42,7 @@ export class ProofFormatCoordinator { // create message. there are two arrays in each message, one for formats the other for attachments const formats: ProofFormatSpec[] = [] - const proposalAttachments: Attachment[] = [] + const proposalAttachments: V1Attachment[] = [] for (const formatService of formatServices) { const { format, attachment } = await formatService.createProposal(agentContext, { @@ -127,7 +127,7 @@ export class ProofFormatCoordinator { // create message. there are two arrays in each message, one for formats the other for attachments const formats: ProofFormatSpec[] = [] - const requestAttachments: Attachment[] = [] + const requestAttachments: V1Attachment[] = [] const proposalMessage = await didCommMessageRepository.getAgentMessage(agentContext, { associatedRecordId: proofRecord.id, @@ -202,7 +202,7 @@ export class ProofFormatCoordinator { // create message. there are two arrays in each message, one for formats the other for attachments const formats: ProofFormatSpec[] = [] - const requestAttachments: Attachment[] = [] + const requestAttachments: V1Attachment[] = [] for (const formatService of formatServices) { const { format, attachment } = await formatService.createRequest(agentContext, { @@ -296,7 +296,7 @@ export class ProofFormatCoordinator { // create message. there are two arrays in each message, one for formats the other for attachments const formats: ProofFormatSpec[] = [] - const presentationAttachments: Attachment[] = [] + const presentationAttachments: V1Attachment[] = [] for (const formatService of formatServices) { const requestAttachment = this.getAttachmentForService( @@ -497,7 +497,7 @@ export class ProofFormatCoordinator { public getAttachmentForService( credentialFormatService: ProofFormatService, formats: ProofFormatSpec[], - attachments: Attachment[] + attachments: V1Attachment[] ) { const attachmentId = this.getAttachmentIdForService(credentialFormatService, formats) const attachment = attachments.find((attachment) => attachment.id === attachmentId) diff --git a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts index 7c3bfbc88c..f873ff9b47 100644 --- a/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts +++ b/packages/core/src/modules/proofs/protocol/v2/V2ProofProtocol.ts @@ -1,7 +1,7 @@ import type { AgentContext } from '../../../../agent' -import type { AgentMessage } from '../../../../agent/AgentMessage' import type { FeatureRegistry } from '../../../../agent/FeatureRegistry' import type { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' +import type { DidCommV1Message } from '../../../../didcomm' import type { DependencyManager } from '../../../../plugins' import type { ProblemReportMessage } from '../../../problem-reports' import type { @@ -107,7 +107,7 @@ export class V2ProofProtocol - ): Promise<{ proofRecord: ProofExchangeRecord; message: AgentMessage }> { + ): Promise<{ proofRecord: ProofExchangeRecord; message: DidCommV1Message }> { const proofRepository = agentContext.dependencyManager.resolve(ProofRepository) const formatServices = this.getFormatServices(proofFormats) diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts index 8dcfb8cf6e..d0a44f69ea 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/V2ProofProtocol.test.ts @@ -7,7 +7,7 @@ import { Subject } from 'rxjs' import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../../../../tests/helpers' import { EventEmitter } from '../../../../../agent/EventEmitter' import { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' -import { V1Attachment, V1AttachmentData } from '../../../decorators/attachment/V1Attachment' +import { V1Attachment, V1AttachmentData } from '../../../../../decorators/attachment/V1Attachment' import { DidCommMessageRepository } from '../../../../../storage' import { uuid } from '../../../../../utils/uuid' import { ConnectionService, DidExchangeState } from '../../../../connections' diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts index 55f1e8bbbb..78e28c3aff 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-connectionless-proofs.e2e.test.ts @@ -21,7 +21,7 @@ import { waitForProofExchangeRecord, } from '../../../../../../tests' import { Agent } from '../../../../../agent/Agent' -import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { V1Attachment, V1AttachmentData } from '../../../../../decorators/attachment/V1Attachment' import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' import { uuid } from '../../../../../utils/uuid' import { HandshakeProtocol } from '../../../../connections' @@ -307,7 +307,7 @@ describe('V2 Connectionless Proofs - Indy', () => { autoAcceptProofs: AutoAcceptProof.Always, }), mediationRecipient: new MediationRecipientModule({ - mediatorInvitationUrl: faberMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + mediatorInvitationUrl: faberMediationOutOfBandRecord.getOutOfBandInvitation().toUrl({ domain: 'https://example.com', }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, @@ -323,7 +323,7 @@ describe('V2 Connectionless Proofs - Indy', () => { autoAcceptProofs: AutoAcceptProof.Always, }), mediationRecipient: new MediationRecipientModule({ - mediatorInvitationUrl: aliceMediationOutOfBandRecord.outOfBandInvitation.toUrl({ + mediatorInvitationUrl: aliceMediationOutOfBandRecord.getOutOfBandInvitation().toUrl({ domain: 'https://example.com', }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, @@ -369,16 +369,16 @@ describe('V2 Connectionless Proofs - Indy', () => { linkedAttachments: [ new LinkedAttachment({ name: 'image_0', - attachment: new Attachment({ + attachment: new V1Attachment({ filename: 'picture-of-a-cat.png', - data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + data: new V1AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), }), }), new LinkedAttachment({ name: 'image_1', - attachment: new Attachment({ + attachment: new V1Attachment({ filename: 'picture-of-a-dog.png', - data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + data: new V1AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), }), }), ], diff --git a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts index 29a7fce4c8..058e64a54a 100644 --- a/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts +++ b/packages/core/src/modules/proofs/protocol/v2/__tests__/v2-indy-proofs.e2e.test.ts @@ -7,7 +7,7 @@ import { } from '../../../../../../../anoncreds/tests/legacyAnonCredsSetup' import { waitForProofExchangeRecord } from '../../../../../../tests' import testLogger from '../../../../../../tests/logger' -import { Attachment, AttachmentData } from '../../../../../decorators/attachment/Attachment' +import { V1Attachment, V1AttachmentData } from '../../../../../decorators/attachment/V1Attachment' import { LinkedAttachment } from '../../../../../utils/LinkedAttachment' import { ProofState } from '../../../models' import { ProofExchangeRecord } from '../../../repository' @@ -62,16 +62,16 @@ describe('Present Proof', () => { linkedAttachments: [ new LinkedAttachment({ name: 'image_0', - attachment: new Attachment({ + attachment: new V1Attachment({ filename: 'picture-of-a-cat.png', - data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + data: new V1AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), }), }), new LinkedAttachment({ name: 'image_1', - attachment: new Attachment({ + attachment: new V1Attachment({ filename: 'picture-of-a-dog.png', - data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + data: new V1AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), }), }), ], diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationMessage.ts index d1c8b28083..5f12a1a86a 100644 --- a/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2PresentationMessage.ts @@ -3,7 +3,6 @@ import { IsArray, IsBoolean, IsInstance, IsOptional, IsString, ValidateNested } import { V1Attachment } from '../../../../../decorators/attachment/V1Attachment' import { DidCommV1Message } from '../../../../../didcomm' -import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { uuid } from '../../../../../utils/uuid' import { ProofFormatSpec } from '../../../models/ProofFormatSpec' @@ -13,7 +12,7 @@ export interface V2PresentationMessageOptions { goalCode?: string comment?: string lastPresentation?: boolean - presentationAttachments: Attachment[] + presentationAttachments: V1Attachment[] formats: ProofFormatSpec[] } diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposePresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposePresentationMessage.ts index 385925a5d0..7990bb254c 100644 --- a/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposePresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2ProposePresentationMessage.ts @@ -1,8 +1,8 @@ import { Expose, Type } from 'class-transformer' import { IsArray, IsInstance, IsOptional, IsString, ValidateNested } from 'class-validator' -import { AgentMessage } from '../../../../../agent/AgentMessage' -import { Attachment } from '../../../../../decorators/attachment/Attachment' +import { V1Attachment } from '../../../../../decorators/attachment/V1Attachment' +import { DidCommV1Message } from '../../../../../didcomm' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { uuid } from '../../../../../utils/uuid' import { ProofFormatSpec } from '../../../models/ProofFormatSpec' @@ -11,11 +11,11 @@ export interface V2ProposePresentationMessageOptions { id?: string comment?: string goalCode?: string - proposalAttachments: Attachment[] + proposalAttachments: V1Attachment[] formats: ProofFormatSpec[] } -export class V2ProposePresentationMessage extends AgentMessage { +export class V2ProposePresentationMessage extends DidCommV1Message { public constructor(options: V2ProposePresentationMessageOptions) { super() @@ -50,13 +50,13 @@ export class V2ProposePresentationMessage extends AgentMessage { public formats!: ProofFormatSpec[] @Expose({ name: 'proposals~attach' }) - @Type(() => Attachment) + @Type(() => V1Attachment) @IsArray() @ValidateNested({ each: true }) - @IsInstance(Attachment, { each: true }) - public proposalAttachments!: Attachment[] + @IsInstance(V1Attachment, { each: true }) + public proposalAttachments!: V1Attachment[] - public getProposalAttachmentById(id: string): Attachment | undefined { + public getProposalAttachmentById(id: string): V1Attachment | undefined { return this.proposalAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts b/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts index 259376b09e..5a890a3f57 100644 --- a/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/protocol/v2/messages/V2RequestPresentationMessage.ts @@ -3,7 +3,6 @@ import { IsArray, IsBoolean, IsInstance, IsOptional, IsString, ValidateNested } import { V1Attachment } from '../../../../../decorators/attachment/V1Attachment' import { DidCommV1Message } from '../../../../../didcomm' -import { AriesFrameworkError } from '../../../../../error' import { IsValidMessageType, parseMessageType } from '../../../../../utils/messageType' import { uuid } from '../../../../../utils/uuid' import { ProofFormatSpec } from '../../../models/ProofFormatSpec' @@ -70,7 +69,7 @@ export class V2RequestPresentationMessage extends DidCommV1Message { @IsInstance(V1Attachment, { each: true }) public requestAttachments!: V1Attachment[] - public getRequestAttachmentById(id: string): Attachment | undefined { + public getRequestAttachmentById(id: string): V1Attachment | undefined { return this.requestAttachments.find((attachment) => attachment.id === id) } } diff --git a/packages/core/src/modules/routing/MediatorApi.ts b/packages/core/src/modules/routing/MediatorApi.ts index 3f4034c62c..24b788ed4f 100644 --- a/packages/core/src/modules/routing/MediatorApi.ts +++ b/packages/core/src/modules/routing/MediatorApi.ts @@ -1,5 +1,5 @@ -import type { EncryptedMessage } from '../../didcomm/types' import type { MediationRecord } from './repository' +import type { EncryptedMessage } from '../../didcomm/types' import { AgentContext } from '../../agent' import { MessageHandlerRegistry } from '../../agent/MessageHandlerRegistry' diff --git a/packages/core/src/modules/routing/__tests__/mediation.test.ts b/packages/core/src/modules/routing/__tests__/mediation.test.ts index 574f999297..459e538909 100644 --- a/packages/core/src/modules/routing/__tests__/mediation.test.ts +++ b/packages/core/src/modules/routing/__tests__/mediation.test.ts @@ -95,7 +95,7 @@ describe('mediator establishment', () => { ...recipientAgentOptions.modules, mediationRecipient: new MediationRecipientModule({ mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, - mediatorInvitationUrl: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ + mediatorInvitationUrl: mediatorOutOfBandRecord.getOutOfBandInvitation().toUrl({ domain: 'https://example.com/ssi', }), }), @@ -133,7 +133,7 @@ describe('mediator establishment', () => { handshake: true, handshakeProtocols: [HandshakeProtocol.Connections], }) - const recipientInvitation = recipientOutOfBandRecord.outOfBandInvitation + const recipientInvitation = recipientOutOfBandRecord.getOutOfBandInvitation() let { connectionRecord: senderRecipientConnection } = await senderAgent.oob.receiveInvitationFromUrl( recipientInvitation.toUrl({ domain: 'https://example.com/ssi' }) @@ -219,7 +219,7 @@ describe('mediator establishment', () => { modules: { ...recipientAgentOptions.modules, mediationRecipient: new MediationRecipientModule({ - mediatorInvitationUrl: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ + mediatorInvitationUrl: mediatorOutOfBandRecord.getOutOfBandInvitation().toUrl({ domain: 'https://example.com/ssi', }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, @@ -252,7 +252,7 @@ describe('mediator establishment', () => { modules: { ...recipientAgentOptions.modules, mediationRecipient: new MediationRecipientModule({ - mediatorInvitationUrl: mediatorOutOfBandRecord.outOfBandInvitation.toUrl({ + mediatorInvitationUrl: mediatorOutOfBandRecord.getOutOfBandInvitation().toUrl({ domain: 'https://example.com/ssi', }), mediatorPickupStrategy: MediatorPickupStrategy.PickUpV1, @@ -274,7 +274,7 @@ describe('mediator establishment', () => { handshake: true, handshakeProtocols: [HandshakeProtocol.Connections], }) - const recipientInvitation = recipientOutOfBandRecord.outOfBandInvitation + const recipientInvitation = recipientOutOfBandRecord.getOutOfBandInvitation() let { connectionRecord: senderRecipientConnection } = await senderAgent.oob.receiveInvitationFromUrl( recipientInvitation.toUrl({ domain: 'https://example.com/ssi' }) diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 62497672b1..e776438007 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -1,8 +1,6 @@ import type { GetRoutingOptions, RemoveRoutingOptions } from './RoutingService' import type { AgentContext } from '../../../agent' -import type { AgentMessageReceivedEvent } from '../../../agent/Events' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { EncryptedMessage } from '../../../didcomm/types' import type { DidCommV1Message } from '../../../didcomm/versions/v1' import type { Query } from '../../../storage/StorageService' import type { ConnectionRecord } from '../../connections' diff --git a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index 9bcd063ddb..a5ac4eff54 100644 --- a/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -6,9 +6,6 @@ import { EventEmitter } from '../../../../agent/EventEmitter' import { MessageSender } from '../../../../agent/MessageSender' import { InboundMessageContext } from '../../../../agent/models/InboundMessageContext' import { Key } from '../../../../crypto' -import { KeyProviderRegistry } from '../../../../crypto/key-provider' -import { V1Attachment } from '../../../../decorators/attachment/V1Attachment' -import { AriesFrameworkError } from '../../../../error' import { uuid } from '../../../../utils/uuid' import { DidExchangeState } from '../../../connections' import { ConnectionMetadataKeys } from '../../../connections/repository/ConnectionMetadataTypes' @@ -66,7 +63,6 @@ describe('MediationRecipientService', () => { let agentContext: AgentContext beforeAll(async () => { - wallet = new IndyWallet(config.agentDependencies, config.logger, new KeyProviderRegistry([])) agentContext = getAgentContext({ agentConfig: config, }) diff --git a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts index 14ec7c22b8..c3377b070f 100644 --- a/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/RoutingService.test.ts @@ -20,7 +20,7 @@ const eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject const wallet = { createKey: jest.fn().mockResolvedValue(recipientKey), // with satisfies Partial we still get type errors when the interface changes -} satisfies Partial +} const agentContext = getAgentContext({ wallet: wallet as unknown as Wallet, agentConfig, diff --git a/packages/core/src/storage/InMemoryMessageRepository.ts b/packages/core/src/storage/InMemoryMessageRepository.ts index 45fb61f0e4..842d24f5d8 100644 --- a/packages/core/src/storage/InMemoryMessageRepository.ts +++ b/packages/core/src/storage/InMemoryMessageRepository.ts @@ -1,5 +1,5 @@ -import type { EncryptedMessage } from '../didcomm/types' import type { MessageRepository } from './MessageRepository' +import type { EncryptedMessage } from '../didcomm/types' import { InjectionSymbols } from '../constants' import { Logger } from '../logger' diff --git a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts index 37c6922ddd..ed01dfa714 100644 --- a/packages/core/src/storage/didcomm/DidCommMessageRecord.ts +++ b/packages/core/src/storage/didcomm/DidCommMessageRecord.ts @@ -1,5 +1,5 @@ -import type { ConstructableDidCommMessage } from '../../didcomm' import type { DidCommMessageRole } from './DidCommMessageRole' +import type { ConstructableDidCommMessage } from '../../didcomm' import type { JsonObject } from '../../types' import { AriesFrameworkError } from '../../error' diff --git a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap index 0b66feb17b..e37cd412d5 100644 --- a/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap +++ b/packages/core/src/storage/migration/__tests__/__snapshots__/0.1.test.ts.snap @@ -843,6 +843,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "role": "receiver", "state": "done", "updatedAt": "2022-01-21T22:50:20.522Z", + "v2OutOfBandInvitation": undefined, }, }, "2-4e4f-41d9-94c4-f49351b811f1": { @@ -909,6 +910,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "role": "sender", "state": "done", "updatedAt": "2022-01-21T22:50:20.522Z", + "v2OutOfBandInvitation": undefined, }, }, "3-4e4f-41d9-94c4-f49351b811f1": { @@ -975,6 +977,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "role": "sender", "state": "done", "updatedAt": "2022-01-21T22:50:20.522Z", + "v2OutOfBandInvitation": undefined, }, }, "4-4e4f-41d9-94c4-f49351b811f1": { @@ -1041,6 +1044,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "role": "receiver", "state": "done", "updatedAt": "2022-01-21T22:50:20.522Z", + "v2OutOfBandInvitation": undefined, }, }, "5-4e4f-41d9-94c4-f49351b811f1": { @@ -1107,6 +1111,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "role": "receiver", "state": "done", "updatedAt": "2022-01-21T22:50:20.522Z", + "v2OutOfBandInvitation": undefined, }, }, "6-4e4f-41d9-94c4-f49351b811f1": { @@ -1168,6 +1173,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "role": "sender", "state": "await-response", "updatedAt": "2022-01-21T22:50:20.522Z", + "v2OutOfBandInvitation": undefined, }, }, "7-4e4f-41d9-94c4-f49351b811f1": { @@ -1234,6 +1240,7 @@ exports[`UpdateAssistant | v0.1 - v0.2 should correctly update the connection re "role": "sender", "state": "await-response", "updatedAt": "2022-01-21T22:50:20.522Z", + "v2OutOfBandInvitation": undefined, }, }, "7781341d-be29-441b-9b79-4a957d8c6d37": { diff --git a/packages/core/src/transport/WsOutboundTransport.ts b/packages/core/src/transport/WsOutboundTransport.ts index bf618833f8..e99e555409 100644 --- a/packages/core/src/transport/WsOutboundTransport.ts +++ b/packages/core/src/transport/WsOutboundTransport.ts @@ -4,8 +4,6 @@ import type { Agent } from '../agent/Agent' import type { AgentMessageReceivedEvent } from '../agent/Events' import type { OutboundPackage } from '../didcomm/types' import type { Logger } from '../logger' -import type { OutboundTransport } from './OutboundTransport' -import type { OutboundWebSocketClosedEvent, OutboundWebSocketOpenedEvent } from './TransportEventTypes' import type WebSocket from 'ws' import { AgentEventTypes } from '../agent/Events' diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 67f00a0e5f..25ab78aad3 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,3 +1,4 @@ +import type { EncryptedMessage } from './didcomm' import type { Logger } from './logger' export enum KeyDerivationMethod { diff --git a/packages/core/src/utils/attachment.ts b/packages/core/src/utils/attachment.ts index 0d34830af3..970b199ee5 100644 --- a/packages/core/src/utils/attachment.ts +++ b/packages/core/src/utils/attachment.ts @@ -1,5 +1,5 @@ -import type { V1Attachment } from '../decorators/attachment/V1Attachment' import type { BaseName } from './MultiBaseEncoder' +import type { V1Attachment } from '../decorators/attachment/V1Attachment' import { AriesFrameworkError } from '../error/AriesFrameworkError' diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 7b8cb24a49..e062ff96c1 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -1,5 +1,5 @@ -import type { Key, KeyType, KeyPair } from '../crypto' -import type { EncryptedMessage, PlaintextMessage } from '../didcomm/types' +import type { Key, KeyPair, KeyType } from '../crypto' +import type { EncryptedMessage, PlaintextMessage, EnvelopeType } from '../didcomm/types' import type { Disposable } from '../plugins' import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '../types' import type { Buffer } from '../utils/buffer' @@ -12,6 +12,7 @@ export interface Wallet extends Disposable { createAndOpen(walletConfig: WalletConfig): Promise open(walletConfig: WalletConfig): Promise rotateKey(walletConfig: WalletConfigRekey): Promise + retrieveKeyPair(kid: string): Promise close(): Promise delete(): Promise @@ -39,7 +40,7 @@ export interface Wallet extends Disposable { sign(options: WalletSignOptions): Promise verify(options: WalletVerifyOptions): Promise - pack(payload: Record, recipientKeys: string[], senderVerkey?: string): Promise + pack(payload: Record, params: WalletPackOptions): Promise unpack(encryptedMessage: EncryptedMessage): Promise generateNonce(): Promise generateWalletKey(): Promise @@ -67,3 +68,19 @@ export interface UnpackedMessageContext { senderKey?: string recipientKey?: string } + +export interface WalletPackV1Options { + version: 'v1' + recipientKeys: string[] + senderKey: string | null + envelopeType?: EnvelopeType +} +export interface WalletPackV2Options { + version: 'v2' + toDid?: string + fromDid?: string + serviceId?: string + envelopeType?: EnvelopeType +} + +export type WalletPackOptions = WalletPackV1Options | WalletPackV2Options diff --git a/packages/core/tests/connections.test.ts b/packages/core/tests/connections.test.ts index 7e7adda126..9284b90be2 100644 --- a/packages/core/tests/connections.test.ts +++ b/packages/core/tests/connections.test.ts @@ -91,7 +91,7 @@ describe('connections', () => { multiUseInvitation: true, }) - const invitation = faberOutOfBandRecord.outOfBandInvitation + const invitation = faberOutOfBandRecord.getOutOfBandInvitation() const invitationUrl = invitation.toUrl({ domain: 'https://example.com' }) // Receive invitation with alice agent diff --git a/packages/core/tests/helpers.ts b/packages/core/tests/helpers.ts index 8537d5c35f..e288843fca 100644 --- a/packages/core/tests/helpers.ts +++ b/packages/core/tests/helpers.ts @@ -16,7 +16,10 @@ import type { Buffer, } from '../src' import type { AgentModulesInput, EmptyModuleMap } from '../src/agent/AgentModules' -import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../src/modules/connections/TrustPingEvents' +import type { + TrustPingReceivedEvent, + TrustPingResponseReceivedEvent, +} from '../src/modules/connections/protocols/trust-ping/TrustPingEvents' import type { ProofState } from '../src/modules/proofs/models/ProofState' import type { WalletConfig } from '../src/types' import type { Observable } from 'rxjs' @@ -49,7 +52,7 @@ import { DidCommV1Service } from '../src/modules/dids' import { DidKey } from '../src/modules/dids/methods/key' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' import { OutOfBandState } from '../src/modules/oob/domain/OutOfBandState' -import { OutOfBandInvitation } from '../src/modules/oob/messages' +import { OutOfBandInvitation } from '../src/modules/oob/protocols/v1/messages/OutOfBandInvitation' import { OutOfBandRecord } from '../src/modules/oob/repository' import { KeyDerivationMethod } from '../src/types' import { uuid } from '../src/utils/uuid' @@ -535,7 +538,9 @@ export async function makeConnection(agentA: Agent, agentB: Agent) { handshakeProtocols: [HandshakeProtocol.Connections], }) - let { connectionRecord: agentBConnection } = await agentB.oob.receiveInvitation(agentAOutOfBand.outOfBandInvitation) + let { connectionRecord: agentBConnection } = await agentB.oob.receiveInvitation( + agentAOutOfBand.getOutOfBandInvitation() + ) agentBConnection = await agentB.connections.returnWhenIsConnected(agentBConnection!.id) let [agentAConnection] = await agentA.connections.findAllByOutOfBandId(agentAOutOfBand.id) diff --git a/packages/core/tests/mocks/MockWallet.ts b/packages/core/tests/mocks/MockWallet.ts index 9f5dbd5615..ecbba3d4d3 100644 --- a/packages/core/tests/mocks/MockWallet.ts +++ b/packages/core/tests/mocks/MockWallet.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import type { Wallet, KeyPair } from '../../src' +import type { Wallet, KeyPair, WalletPackOptions } from '../../src' import type { Key } from '../../src/crypto' import type { EncryptedMessage } from '../../src/didcomm/types' import type { WalletConfig, WalletExportImportConfig, WalletConfigRekey } from '../../src/types' @@ -39,11 +39,7 @@ export class MockWallet implements Wallet { public import(walletConfig: WalletConfig, importConfig: WalletExportImportConfig): Promise { throw new Error('Method not implemented.') } - public pack( - payload: Record, - recipientKeys: string[], - senderVerkey?: string - ): Promise { + public pack(payload: Record, params: WalletPackOptions): Promise { throw new Error('Method not implemented.') } public unpack(encryptedMessage: EncryptedMessage): Promise { diff --git a/packages/core/tests/multi-protocol-version.test.ts b/packages/core/tests/multi-protocol-version.test.ts index 971203cfe4..fdb28cf00d 100644 --- a/packages/core/tests/multi-protocol-version.test.ts +++ b/packages/core/tests/multi-protocol-version.test.ts @@ -3,7 +3,7 @@ import type { AgentMessageProcessedEvent } from '../src/agent/Events' import { filter, firstValueFrom, timeout } from 'rxjs' import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' -import { parseMessageType, MessageSender, AgentMessage, IsValidMessageType } from '../src' +import { parseMessageType, MessageSender, IsValidMessageType } from '../src' import { Agent } from '../src/agent/Agent' import { AgentEventTypes } from '../src/agent/Events' import { OutboundMessageContext } from '../src/agent/models' diff --git a/packages/core/tests/oob-mediation-provision.test.ts b/packages/core/tests/oob-mediation-provision.test.ts index 190ce7090f..147c88cd0a 100644 --- a/packages/core/tests/oob-mediation-provision.test.ts +++ b/packages/core/tests/oob-mediation-provision.test.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { OutOfBandInvitation } from '../src/modules/oob/messages' -import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' +import type { OutOfBandInvitation } from '../src/modules/oob/protocols/v1/messages/OutOfBandInvitation' import type { V2OutOfBandInvitation } from '../src/modules/oob/protocols/v2' import { getIndySdkModules } from '../../indy-sdk/tests/setupIndySdkModule' @@ -72,7 +71,7 @@ describe('out of band with mediation set up with provision method', () => { await faberAgent.initialize() const mediationOutOfBandRecord = await mediatorAgent.oob.createInvitation(makeConnectionConfig) - mediatorOutOfBandInvitation = mediationOutOfBandRecord.outOfBandInvitation + mediatorOutOfBandInvitation = mediationOutOfBandRecord.getOutOfBandInvitation() let { connectionRecord } = await aliceAgent.oob.receiveInvitation(mediatorOutOfBandInvitation) connectionRecord = await aliceAgent.connections.returnWhenIsConnected(connectionRecord!.id) diff --git a/packages/core/tests/oob.test.ts b/packages/core/tests/oob.test.ts index c07da5d043..eb64b27e51 100644 --- a/packages/core/tests/oob.test.ts +++ b/packages/core/tests/oob.test.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' import type { V1CredentialProtocol } from '../../anoncreds/src' -import type { CreateCredentialOfferOptions } from '../src/modules/credentials' import type { DidCommV1Message } from '../src/didcomm/versions/v1' +import type { CreateCredentialOfferOptions } from '../src/modules/credentials' import type { AgentMessageReceivedEvent } from '@aries-framework/core' import { Subject } from 'rxjs' @@ -15,7 +15,7 @@ import { AgentEventTypes } from '../src/agent/Events' import { Key } from '../src/crypto' import { AriesFrameworkError } from '../src/error' import { DidExchangeState, HandshakeProtocol } from '../src/modules/connections' -import { AutoAcceptCredential, CredentialState, V1CredentialPreview } from '../src/modules/credentials' +import { AutoAcceptCredential, CredentialState } from '../src/modules/credentials' import { OutOfBandDidCommService } from '../src/modules/oob/domain/OutOfBandDidCommService' import { OutOfBandEventTypes } from '../src/modules/oob/domain/OutOfBandEvents' import { OutOfBandRole } from '../src/modules/oob/domain/OutOfBandRole' @@ -27,8 +27,6 @@ import { JsonEncoder } from '../src/utils' import { TestMessage } from './TestMessage' import { getAgentOptions, waitForCredentialRecord } from './helpers' -import { AgentEventTypes, AriesFrameworkError, AutoAcceptCredential, CredentialState } from '@aries-framework/core' - const faberAgentOptions = getAgentOptions( 'Faber Agent OOB', { @@ -189,10 +187,12 @@ describe('out of band', () => { expect(outOfBandRecord.state).toBe(OutOfBandState.AwaitResponse) expect(outOfBandRecord.alias).toBe(makeConnectionConfig.alias) expect(outOfBandRecord.reusable).toBe(false) - expect(outOfBandRecord.outOfBandInvitation.goal).toBe(makeConnectionConfig.goal) - expect(outOfBandRecord.outOfBandInvitation.goalCode).toBe(makeConnectionConfig.goalCode) - expect(outOfBandRecord.outOfBandInvitation.label).toBe(makeConnectionConfig.label) - expect(outOfBandRecord.outOfBandInvitation.imageUrl).toBe(makeConnectionConfig.imageUrl) + + const outOfBandInvitation = outOfBandRecord.getOutOfBandInvitation() + expect(outOfBandInvitation.goal).toBe(makeConnectionConfig.goal) + expect(outOfBandInvitation.goalCode).toBe(makeConnectionConfig.goalCode) + expect(outOfBandInvitation.label).toBe(makeConnectionConfig.label) + expect(outOfBandInvitation.imageUrl).toBe(makeConnectionConfig.imageUrl) }) test('create OOB message only with handshake', async () => { diff --git a/packages/didcomm-v2/README.md b/packages/didcomm-v2/README.md index 2a0d099c27..e69de29bb2 100644 --- a/packages/didcomm-v2/README.md +++ b/packages/didcomm-v2/README.md @@ -1,57 +0,0 @@ -

-
- Hyperledger Aries logo -

-

Aries Framework JavaScript - DidComm V2 Module

-

- License - typescript - @aries-framework/bbs-signatures version - -

-
- -Aries Framework JavaScript DidComm V2 Module provides an optional addon to Aries Framework JavaScript to support [DID Comm V2 messages](https://identity.foundation/didcomm-messaging/spec/). - -## Installation - -```sh -yarn add @aries-framework/didcomm-v2 -``` - -### Usage - -- NodeJS - [didcomm-node](https://www.npmjs.com/package/didcomm-node) package should be added into project dependencies and passed as a parameter into module constructor. - - ``` - import * as didcomm from 'didcomm-node' - - const didcommV2Module = new DidCommV2Module({ didcomm }) - didcommV2Module.register(this.agent.dependencyManager) - ``` - -- React Native - [@sicpa_open_source/didcomm-react-native](https://www.npmjs.com/package/@sicpa_open_source/didcomm-react-native) package should be added into project dependencies and passed as a parameter into module constructor. - - ``` - import * as didcomm from '@sicpa_open_source/didcomm-react-native' - - const didcommV2Module = new DidCommV2Module({ didcomm }) - didcommV2Module.register(this.agent.dependencyManager) - ``` diff --git a/packages/didcomm-v2/jest.config.ts b/packages/didcomm-v2/jest.config.ts index 55c67d70a6..93c0197296 100644 --- a/packages/didcomm-v2/jest.config.ts +++ b/packages/didcomm-v2/jest.config.ts @@ -6,7 +6,6 @@ import packageJson from './package.json' const config: Config.InitialOptions = { ...base, - name: packageJson.name, displayName: packageJson.name, setupFilesAfterEnv: ['./tests/setup.ts'], } diff --git a/packages/didcomm-v2/package.json b/packages/didcomm-v2/package.json index a4426b63f7..f460ba2e55 100644 --- a/packages/didcomm-v2/package.json +++ b/packages/didcomm-v2/package.json @@ -25,17 +25,17 @@ "test": "jest" }, "dependencies": { - "didcomm": "0.3.4" + "didcomm": "0.4.1" }, "peerDependencies": { - "@aries-framework/core": "0.3.2" + "@aries-framework/core": "0.3.3" }, "devDependencies": { - "@aries-framework/core": "0.3.2", - "@aries-framework/node": "0.3.2", - "didcomm-node": "0.3.4", + "@aries-framework/core": "0.3.3", + "@aries-framework/node": "0.3.3", + "didcomm-node": "0.4.1", "reflect-metadata": "^0.1.13", - "rimraf": "~3.0.2", - "typescript": "~4.3.0" + "rimraf": "^4.4.0", + "typescript": "~4.9.5" } } diff --git a/packages/didcomm-v2/src/DidCommV2Module.ts b/packages/didcomm-v2/src/DidCommV2Module.ts index 6b73894d51..f48a04b7b0 100644 --- a/packages/didcomm-v2/src/DidCommV2Module.ts +++ b/packages/didcomm-v2/src/DidCommV2Module.ts @@ -1,9 +1,9 @@ import type { DependencyManager, Module } from '@aries-framework/core' import type { default as didcomm } from 'didcomm' -import { DidCommV2EnvelopeServiceToken } from '@aries-framework/core' +// import { DidCommV2EnvelopeServiceToken } from '@aries-framework/core' -import { DidCommV2EnvelopeServiceImpl, DIDCommV2LibraryToken } from './services' +import { DIDCommV2LibraryToken, DidCommV2EnvelopeServiceToken, DidCommV2EnvelopeService } from './services' import { DidCommV2DidResolver } from './services/DidCommV2DidResolver' import { DidCommV2SecretsResolver } from './services/DidCommV2SecretsResolver' @@ -27,7 +27,7 @@ export class DidCommV2Module implements Module { public register(dependencyManager: DependencyManager) { dependencyManager.registerInstance(DIDCommV2LibraryToken, this.didcomm) - dependencyManager.registerSingleton(DidCommV2EnvelopeServiceToken, DidCommV2EnvelopeServiceImpl) + dependencyManager.registerSingleton(DidCommV2EnvelopeServiceToken, DidCommV2EnvelopeService) dependencyManager.registerContextScoped(DidCommV2DidResolver) dependencyManager.registerContextScoped(DidCommV2SecretsResolver) } diff --git a/packages/didcomm-v2/src/index.ts b/packages/didcomm-v2/src/index.ts index dab9f795a4..ab59a585a1 100644 --- a/packages/didcomm-v2/src/index.ts +++ b/packages/didcomm-v2/src/index.ts @@ -1,2 +1,2 @@ export * from './DidCommV2Module' -export { DidCommV2EnvelopeServiceImpl, DIDCommV2LibraryToken } from './services/DidCommV2EnvelopeServiceImpl' +export { DidCommV2EnvelopeService, DIDCommV2LibraryToken } from './services/DidCommV2EnvelopeService' diff --git a/packages/didcomm-v2/src/services/DidCommV2DidResolver.ts b/packages/didcomm-v2/src/services/DidCommV2DidResolver.ts index 24904ca6bd..7360184767 100644 --- a/packages/didcomm-v2/src/services/DidCommV2DidResolver.ts +++ b/packages/didcomm-v2/src/services/DidCommV2DidResolver.ts @@ -1,4 +1,4 @@ -import type { VerificationMethod, DidDocumentService } from '@aries-framework/core' +import type { DidDocumentService } from '@aries-framework/core' import type { DIDDoc, DIDResolver } from 'didcomm' import { @@ -6,8 +6,8 @@ import { AgentContext, DidResolverService, DidCommV2Service, - DidCommV1Service, IndyAgentService, + DidCommV1Service, } from '@aries-framework/core' @injectable() @@ -26,114 +26,66 @@ export class DidCommV2DidResolver implements DIDResolver { return null } - const verificationMethods = result.didDocument.verificationMethod?.map((verificationMethod) => - DidCommV2DidResolver.mapVerificationMethod(verificationMethod) - ) - const services = result.didDocument.service?.map((service) => DidCommV2DidResolver.mapService(service)) const didDod: DIDDoc = { - did: result.didDocument.id, - verification_methods: verificationMethods || [], - services: services || [], - key_agreements: [], - authentications: [], + id: result.didDocument.id, + verificationMethod: result.didDocument.verificationMethod || [], + service: services || [], + keyAgreement: [], + authentication: [], } const keyAgreements = result.didDocument.keyAgreement || [] for (const keyAgreement of keyAgreements) { if (typeof keyAgreement === 'string') { - didDod.key_agreements.push(keyAgreement) + didDod.keyAgreement.push(keyAgreement) } else { - didDod.key_agreements.push(keyAgreement.id) - didDod.verification_methods.push(DidCommV2DidResolver.mapVerificationMethod(keyAgreement)) + didDod.keyAgreement.push(keyAgreement.id) + didDod.verificationMethod.push(keyAgreement) } } const authentications = result.didDocument.authentication || [] for (const authentication of authentications) { if (typeof authentication === 'string') { - didDod.authentications.push(authentication) + didDod.authentication.push(authentication) } else { - didDod.authentications.push(authentication.id) - didDod.verification_methods.push(DidCommV2DidResolver.mapVerificationMethod(authentication)) + didDod.authentication.push(authentication.id) + didDod.verificationMethod.push(authentication) } } return didDod } - private static mapVerificationMethod(verificationMethod: VerificationMethod) { - return { - id: verificationMethod.id, - type: verificationMethod.type, - controller: verificationMethod.controller, - verification_material: verificationMethod.publicKeyBase58 - ? { format: 'Base58', value: verificationMethod.publicKeyBase58 } - : verificationMethod.publicKeyMultibase - ? { format: 'Multibase', value: verificationMethod.publicKeyMultibase } - : verificationMethod.publicKeyHex - ? { format: 'Hex', value: verificationMethod.publicKeyHex } - : verificationMethod.publicKeyJwk - ? { format: 'JWK', value: verificationMethod.publicKeyJwk } - : { - format: 'Other', - value: - verificationMethod.publicKeyPem || - verificationMethod.publicKeyBase64 || - verificationMethod.blockchainAccountId || - verificationMethod.ethereumAddress, - }, - } - } - private static mapService(service: DidDocumentService) { if (service instanceof DidCommV2Service) { return { id: service.id, - kind: { - DIDCommMessaging: { - service_endpoint: service.serviceEndpoint, - accept: service.accept ?? [], - routing_keys: service.routingKeys ?? [], - }, + type: 'DIDCommMessaging', + serviceEndpoint: { + uri: service.serviceEndpoint, + accept: service.accept ?? [], + routingKeys: service.routingKeys ?? [], }, } } else if (service instanceof DidCommV1Service) { return { id: service.id, - kind: { - Other: { - type: service.type, - serviceEndpoint: service.serviceEndpoint, - recipientKeys: service.recipientKeys || [], - routingKeys: service.routingKeys, - accept: service.accept, - priority: service.priority, - }, - }, - } - } else if (service instanceof IndyAgentService) { - return { - id: service.id, - kind: { - Other: { - type: service.type, - serviceEndpoint: service.serviceEndpoint, - recipientKeys: service.recipientKeys, - routingKeys: service.routingKeys, - priority: service.priority, - }, + type: 'DIDCommMessaging', + serviceEndpoint: { + uri: service.serviceEndpoint, + accept: service.accept ?? [], + routingKeys: service.routingKeys ?? [], }, } } else { return { id: service.id, - kind: { - Other: { - type: service.type, - serviceEndpoint: service.serviceEndpoint, - }, + type: 'DIDCommMessaging', + serviceEndpoint: { + uri: service.serviceEndpoint, }, } } diff --git a/packages/didcomm-v2/src/services/DidCommV2EnvelopeServiceImpl.ts b/packages/didcomm-v2/src/services/DidCommV2EnvelopeService.ts similarity index 94% rename from packages/didcomm-v2/src/services/DidCommV2EnvelopeServiceImpl.ts rename to packages/didcomm-v2/src/services/DidCommV2EnvelopeService.ts index ca9ce4f490..56bd9bc610 100644 --- a/packages/didcomm-v2/src/services/DidCommV2EnvelopeServiceImpl.ts +++ b/packages/didcomm-v2/src/services/DidCommV2EnvelopeService.ts @@ -1,5 +1,4 @@ import type { - DidCommV2EnvelopeService, AgentContext, DidCommV2Message, V2PackMessageParams, @@ -23,9 +22,10 @@ import { DidCommV2DidResolver } from './DidCommV2DidResolver' import { DidCommV2SecretsResolver } from './DidCommV2SecretsResolver' export const DIDCommV2LibraryToken = Symbol('DIDCommV2LibraryToken') +export const DidCommV2EnvelopeServiceToken = Symbol('DidCommV2EnvelopeServiceToken') @injectable() -export class DidCommV2EnvelopeServiceImpl implements DidCommV2EnvelopeService { +export class DidCommV2EnvelopeService { private didcomm: typeof didcommLibrary public constructor(@inject(DIDCommV2LibraryToken) didcomm: typeof didcommLibrary) { @@ -54,8 +54,7 @@ export class DidCommV2EnvelopeServiceImpl implements DidCommV2EnvelopeService { didResolver, secretsResolver, { - messaging_service: params.serviceId, - forward: params.wrapIntoForward, + forward: true, } ) return JsonEncoder.fromString(encryptedMsg) diff --git a/packages/didcomm-v2/src/services/DidCommV2SecretsResolver.ts b/packages/didcomm-v2/src/services/DidCommV2SecretsResolver.ts index 50f7f4ea07..cf3bc1b1f5 100644 --- a/packages/didcomm-v2/src/services/DidCommV2SecretsResolver.ts +++ b/packages/didcomm-v2/src/services/DidCommV2SecretsResolver.ts @@ -1,6 +1,6 @@ import type { Secret, SecretsResolver } from 'didcomm' -import { InjectionSymbols, Wallet, inject, injectable, getKeyDidMappingByKeyType, Key } from '@aries-framework/core' +import { InjectionSymbols, Wallet, inject, injectable, Key, getKeyDidMappingByKeyType } from '@aries-framework/core' @injectable() export class DidCommV2SecretsResolver implements SecretsResolver { @@ -33,7 +33,7 @@ export class DidCommV2SecretsResolver implements SecretsResolver { return { id: secret_id, type: supportedVerificationMethodTypes[0], - secret_material: { format: 'Base58', value: key.privateKeyBase58 }, + privateKeyBase58: key.privateKeyBase58, } } } diff --git a/packages/didcomm-v2/src/services/index.ts b/packages/didcomm-v2/src/services/index.ts index d9fcd86b01..2c625a1ffd 100644 --- a/packages/didcomm-v2/src/services/index.ts +++ b/packages/didcomm-v2/src/services/index.ts @@ -1 +1,5 @@ -export { DIDCommV2LibraryToken, DidCommV2EnvelopeServiceImpl } from './DidCommV2EnvelopeServiceImpl' +export { + DIDCommV2LibraryToken, + DidCommV2EnvelopeServiceToken, + DidCommV2EnvelopeService, +} from './DidCommV2EnvelopeService' diff --git a/packages/didcomm-v2/tests/DidCommV2DidResolver.test.ts b/packages/didcomm-v2/tests/DidCommV2DidResolver.test.ts index 35c1413f72..c9263896df 100644 --- a/packages/didcomm-v2/tests/DidCommV2DidResolver.test.ts +++ b/packages/didcomm-v2/tests/DidCommV2DidResolver.test.ts @@ -81,15 +81,10 @@ describe('DidCommV2DidResolver', () => { expect(result).not.toBeNull() const adaptedDidDocument = result as DIDDoc - expect(adaptedDidDocument.services).toHaveLength(4) - - expect(adaptedDidDocument.services[0].id).toBe('did:example:alice#mediator') - expect(adaptedDidDocument.services[0].kind).toStrictEqual({ - Other: { - type: 'Mediator', - serviceEndpoint: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', - }, - }) + expect(adaptedDidDocument.service).toHaveLength(4) + + expect(adaptedDidDocument.service[0].id).toBe('did:example:alice#mediator') + expect(adaptedDidDocument.service[0].serviceEndpoint).toStrictEqual({ uri: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h' }) }) it('converts DidDocumentService of endpoint type into didcomm lib Service of Other kind', async () => { @@ -97,15 +92,10 @@ describe('DidCommV2DidResolver', () => { expect(result).not.toBeNull() const adaptedDidDocument = result as DIDDoc - expect(adaptedDidDocument.services).toHaveLength(4) - - expect(adaptedDidDocument.services[1].id).toBe('did:example:alice#endpoint') - expect(adaptedDidDocument.services[1].kind).toStrictEqual({ - Other: { - type: 'endpoint', - serviceEndpoint: 'https://agent.com', - }, - }) + expect(adaptedDidDocument.service).toHaveLength(4) + + expect(adaptedDidDocument.service[1].id).toBe('did:example:alice#endpoint') + expect(adaptedDidDocument.service[1].serviceEndpoint).toStrictEqual({ uri: 'https://agent.com' }) }) it('converts IndyAgentService into didcomm lib Service of Other kind', async () => { @@ -113,18 +103,10 @@ describe('DidCommV2DidResolver', () => { expect(result).not.toBeNull() const adaptedDidDocument = result as DIDDoc - expect(adaptedDidDocument.services).toHaveLength(4) - - expect(adaptedDidDocument.services[2].id).toBe('did:example:alice#indy-agent') - expect(adaptedDidDocument.services[2].kind).toStrictEqual({ - Other: { - type: 'IndyAgent', - serviceEndpoint: 'did:sov:LjgpST2rjsoxYegQDRm7EL', - recipientKeys: ['did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1'], - routingKeys: ['did:sov:mediator1#key-agreement-1', 'did:sov:mediator2#key-agreement-2'], - priority: 5, - }, - }) + expect(adaptedDidDocument.service).toHaveLength(4) + + expect(adaptedDidDocument.service[2].id).toBe('did:example:alice#indy-agent') + expect(adaptedDidDocument.service[2].serviceEndpoint).toStrictEqual({ uri: 'did:sov:LjgpST2rjsoxYegQDRm7EL' }) }) it('converts DidCommV2Service into didcomm lib Service of DIDCommMessaging kind', async () => { @@ -132,15 +114,13 @@ describe('DidCommV2DidResolver', () => { expect(result).not.toBeNull() const adaptedDidDocument = result as DIDDoc - expect(adaptedDidDocument.services).toHaveLength(4) - - expect(adaptedDidDocument.services[3].id).toBe('did:example:alice#did-comm-v2') - expect(adaptedDidDocument.services[3].kind).toStrictEqual({ - DIDCommMessaging: { - service_endpoint: 'https://agent.com/did-comm-v2', - accept: ['didcomm/v2', 'didcomm/aip2;env=rfc587'], - routing_keys: ['did:example:mediator1#key-x25519', 'did:example:mediator2#key-x25519'], - }, + expect(adaptedDidDocument.service).toHaveLength(4) + + expect(adaptedDidDocument.service[3].id).toBe('did:example:alice#did-comm-v2') + expect(adaptedDidDocument.service[3].serviceEndpoint).toStrictEqual({ + accept: ['didcomm/v2', 'didcomm/aip2;env=rfc587'], + routing_keys: ['did:example:mediator1#key-x25519', 'did:example:mediator2#key-x25519'], + uri: 'https://agent.com/did-comm-v2', }) }) }) diff --git a/packages/didcomm-v2/tests/DidCommV2EnvelopeService.test.ts b/packages/didcomm-v2/tests/DidCommV2EnvelopeService.test.ts index 8bf1b9a2bd..ca0a7037ac 100644 --- a/packages/didcomm-v2/tests/DidCommV2EnvelopeService.test.ts +++ b/packages/didcomm-v2/tests/DidCommV2EnvelopeService.test.ts @@ -19,7 +19,7 @@ import { } from '@aries-framework/core' import { default as didcomm } from 'didcomm-node' -import { DidCommV2EnvelopeServiceImpl } from '../src/services' +import { DidCommV2EnvelopeService } from '../src/services' import { DidCommV2DidResolver } from '../src/services/DidCommV2DidResolver' import { DidCommV2SecretsResolver } from '../src/services/DidCommV2SecretsResolver' @@ -112,12 +112,14 @@ async function didResolutionFailureResult(): Promise { function oneKeySecretsResolver(secret: Secret) { const service = new SecretsResolverMock() - mockFunction(service.find_secrets).mockImplementation(async (secret_ids) => - Promise.resolve(secret_ids.filter((value) => value == secret.id)) - ) - mockFunction(service.get_secret).mockImplementation(async (secret_id) => - Promise.resolve(secret_id == secret.id ? secret : null) - ) + mockFunction(service.find_secrets).mockImplementation(async (secret_ids) => { + const a = await Promise.resolve(secret_ids.filter((value) => value == secret.id)) + return a + }) + mockFunction(service.get_secret).mockImplementation(async (secret_id) => { + const a = await Promise.resolve(secret_id == secret.id ? secret : null) + return a + }) return service } @@ -183,47 +185,39 @@ const mediator2DidDocument = new DidDocument({ const bobSecret = { id: 'did:example:bob#key-x25519', type: 'JsonWebKey2020', - secret_material: { - format: 'JWK', - value: { - kty: 'OKP', - crv: 'X25519', - x: 'GDTrI66K0pFfO54tlCSvfjjNapIs44dzpneBgyx0S3E', - d: 'b9NnuOCB0hm7YGNvaE9DMhwH_wjZA1-gWD6dA0JWdL0', - }, + privateKeyJwk: { + kty: 'OKP', + crv: 'X25519', + x: 'GDTrI66K0pFfO54tlCSvfjjNapIs44dzpneBgyx0S3E', + d: 'b9NnuOCB0hm7YGNvaE9DMhwH_wjZA1-gWD6dA0JWdL0', }, } const mediator1Secret = { id: 'did:example:mediator1#key-x25519', type: 'JsonWebKey2020', - secret_material: { - format: 'JWK', - value: { - kty: 'OKP', - crv: 'X25519', - x: 'UT9S3F5ep16KSNBBShU2wh3qSfqYjlasZimn0mB8_VM', - d: 'p-vteoF1gopny1HXywt76xz_uC83UUmrgszsI-ThBKk', - }, + privateKeyJwk: { + kty: 'OKP', + crv: 'X25519', + x: 'UT9S3F5ep16KSNBBShU2wh3qSfqYjlasZimn0mB8_VM', + d: 'p-vteoF1gopny1HXywt76xz_uC83UUmrgszsI-ThBKk', }, } const mediator2Secret = { id: 'did:example:mediator2#key-x25519', type: 'JsonWebKey2020', - secret_material: { - format: 'JWK', - value: { - kty: 'OKP', - crv: 'X25519', - x: '82k2BTUiywKv49fKLZa-WwDi8RBf0tB0M8bvSAUQ3yY', - d: 'f9WJeuQXEItkGM8shN4dqFr5fLQLBasHnWZ-8dPaSo0', - }, + privateKeyJwk: { + kty: 'OKP', + crv: 'X25519', + x: '82k2BTUiywKv49fKLZa-WwDi8RBf0tB0M8bvSAUQ3yY', + d: 'f9WJeuQXEItkGM8shN4dqFr5fLQLBasHnWZ-8dPaSo0', }, } +// FIXME: Forwrad wrapping seems to be broken describe('DIDCommV2EnvelopeService', () => { - const envelopeService = new DidCommV2EnvelopeServiceImpl(didcomm) + const envelopeService = new DidCommV2EnvelopeService(didcomm) const didResolverService = new DidResolverServiceMock() diff --git a/packages/didcomm-v2/tests/DidCommV2Module.test.ts b/packages/didcomm-v2/tests/DidCommV2Module.test.ts index c553cb3735..e0f0288de3 100644 --- a/packages/didcomm-v2/tests/DidCommV2Module.test.ts +++ b/packages/didcomm-v2/tests/DidCommV2Module.test.ts @@ -1,9 +1,9 @@ import type { DependencyManager } from '@aries-framework/core' -import { DidCommV2EnvelopeServiceToken } from '@aries-framework/core' +// import { DidCommV2EnvelopeServiceToken } from '@aries-framework/core' import { default as didcomm } from 'didcomm-node' -import { DIDCommV2LibraryToken, DidCommV2Module, DidCommV2EnvelopeServiceImpl } from '../src' +import { DIDCommV2LibraryToken, DidCommV2Module } from '../src' const dependencyManager = { registerInstance: jest.fn(), @@ -20,9 +20,9 @@ describe('DidCommV2Module', () => { expect(dependencyManager.registerInstance).toHaveBeenCalledWith(DIDCommV2LibraryToken, didcomm) expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith( - DidCommV2EnvelopeServiceToken, - DidCommV2EnvelopeServiceImpl - ) + // expect(dependencyManager.registerSingleton).toHaveBeenCalledWith( + // DidCommV2EnvelopeServiceToken, + // DidCommV2EnvelopeServiceImpl + // ) }) }) diff --git a/packages/didcomm-v2/tsconfig.build.json b/packages/didcomm-v2/tsconfig.build.json index 9c30e30bd2..e69de29bb2 100644 --- a/packages/didcomm-v2/tsconfig.build.json +++ b/packages/didcomm-v2/tsconfig.build.json @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - - "compilerOptions": { - "outDir": "./build" - }, - - "include": ["src/**/*"] -} diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts index f475158263..3bd27a3455 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidRegistrar.test.ts @@ -3,7 +3,7 @@ import type { IndySdkPool } from '../../ledger/IndySdkPool' import type { DidRecord, RecordSavedEvent } from '@aries-framework/core' import { - SigningProviderRegistry, + KeyProviderRegistry, DidsApi, DidDocument, VerificationMethod, @@ -34,7 +34,7 @@ const pool = { mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue(pool) const agentConfig = getAgentConfig('IndySdkIndyDidRegistrar') -const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new KeyProviderRegistry([])) jest .spyOn(wallet, 'createKey') @@ -341,7 +341,7 @@ describe('IndySdkIndyDidRegistrar', () => { endpoints: { endpoint: 'https://example.com/endpoint', routingKeys: ['key-1'], - types: ['DIDComm', 'did-communication', 'endpoint'], + types: ['DIDCommMessaging', 'did-communication', 'endpoint'], }, }, secret: { @@ -411,7 +411,7 @@ describe('IndySdkIndyDidRegistrar', () => { { id: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ#didcomm-1', serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', + type: 'DIDCommMessaging', routingKeys: ['key-1'], accept: ['didcomm/v2'], }, @@ -452,7 +452,7 @@ describe('IndySdkIndyDidRegistrar', () => { endpoints: { endpoint: 'https://example.com/endpoint', routingKeys: ['key-1'], - types: ['DIDComm', 'did-communication', 'endpoint'], + types: ['DIDCommMessaging', 'did-communication', 'endpoint'], }, }, secret: { diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts index 7c4f294286..765717013b 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkIndyDidResolver.test.ts @@ -2,7 +2,7 @@ import type { IndySdkPool } from '../../ledger' import type { IndyEndpointAttrib } from '../didSovUtil' import type { GetNymResponse } from 'indy-sdk' -import { SigningProviderRegistry, JsonTransformer } from '@aries-framework/core' +import { KeyProviderRegistry, JsonTransformer } from '@aries-framework/core' import indySdk from 'indy-sdk' import { mockFunction, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' @@ -24,7 +24,7 @@ mockFunction(indySdkPoolServiceMock.getPoolForNamespace).mockReturnValue({ const agentConfig = getAgentConfig('IndySdkIndyDidResolver') -const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new KeyProviderRegistry([])) const agentContext = getAgentContext({ wallet, @@ -83,7 +83,7 @@ describe('IndySdkIndyDidResolver', () => { const endpoints: IndyEndpointAttrib = { endpoint: 'https://agent.com', - types: ['endpoint', 'did-communication', 'DIDComm'], + types: ['endpoint', 'did-communication', 'DIDCommMessaging'], routingKeys: ['routingKey1', 'routingKey2'], } diff --git a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts index 6f4eabab97..5b1d2f1146 100644 --- a/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts +++ b/packages/indy-sdk/src/dids/__tests__/IndySdkSovDidResolver.test.ts @@ -2,7 +2,7 @@ import type { IndySdkPool } from '../../ledger' import type { IndyEndpointAttrib } from '../didSovUtil' import type { GetNymResponse } from 'indy-sdk' -import { SigningProviderRegistry, JsonTransformer } from '@aries-framework/core' +import { KeyProviderRegistry, JsonTransformer } from '@aries-framework/core' import indySdk from 'indy-sdk' import { parseDid } from '../../../../core/src/modules/dids/domain/parse' @@ -29,7 +29,7 @@ mockFunction(indySdkPoolServiceMock.getPoolForDid).mockResolvedValue({ const agentConfig = getAgentConfig('IndySdkSovDidResolver') -const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new KeyProviderRegistry([])) const agentContext = getAgentContext({ wallet, @@ -88,7 +88,7 @@ describe('IndySdkSovDidResolver', () => { const endpoints: IndyEndpointAttrib = { endpoint: 'https://agent.com', - types: ['endpoint', 'did-communication', 'DIDComm'], + types: ['endpoint', 'did-communication', 'DIDCommMessaging'], routingKeys: ['routingKey1', 'routingKey2'], } diff --git a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json index a943f3bf9e..0216cdc6a0 100644 --- a/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json +++ b/packages/indy-sdk/src/dids/__tests__/__fixtures__/didIndyPool1WJz9mHyW9BZksioQnRsrAo.json @@ -39,7 +39,7 @@ }, { "id": "did:indy:pool1:WJz9mHyW9BZksioQnRsrAo#didcomm-1", - "type": "DIDComm", + "type": "DIDCommMessaging", "serviceEndpoint": "https://agent.com", "accept": ["didcomm/v2"], "routingKeys": ["routingKey1", "routingKey2"] diff --git a/packages/indy-sdk/src/dids/didSovUtil.ts b/packages/indy-sdk/src/dids/didSovUtil.ts index 2a5df94a6e..2975fee9b0 100644 --- a/packages/indy-sdk/src/dids/didSovUtil.ts +++ b/packages/indy-sdk/src/dids/didSovUtil.ts @@ -9,11 +9,11 @@ import { import { getFullVerkey } from '../utils/did' -export type DidCommServicesEndpointType = 'endpoint' | 'did-communication' | 'DIDComm' +export type DidCommServicesEndpointType = 'endpoint' | 'did-communication' | 'DIDCommMessaging' export interface IndyEndpointAttrib { endpoint?: string - types?: Array<'endpoint' | 'did-communication' | 'DIDComm'> + types?: Array<'endpoint' | 'did-communication' | 'DIDCommMessaging'> routingKeys?: string[] [key: string]: unknown } @@ -109,7 +109,7 @@ export function addServicesFromEndpointsAttrib( }) ) - // If 'DIDComm' included in types, add DIDComm v2 entry + // If 'DIDCommMessaging' included in types, add DIDComm v2 entry if (processedTypes.includes('DIDCommMessaging')) { builder .addService( diff --git a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts index bf632152bb..628d603c0d 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPoolService.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPoolService.ts @@ -151,7 +151,7 @@ export class IndySdkPoolService { await cache.set(agentContext, cacheKey, { nymResponse: value.did, indyNamespace: value.pool.didIndyNamespace, - } satisfies CachedDidResponse) + }) return { pool: value.pool, nymResponse: value.did } } diff --git a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts index 20d79e0564..db61266ea3 100644 --- a/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts +++ b/packages/indy-sdk/src/ledger/__tests__/IndySdkPoolService.test.ts @@ -4,7 +4,7 @@ import type { CachedDidResponse } from '../IndySdkPoolService' import { CacheModuleConfig, InMemoryLruCache, - SigningProviderRegistry, + KeyProviderRegistry, AriesFrameworkError, Key, KeyType, @@ -58,7 +58,7 @@ const config = getAgentConfig('IndySdkPoolServiceTest') const cache = new InMemoryLruCache({ limit: 1 }) const indySdkModule = new IndySdkModuleConfig({ indySdk, networks: pools }) -const wallet = new IndySdkWallet(indySdk, config.logger, new SigningProviderRegistry([])) +const wallet = new IndySdkWallet(indySdk, config.logger, new KeyProviderRegistry([])) const agentContext = getAgentContext({ wallet, diff --git a/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts b/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts index baa7207d7f..155ae2d6dd 100644 --- a/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts +++ b/packages/indy-sdk/src/storage/__tests__/IndySdkStorageService.test.ts @@ -1,7 +1,7 @@ import type { IndySdk } from '../../types' import type { TagsBase } from '@aries-framework/core' -import { RecordDuplicateError, RecordNotFoundError, SigningProviderRegistry } from '@aries-framework/core' +import { RecordDuplicateError, RecordNotFoundError, KeyProviderRegistry } from '@aries-framework/core' import * as indySdk from 'indy-sdk' import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' @@ -10,7 +10,7 @@ import { IndySdkWallet } from '../../wallet/IndySdkWallet' import { IndySdkStorageService } from '../IndySdkStorageService' const agentConfig = getAgentConfig('IndySdkStorageServiceTest') -const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new KeyProviderRegistry([])) const agentContext = getAgentContext({ wallet, diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index b3d5bed9a9..96bc1d86c9 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -9,6 +9,7 @@ import type { WalletConfigRekey, WalletCreateKeyOptions, WalletExportImportConfig, + WalletPackOptions, WalletSignOptions, WalletVerifyOptions, } from '@aries-framework/core' @@ -25,7 +26,7 @@ import { KeyType, Logger, RecordNotFoundError, - SigningProviderRegistry, + KeyProviderRegistry, TypedArrayEncoder, WalletDuplicateError, WalletError, @@ -48,16 +49,16 @@ export class IndySdkWallet implements Wallet { private walletHandle?: number private logger: Logger - private signingKeyProviderRegistry: SigningProviderRegistry + private keyProviderRegistry: KeyProviderRegistry private indySdk: IndySdk public constructor( @inject(IndySdkSymbol) indySdk: IndySdk, @inject(InjectionSymbols.Logger) logger: Logger, - signingKeyProviderRegistry: SigningProviderRegistry + signingKeyProviderRegistry: KeyProviderRegistry ) { this.logger = logger - this.signingKeyProviderRegistry = signingKeyProviderRegistry + this.keyProviderRegistry = signingKeyProviderRegistry this.indySdk = indySdk } @@ -431,8 +432,8 @@ export class IndySdkWallet implements Wallet { } // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) + if (this.keyProviderRegistry.hasProviderForKeyType(keyType)) { + const signingKeyProvider = this.keyProviderRegistry.getProviderForKeyType(keyType) const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) await this.storeKeyPair(keyPair) @@ -476,8 +477,8 @@ export class IndySdkWallet implements Wallet { } // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + if (this.keyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.keyProviderRegistry.getProviderForKeyType(key.keyType) const keyPair = await this.retrieveKeyPair(key.publicKeyBase58) const signed = await signingKeyProvider.sign({ @@ -523,8 +524,8 @@ export class IndySdkWallet implements Wallet { } // Check if there is a signing key provider for the specified key type. - if (this.signingKeyProviderRegistry.hasProviderForKeyType(key.keyType)) { - const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(key.keyType) + if (this.keyProviderRegistry.hasProviderForKeyType(key.keyType)) { + const signingKeyProvider = this.keyProviderRegistry.getProviderForKeyType(key.keyType) const signed = await signingKeyProvider.verify({ data, @@ -545,15 +546,16 @@ export class IndySdkWallet implements Wallet { throw new WalletError(`Unsupported keyType: ${key.keyType}`) } - public async pack( - payload: Record, - recipientKeys: string[], - senderVerkey?: string - ): Promise { + public async pack(payload: Record, params: WalletPackOptions): Promise { try { - const messageRaw = JsonEncoder.toBuffer(payload) - const packedMessage = await this.indySdk.packMessage(this.handle, messageRaw, recipientKeys, senderVerkey ?? null) - return JsonEncoder.fromBuffer(packedMessage) + if (params.version === 'v1') { + const messageRaw = JsonEncoder.toBuffer(payload) + const { recipientKeys, senderKey } = params + const packedMessage = await this.indySdk.packMessage(this.handle, messageRaw, recipientKeys, senderKey) + return JsonEncoder.fromBuffer(packedMessage) + } else { + throw new AriesFrameworkError('DIDComm V2 message packing is not supported for IndyWallet') + } } catch (error) { if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) @@ -590,7 +592,7 @@ export class IndySdkWallet implements Wallet { } } - private async retrieveKeyPair(publicKeyBase58: string): Promise { + public async retrieveKeyPair(publicKeyBase58: string): Promise { try { const { value } = await this.indySdk.getWalletRecord(this.handle, 'KeyPairRecord', `key-${publicKeyBase58}`, {}) if (value) { diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts index 1aa3120681..acb4487592 100644 --- a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -1,10 +1,10 @@ -import type { SigningProvider, WalletConfig } from '@aries-framework/core' +import type { KeyProvider, WalletConfig } from '@aries-framework/core' import { Key, WalletKeyExistsError, KeyType, - SigningProviderRegistry, + KeyProviderRegistry, TypedArrayEncoder, KeyDerivationMethod, } from '@aries-framework/core' @@ -24,7 +24,7 @@ const walletConfig: WalletConfig = { const signingProvider = { keyType: KeyType.X25519, createKeyPair: () => Promise.resolve({ keyType: KeyType.X25519, privateKeyBase58: 'b', publicKeyBase58: 'a' }), -} satisfies Partial +} describe('IndySdkWallet', () => { let indySdkWallet: IndySdkWallet @@ -36,7 +36,7 @@ describe('IndySdkWallet', () => { indySdkWallet = new IndySdkWallet( indySdk, testLogger, - new SigningProviderRegistry([signingProvider as unknown as SigningProvider]) + new KeyProviderRegistry([signingProvider as unknown as KeyProvider]) ) await indySdkWallet.createAndOpen(walletConfig) }) diff --git a/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts b/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts index 04781e2e62..bcdd04e039 100644 --- a/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts +++ b/packages/indy-sdk/tests/indy-did-registrar.e2e.test.ts @@ -48,7 +48,7 @@ describe('Indy SDK Indy Did Registrar', () => { alias: 'Alias', endpoints: { endpoint: 'https://example.com/endpoint', - types: ['DIDComm', 'did-communication', 'endpoint'], + types: ['DIDCommMessaging', 'did-communication', 'endpoint'], routingKeys: ['a-routing-key'], }, }, @@ -106,7 +106,7 @@ describe('Indy SDK Indy Did Registrar', () => { id: `did:indy:pool:localtest:${unqualifiedDid}#didcomm-1`, routingKeys: ['a-routing-key'], serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', + type: 'DIDCommMessaging', }, ], authentication: [`did:indy:pool:localtest:${unqualifiedDid}#verkey`], diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts index 1783f13ee3..d040cf0667 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidRegistrar.test.ts @@ -14,7 +14,7 @@ import { Key, KeyType, RepositoryEventTypes, - SigningProviderRegistry, + KeyProviderRegistry, TypedArrayEncoder, VerificationMethod, } from '@aries-framework/core' @@ -33,7 +33,7 @@ mockProperty(poolMock, 'indyNamespace', 'ns1') const agentConfig = getAgentConfig('IndyVdrIndyDidRegistrar') -const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) +const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new KeyProviderRegistry([])) jest .spyOn(wallet, 'createKey') @@ -427,7 +427,7 @@ describe('IndyVdrIndyDidRegistrar', () => { id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-1', routingKeys: ['key-1'], serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', + type: 'DIDCommMessaging', }, ], verificationMethod: [ @@ -494,7 +494,7 @@ describe('IndyVdrIndyDidRegistrar', () => { { id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-1', serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', + type: 'DIDCommMessaging', routingKeys: ['key-1'], accept: ['didcomm/v2'], }, @@ -601,7 +601,7 @@ describe('IndyVdrIndyDidRegistrar', () => { endpoints: { endpoint: 'https://example.com/endpoint', routingKeys: ['key-1'], - types: ['endpoint', 'did-communication', 'DIDComm'], + types: ['endpoint', 'did-communication', 'DIDCommMessaging'], }, }) expect(setEndpointsForDidSpy).not.toHaveBeenCalled() @@ -651,7 +651,7 @@ describe('IndyVdrIndyDidRegistrar', () => { { id: 'did:indy:pool1:B6xaJg1c2xU3D9ppCtt1CZ#didcomm-1', serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', + type: 'DIDCommMessaging', routingKeys: ['key-1'], accept: ['didcomm/v2'], }, diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts index 1e652a9b5f..f1221726b7 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrIndyDidResolver.test.ts @@ -107,7 +107,7 @@ describe('IndyVdrIndyDidResolver', () => { data: JSON.stringify({ endpoint: { endpoint: 'https://agent.com', - types: ['endpoint', 'did-communication', 'DIDComm'], + types: ['endpoint', 'did-communication', 'DIDCommMessaging'], routingKeys: ['routingKey1', 'routingKey2'], }, }), diff --git a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts index 84ac0006df..cfe09fa3f1 100644 --- a/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts +++ b/packages/indy-vdr/src/dids/__tests__/IndyVdrSovDidResolver.test.ts @@ -82,7 +82,7 @@ describe('DidResolver', () => { data: JSON.stringify({ endpoint: { endpoint: 'https://agent.com', - types: ['endpoint', 'did-communication', 'DIDComm'], + types: ['endpoint', 'did-communication', 'DIDCommMessaging'], routingKeys: ['routingKey1', 'routingKey2'], }, }), diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json index 2a58c356ca..b73953aae8 100644 --- a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didIndyWJz9mHyW9BZksioQnRsrAo.json @@ -39,7 +39,7 @@ }, { "id": "did:indy:ns1:WJz9mHyW9BZksioQnRsrAo#didcomm-1", - "type": "DIDComm", + "type": "DIDCommMessaging", "serviceEndpoint": "https://agent.com", "accept": ["didcomm/v2"], "routingKeys": ["routingKey1", "routingKey2"] diff --git a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json index 7b74e0587f..81bbc35e06 100644 --- a/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json +++ b/packages/indy-vdr/src/dids/__tests__/__fixtures__/didSovWJz9mHyW9BZksioQnRsrAo.json @@ -40,7 +40,7 @@ }, { "id": "did:sov:WJz9mHyW9BZksioQnRsrAo#didcomm-1", - "type": "DIDComm", + "type": "DIDCommMessaging", "serviceEndpoint": "https://agent.com", "accept": ["didcomm/v2"], "routingKeys": ["routingKey1", "routingKey2"] diff --git a/packages/indy-vdr/src/dids/didSovUtil.ts b/packages/indy-vdr/src/dids/didSovUtil.ts index 0517d00315..0520e75532 100644 --- a/packages/indy-vdr/src/dids/didSovUtil.ts +++ b/packages/indy-vdr/src/dids/didSovUtil.ts @@ -8,7 +8,7 @@ import { AriesFrameworkError, } from '@aries-framework/core' -export type CommEndpointType = 'endpoint' | 'did-communication' | 'DIDComm' +export type CommEndpointType = 'endpoint' | 'did-communication' | 'DIDCommMessaging' export interface IndyEndpointAttrib { endpoint?: string @@ -88,7 +88,7 @@ export function sovDidDocumentFromDid(fullDid: string, verkey: string) { // Process Indy Attrib Endpoint Types according to: https://sovrin-foundation.github.io/sovrin/spec/did-method-spec-template.html > Read (Resolve) > DID Service Endpoint function processEndpointTypes(types?: string[]) { - const expectedTypes = ['endpoint', 'did-communication', 'DIDComm'] + const expectedTypes = ['endpoint', 'did-communication', 'DIDCommMessaging'] const defaultTypes = ['endpoint', 'did-communication'] // Return default types if types "is NOT present [or] empty" @@ -108,7 +108,7 @@ function processEndpointTypes(types?: string[]) { } export function endpointsAttribFromServices(services: DidDocumentService[]): IndyEndpointAttrib { - const commTypes: CommEndpointType[] = ['endpoint', 'did-communication', 'DIDComm'] + const commTypes: CommEndpointType[] = ['endpoint', 'did-communication', 'DIDCommMessaging'] const commServices = services.filter((item) => commTypes.includes(item.type as CommEndpointType)) // Check that all services use the same endpoint, as only one is accepted @@ -173,9 +173,9 @@ export function addServicesFromEndpointsAttrib( }) ) - // If 'DIDComm' included in types, add DIDComm v2 entry + // If 'DIDCommMessaging' included in types, add DIDComm v2 entry // TODO: should it be DIDComm or DIDCommMessaging? (see https://github.com/sovrin-foundation/sovrin/issues/343) - if (processedTypes.includes('DIDComm')) { + if (processedTypes.includes('DIDCommMessaging')) { builder .addService( new DidCommV2Service({ diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index 95e8742dd6..c74c522057 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -517,7 +517,7 @@ describe('Indy VDR Indy Did Registrar', () => { id: `${did}#didcomm-1`, routingKeys: ['a-routing-key'], serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', + type: 'DIDCommMessaging', }, ], } @@ -667,7 +667,7 @@ describe('Indy VDR Indy Did Registrar', () => { id: `${did}#didcomm-1`, routingKeys: ['a-routing-key'], serviceEndpoint: 'https://example.com/endpoint', - type: 'DIDComm', + type: 'DIDCommMessaging', }, ], } diff --git a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts index 92068e5690..c642684212 100644 --- a/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-indy-did-resolver.e2e.test.ts @@ -132,7 +132,7 @@ describe('indy-vdr DID Resolver E2E', () => { accept: ['didcomm/v2'], routingKeys: ['a-routing-key'], serviceEndpoint: 'http://localhost:3000', - type: 'DIDComm', + type: 'DIDCommMessaging', }, ], }, diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index e3dab42a4b..95e1ffa41c 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -1,6 +1,6 @@ import type { Key } from '@aries-framework/core' -import { TypedArrayEncoder, KeyType, SigningProviderRegistry } from '@aries-framework/core' +import { TypedArrayEncoder, KeyType, KeyProviderRegistry } from '@aries-framework/core' import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from '@hyperledger/indy-vdr-shared' import { genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' @@ -14,7 +14,7 @@ import { indyDidFromPublicKeyBase58 } from '../src/utils/did' import { indyVdrModuleConfig } from './helpers' const indyVdrPoolService = new IndyVdrPoolService(testLogger, indyVdrModuleConfig) -const wallet = new IndySdkWallet(indySdk, testLogger, new SigningProviderRegistry([])) +const wallet = new IndySdkWallet(indySdk, testLogger, new KeyProviderRegistry([])) const agentConfig = getAgentConfig('IndyVdrPoolService') const agentContext = getAgentContext({ wallet, agentConfig }) diff --git a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts index cc987e7888..705838f71f 100644 --- a/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-sov-did-resolver.e2e.test.ts @@ -146,7 +146,7 @@ describe('Indy VDR Sov DID Resolver', () => { accept: ['didcomm/v2'], routingKeys: ['a-routing-key'], serviceEndpoint: 'http://localhost:3000', - type: 'DIDComm', + type: 'DIDCommMessaging', }, ], }, diff --git a/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts index 4a21491d36..74f80ec4ff 100644 --- a/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts +++ b/packages/question-answer/src/__tests__/QuestionAnswerService.test.ts @@ -1,7 +1,7 @@ import type { AgentConfig, AgentContext, Repository, Wallet } from '@aries-framework/core' import type { QuestionAnswerStateChangedEvent, ValidResponse } from '@aries-framework/question-answer' -import { EventEmitter, SigningProviderRegistry, InboundMessageContext, DidExchangeState } from '@aries-framework/core' +import { EventEmitter, InboundMessageContext, DidExchangeState, KeyProviderRegistry } from '@aries-framework/core' import { agentDependencies } from '@aries-framework/node' import { Subject } from 'rxjs' @@ -61,7 +61,7 @@ describe('QuestionAnswerService', () => { beforeAll(async () => { agentConfig = getAgentConfig('QuestionAnswerServiceTest') - wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([])) + wallet = new IndySdkWallet(indySdk, agentConfig.logger, new KeyProviderRegistry([])) agentContext = getAgentContext() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await wallet.createAndOpen(agentConfig.walletConfig!) diff --git a/txn.txt b/txn.txt new file mode 100644 index 0000000000..0c5b0dcf42 --- /dev/null +++ b/txn.txt @@ -0,0 +1,4 @@ +{"reqSignature": {}, "txn": {"data": {"data": {"alias": "Node1", "blskey": "4N8aUNHSgjQVgkpm8nhNEfDf6txHznoYREg9kirmJrkivgL4oSEimFF6nsQ6M41QvhM2Z33nves5vfSn9n1UwNFJBYtWVnHYMATn76vLuL3zU88KyeAYcHfsih3He6UHcXDxcaecHVz6jhCYz1P2UZn2bDVruL5wXpehgBfBaLKm3Ba", "blskey_pop": "RahHYiCvoNCtPTrVtP7nMC5eTYrsUA8WjXbdhNc8debh1agE9bGiJxWBXYNFbnJXoXhWFMvyqhqhRoq737YQemH5ik9oL7R4NTTCz2LEZhkgLJzB3QRQqJyBNyv7acbdHrAT8nQ9UkLbaVL9NBpnWXBTw4LEMePaSHEw66RzPNdAX1", "client_ip": "127.0.0.1", "client_port": 9702, "node_ip": "127.0.0.1", "node_port": 9701, "services": ["VALIDATOR"]}, "dest": "Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv"}, "metadata": {"from": "Th7MpTaRZVRYnPiabds81Y"}, "type": "0"}, "txnMetadata": {"seqNo": 1, "txnId": "fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62"}, "ver": "1"} +{"reqSignature": {}, "txn": {"data": {"data": {"alias": "Node2", "blskey": "37rAPpXVoxzKhz7d9gkUe52XuXryuLXoM6P6LbWDB7LSbG62Lsb33sfG7zqS8TK1MXwuCHj1FKNzVpsnafmqLG1vXN88rt38mNFs9TENzm4QHdBzsvCuoBnPH7rpYYDo9DZNJePaDvRvqJKByCabubJz3XXKbEeshzpz4Ma5QYpJqjk", "blskey_pop": "Qr658mWZ2YC8JXGXwMDQTzuZCWF7NK9EwxphGmcBvCh6ybUuLxbG65nsX4JvD4SPNtkJ2w9ug1yLTj6fgmuDg41TgECXjLCij3RMsV8CwewBVgVN67wsA45DFWvqvLtu4rjNnE9JbdFTc1Z4WCPA3Xan44K1HoHAq9EVeaRYs8zoF5", "client_ip": "127.0.0.1", "client_port": 9704, "node_ip": "127.0.0.1", "node_port": 9703, "services": ["VALIDATOR"]}, "dest": "8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb"}, "metadata": {"from": "EbP4aYNeTHL6q385GuVpRV"}, "type": "0"}, "txnMetadata": {"seqNo": 2, "txnId": "1ac8aece2a18ced660fef8694b61aac3af08ba875ce3026a160acbc3a3af35fc"}, "ver": "1"} +{"reqSignature": {}, "txn": {"data": {"data": {"alias": "Node3", "blskey": "3WFpdbg7C5cnLYZwFZevJqhubkFALBfCBBok15GdrKMUhUjGsk3jV6QKj6MZgEubF7oqCafxNdkm7eswgA4sdKTRc82tLGzZBd6vNqU8dupzup6uYUf32KTHTPQbuUM8Yk4QFXjEf2Usu2TJcNkdgpyeUSX42u5LqdDDpNSWUK5deC5", "blskey_pop": "QwDeb2CkNSx6r8QC8vGQK3GRv7Yndn84TGNijX8YXHPiagXajyfTjoR87rXUu4G4QLk2cF8NNyqWiYMus1623dELWwx57rLCFqGh7N4ZRbGDRP4fnVcaKg1BcUxQ866Ven4gw8y4N56S5HzxXNBZtLYmhGHvDtk6PFkFwCvxYrNYjh", "client_ip": "127.0.0.1", "client_port": 9706, "node_ip": "127.0.0.1", "node_port": 9705, "services": ["VALIDATOR"]}, "dest": "DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya"}, "metadata": {"from": "4cU41vWW82ArfxJxHkzXPG"}, "type": "0"}, "txnMetadata": {"seqNo": 3, "txnId": "7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4"}, "ver": "1"} +{"reqSignature": {}, "txn": {"data": {"data": {"alias": "Node4", "blskey": "2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw", "blskey_pop": "RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP", "client_ip": "127.0.0.1", "client_port": 9708, "node_ip": "127.0.0.1", "node_port": 9707, "services": ["VALIDATOR"]}, "dest": "4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"}, "metadata": {"from": "TWwCRQRZ2ZHMJFn9TzLp7W"}, "type": "0"}, "txnMetadata": {"seqNo": 4, "txnId": "aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"}, "ver": "1"} diff --git a/yarn.lock b/yarn.lock index 5b6b66c9c0..4ad6bb13e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4784,6 +4784,16 @@ did-resolver@^4.0.0, did-resolver@^4.1.0: resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.1.0.tgz#740852083c4fd5bf9729d528eca5d105aff45eb6" integrity sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA== +didcomm-node@0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/didcomm-node/-/didcomm-node-0.4.1.tgz#85f8cd55cb637370d4c0528f06693df5719af700" + integrity sha512-5vR2T6AyFhw20LI3Hu7Xps1nYimpooEIbefweIAsZ964p8sSnWW07qulvnRwd1fMihtUaAK+DlwGkR4jZIf6Mg== + +didcomm@0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/didcomm/-/didcomm-0.4.1.tgz#f94923f6d7f00dbf27818a5965d9a432874cdf96" + integrity sha512-fcGSfLL5ri18vbbjRexHWVk2RAxZ/eBlyKeLCzS/3k5RdogqJbnUEcNsq64at1nA1u16ENa1bsZodGeRoq84ZA== + diff-sequences@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" From 1017f723d164119e707bec77be5b07839cfd1e23 Mon Sep 17 00:00:00 2001 From: Artemkaaas Date: Tue, 16 May 2023 17:26:56 +0300 Subject: [PATCH 139/139] Implemented DidComm V2 encryption in askar wallet Signed-off-by: Artemkaaas --- demo/src/Listener.ts | 21 +- packages/askar/src/wallet/AskarWallet.ts | 655 ++++++++++++++---- packages/askar/src/wallet/JweEnvelope.ts | 62 -- .../src/wallet/__tests__/packing.test.ts | 83 ++- packages/core/src/agent/Agent.ts | 8 +- packages/core/src/agent/EnvelopeService.ts | 254 ++++--- packages/core/src/agent/MessageReceiver.ts | 27 +- packages/core/src/agent/MessageSender.ts | 302 +++----- .../src/agent/__tests__/MessageSender.test.ts | 5 +- packages/core/src/agent/__tests__/stubs.ts | 5 +- .../crypto/key-provider/Ed25519KeyProvider.ts | 42 -- .../key-provider/KeyProviderRegistry.ts | 19 +- .../crypto/key-provider/X25519KeyProvider.ts | 41 -- packages/core/src/didcomm/JweEnvelope.ts | 200 ++++++ packages/core/src/didcomm/index.ts | 1 + packages/core/src/didcomm/types.ts | 1 + .../core/src/didcomm/versions/v1/index.ts | 11 +- .../didcomm/versions/v2/DidCommV2Message.ts | 12 +- .../core/src/didcomm/versions/v2/index.ts | 18 +- .../core/src/didcomm/versions/v2/types.ts | 16 + .../src/modules/connections/ConnectionsApi.ts | 4 +- .../protocols/trust-ping/TrustPingEvents.ts | 22 +- .../trust-ping/v2/V2TrustPingService.ts | 13 +- .../v2/handlers/TrustPingMessageHandler.ts | 6 +- .../services/DidCommDocumentService.ts | 18 +- .../src/modules/dids/domain/DidDocument.ts | 28 +- .../routing/messages/V2ForwardMessage.ts | 42 ++ .../src/modules/routing/messages/index.ts | 1 + packages/core/src/wallet/Wallet.ts | 27 +- packages/core/tests/TestMessage.ts | 13 +- packages/core/tests/mocks/MockWallet.ts | 4 - packages/didcomm-v2/README.md | 0 packages/didcomm-v2/integration-notes.md | 154 ---- packages/didcomm-v2/jest.config.ts | 13 - packages/didcomm-v2/package.json | 41 -- packages/didcomm-v2/src/DidCommV2Module.ts | 34 - packages/didcomm-v2/src/index.ts | 2 - .../src/services/DidCommV2DidResolver.ts | 93 --- .../src/services/DidCommV2EnvelopeService.ts | 104 --- .../src/services/DidCommV2SecretsResolver.ts | 39 -- packages/didcomm-v2/src/services/index.ts | 5 - .../tests/DidCommV2DidResolver.test.ts | 126 ---- .../tests/DidCommV2EnvelopeService.test.ts | 297 -------- .../didcomm-v2/tests/DidCommV2Module.test.ts | 28 - packages/didcomm-v2/tests/helpers.ts | 23 - packages/didcomm-v2/tests/setup.ts | 3 - packages/didcomm-v2/tsconfig.build.json | 0 packages/didcomm-v2/tsconfig.json | 6 - packages/indy-sdk/src/wallet/IndySdkWallet.ts | 56 +- yarn.lock | 10 - 50 files changed, 1352 insertions(+), 1643 deletions(-) delete mode 100644 packages/askar/src/wallet/JweEnvelope.ts delete mode 100644 packages/core/src/crypto/key-provider/Ed25519KeyProvider.ts delete mode 100644 packages/core/src/crypto/key-provider/X25519KeyProvider.ts create mode 100644 packages/core/src/didcomm/JweEnvelope.ts create mode 100644 packages/core/src/modules/routing/messages/V2ForwardMessage.ts delete mode 100644 packages/didcomm-v2/README.md delete mode 100644 packages/didcomm-v2/integration-notes.md delete mode 100644 packages/didcomm-v2/jest.config.ts delete mode 100644 packages/didcomm-v2/package.json delete mode 100644 packages/didcomm-v2/src/DidCommV2Module.ts delete mode 100644 packages/didcomm-v2/src/index.ts delete mode 100644 packages/didcomm-v2/src/services/DidCommV2DidResolver.ts delete mode 100644 packages/didcomm-v2/src/services/DidCommV2EnvelopeService.ts delete mode 100644 packages/didcomm-v2/src/services/DidCommV2SecretsResolver.ts delete mode 100644 packages/didcomm-v2/src/services/index.ts delete mode 100644 packages/didcomm-v2/tests/DidCommV2DidResolver.test.ts delete mode 100644 packages/didcomm-v2/tests/DidCommV2EnvelopeService.test.ts delete mode 100644 packages/didcomm-v2/tests/DidCommV2Module.test.ts delete mode 100644 packages/didcomm-v2/tests/helpers.ts delete mode 100644 packages/didcomm-v2/tests/setup.ts delete mode 100644 packages/didcomm-v2/tsconfig.build.json delete mode 100644 packages/didcomm-v2/tsconfig.json diff --git a/demo/src/Listener.ts b/demo/src/Listener.ts index 703e9a5ce9..4a4c7e383a 100644 --- a/demo/src/Listener.ts +++ b/demo/src/Listener.ts @@ -9,6 +9,8 @@ import type { CredentialStateChangedEvent, TrustPingReceivedEvent, TrustPingResponseReceivedEvent, + V2TrustPingReceivedEvent, + V2TrustPingResponseReceivedEvent, ProofExchangeRecord, ProofStateChangedEvent, } from '@aries-framework/core' @@ -83,12 +85,27 @@ export class Listener { public pingListener(agent: Agent, name: string) { agent.events.on(TrustPingEventTypes.TrustPingReceivedEvent, async (event: TrustPingReceivedEvent) => { - this.ui.updateBottomBar(purpleText(`\n${name} received ping message from ${event.payload}\n`)) + this.ui.updateBottomBar( + purpleText(`\n${name} received ping message from ${event.payload.connectionRecord?.theirDid}\n`) + ) }) agent.events.on( TrustPingEventTypes.TrustPingResponseReceivedEvent, async (event: TrustPingResponseReceivedEvent) => { - this.ui.updateBottomBar(purpleText(`\n${name} received ping response message from ${event.payload}\n`)) + this.ui.updateBottomBar( + purpleText(`\n${name} received ping response message from ${event.payload.connectionRecord?.theirDid}\n`) + ) + } + ) + agent.events.on(TrustPingEventTypes.V2TrustPingReceivedEvent, async (event: V2TrustPingReceivedEvent) => { + this.ui.updateBottomBar(purpleText(`\n${name} received ping message from ${event.payload.message.from}\n`)) + }) + agent.events.on( + TrustPingEventTypes.V2TrustPingResponseReceivedEvent, + async (event: V2TrustPingResponseReceivedEvent) => { + this.ui.updateBottomBar( + purpleText(`\n${name} received ping response message from ${event.payload.message.from}\n`) + ) } ) } diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 2ab8b9ef62..e2ddee9154 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -1,46 +1,60 @@ import type { EncryptedMessage, - WalletConfig, - WalletCreateKeyOptions, - WalletSignOptions, + KeyPair, UnpackedMessageContext, - WalletVerifyOptions, Wallet, + WalletConfig, WalletConfigRekey, - KeyPair, + WalletCreateKeyOptions, WalletExportImportConfig, WalletPackOptions, + WalletSignOptions, + WalletVerifyOptions, } from '@aries-framework/core' import type { KeyEntryObject, Session } from '@hyperledger/aries-askar-shared' import { - WalletExportPathExistsError, - WalletKeyExistsError, - isValidSeed, - isValidPrivateKey, - JsonTransformer, - WalletInvalidKeyError, - WalletDuplicateError, - JsonEncoder, - KeyType, - Buffer, AriesFrameworkError, - Logger, - WalletError, + Buffer, + DidCommMessageVersion, + DidCommV2EncryptionAlgs, + DidCommV2KeyProtectionAlgs, + DidCommV2Types, + DidKey, + FileSystem, InjectionSymbols, + isDidCommV1EncryptedEnvelope, + isValidPrivateKey, + isValidSeed, + JsonEncoder, + JsonTransformer, + JweEnvelope, + JweEnvelopeBuilder, + JweRecipient, Key, + KeyDerivationMethod, KeyProviderRegistry, + KeyType, + Logger, TypedArrayEncoder, - FileSystem, + WalletDuplicateError, + WalletError, + WalletExportPathExistsError, + WalletInvalidKeyError, + WalletKeyExistsError, WalletNotFoundError, - KeyDerivationMethod, } from '@aries-framework/core' -import { KeyAlgs, CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-shared' -// eslint-disable-next-line import/order +import { + CryptoBox, + Ecdh1PU, + EcdhEs, + Key as AskarKey, + keyAlgFromString, + KeyAlgs, + Store, +} from '@hyperledger/aries-askar-shared' +import { Jwk } from '@hyperledger/aries-askar-shared/build/crypto/Jwk' import BigNumber from 'bn.js' - -const isError = (error: unknown): error is Error => error instanceof Error - import { inject, injectable } from 'tsyringe' import { @@ -51,7 +65,7 @@ import { uriFromWalletConfig, } from '../utils' -import { JweEnvelope, JweRecipient } from './JweEnvelope' +const isError = (error: unknown): error is Error => error instanceof Error @injectable() export class AskarWallet implements Wallet { @@ -68,11 +82,11 @@ export class AskarWallet implements Wallet { public constructor( @inject(InjectionSymbols.Logger) logger: Logger, @inject(InjectionSymbols.FileSystem) fileSystem: FileSystem, - signingKeyProviderRegistry: KeyProviderRegistry + keyProviderRegistry: KeyProviderRegistry ) { this.logger = logger this.fileSystem = fileSystem - this.keyProviderRegistry = signingKeyProviderRegistry + this.keyProviderRegistry = keyProviderRegistry } public get isProvisioned() { @@ -366,7 +380,10 @@ export class AskarWallet implements Wallet { passKey: importKey, }) - await importedWalletStore.rekey({ keyMethod: importWalletConfig.keyMethod, passKey: importWalletConfig.passKey }) + await importedWalletStore.rekey({ + keyMethod: importWalletConfig.keyMethod, + passKey: importWalletConfig.passKey, + }) await importedWalletStore.close() } catch (error) { @@ -577,129 +594,348 @@ export class AskarWallet implements Wallet { } /** - * Pack a message using DIDComm V1 algorithm + * Pack a message using DIDComm V1 or DIDComm V2 encryption algorithms * * @param payload message to send * @param params packing options specific for envelop version * @returns JWE Envelope to send */ public async pack(payload: Record, params: WalletPackOptions): Promise { - if (params.version === 'v1') { - let cek: AskarKey | undefined - let senderKey: KeyEntryObject | null | undefined - let senderExchangeKey: AskarKey | undefined + if (params.didCommVersion === DidCommMessageVersion.V1) { + return this.packDidCommV1(payload, params) + } + if (params.didCommVersion === DidCommMessageVersion.V2) { + return this.packDidCommV2(payload, params) + } + throw new AriesFrameworkError(`Unsupported DidComm version: ${params.didCommVersion}`) + } - const { senderKey: senderVerkey, recipientKeys } = params + /** + * Pack a message using DIDComm V1 encryption algorithms + * + * @param payload message to send + * @param params packing options specific for envelop version + * @returns JWE Envelope to send + */ + private async packDidCommV1(payload: Record, params: WalletPackOptions): Promise { + const { senderKey: senderVerkey, recipientKeys } = params - try { - cek = AskarKey.generate(KeyAlgs.Chacha20C20P) + let cek: AskarKey | undefined + let senderKey: KeyEntryObject | null | undefined + let senderExchangeKey: AskarKey | undefined - senderKey = senderVerkey ? await this.session.fetchKey({ name: senderVerkey }) : undefined - if (senderVerkey && !senderKey) { - throw new WalletError(`Unable to pack message. Sender key ${senderVerkey} not found in wallet.`) - } + try { + cek = AskarKey.generate(KeyAlgs.Chacha20C20P) - senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined + const senderKid = senderVerkey?.publicKeyBase58 + senderKey = senderKid ? await this.session.fetchKey({ name: senderKid }) : undefined + if (senderVerkey && !senderKey) { + throw new WalletError(`Unable to pack message. Sender key ${senderVerkey} not found in wallet.`) + } - const recipients: JweRecipient[] = [] + senderExchangeKey = senderKey ? senderKey.key.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined - for (const recipientKey of recipientKeys) { - let targetExchangeKey: AskarKey | undefined - try { - targetExchangeKey = AskarKey.fromPublicBytes({ - publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, - algorithm: KeyAlgs.Ed25519, - }).convertkey({ algorithm: KeyAlgs.X25519 }) + const recipients: JweRecipient[] = [] - if (senderVerkey && senderExchangeKey) { - const encryptedSender = CryptoBox.seal({ - recipientKey: targetExchangeKey, - message: Buffer.from(senderVerkey), - }) - const nonce = CryptoBox.randomNonce() - const encryptedCek = CryptoBox.cryptoBox({ - recipientKey: targetExchangeKey, - senderKey: senderExchangeKey, - message: cek.secretBytes, - nonce, - }) + for (const recipientKey of recipientKeys) { + const recipientKid = recipientKey.publicKeyBase58 + let targetExchangeKey: AskarKey | undefined + try { + targetExchangeKey = AskarKey.fromPublicBytes({ + publicKey: Key.fromPublicKeyBase58(recipientKid, KeyType.Ed25519).publicKey, + algorithm: KeyAlgs.Ed25519, + }).convertkey({ algorithm: KeyAlgs.X25519 }) + + if (senderVerkey && senderExchangeKey && senderKid) { + const encryptedSender = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: Buffer.from(senderKid), + }) + const nonce = CryptoBox.randomNonce() + const encryptedCek = CryptoBox.cryptoBox({ + recipientKey: targetExchangeKey, + senderKey: senderExchangeKey, + message: cek.secretBytes, + nonce, + }) - recipients.push( - new JweRecipient({ - encryptedKey: encryptedCek, - header: { - kid: recipientKey, - sender: TypedArrayEncoder.toBase64URL(encryptedSender), - iv: TypedArrayEncoder.toBase64URL(nonce), - }, - }) - ) - } else { - const encryptedCek = CryptoBox.seal({ - recipientKey: targetExchangeKey, - message: cek.secretBytes, + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKid, + sender: TypedArrayEncoder.toBase64URL(encryptedSender), + iv: TypedArrayEncoder.toBase64URL(nonce), + }, }) - recipients.push( - new JweRecipient({ - encryptedKey: encryptedCek, - header: { - kid: recipientKey, - }, - }) - ) - } - } finally { - targetExchangeKey?.handle.free() + ) + } else { + const encryptedCek = CryptoBox.seal({ + recipientKey: targetExchangeKey, + message: cek.secretBytes, + }) + recipients.push( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKid, + }, + }) + ) } + } finally { + targetExchangeKey?.handle.free() } + } + + const protectedJson = { + enc: 'xchacha20poly1305_ietf', + typ: 'JWM/1.0', + alg: senderVerkey ? 'Authcrypt' : 'Anoncrypt', + recipients: recipients.map((item) => JsonTransformer.toJSON(item)), + } + + const { ciphertext, tag, nonce } = cek.aeadEncrypt({ + message: Buffer.from(JSON.stringify(payload)), + aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), + }).parts + + const envelope = new JweEnvelope({ + ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), + iv: TypedArrayEncoder.toBase64URL(nonce), + protected: JsonEncoder.toBase64URL(protectedJson), + tag: TypedArrayEncoder.toBase64URL(tag), + }).toJson() + + return envelope as EncryptedMessage + } finally { + cek?.handle.free() + senderKey?.key.handle.free() + senderExchangeKey?.handle.free() + } + } + + /** + * Pack a message using DIDComm V2 encryption algorithms + * + * @param payload message to send + * @param params packing options specific for envelop version + * @returns JWE Envelope to send + */ + private async packDidCommV2(payload: Record, params: WalletPackOptions): Promise { + if (params.senderKey) { + return this.encryptEcdh1Pu(payload, params.senderKey, params.recipientKeys) + } else { + return this.encryptEcdhEs(payload, params.recipientKeys) + } + } + + private async encryptEcdhEs(payload: Record, recipientKeys: Key[]): Promise { + const wrapId = DidCommV2KeyProtectionAlgs.EcdhEsA256Kw + const wrapAlg = KeyAlgs.AesA256Kw + const encId = DidCommV2EncryptionAlgs.XC20P + const encAlg = KeyAlgs.Chacha20XC20P + const keyAlg = KeyAlgs.X25519 + + let recipientX25519Key: AskarKey | undefined + let cek: AskarKey | undefined + let epk: AskarKey | undefined + + try { + // Generated once for all recipients + // https://identity.foundation/didcomm-messaging/spec/#ecdh-es-key-wrapping-and-common-protected-headers + epk = AskarKey.generate(keyAlg, true) + + const jweBuilder = new JweEnvelopeBuilder({ + typ: DidCommV2Types.EncryptedJson, + enc: encId, + alg: wrapId, + }) + .setEpk(JsonEncoder.toString(epk.jwkPublic)) + .setApv([...recipientKeys].map((recipientKey) => recipientKey.fingerprint)) + + // As per spec we firstly need to encrypt the payload and then use tag as part of the key derivation process + // https://identity.foundation/didcomm-messaging/spec/#ecdh-es-key-wrapping-and-common-protected-headers + cek = AskarKey.generate(encAlg) + + const { ciphertext, tag, nonce } = cek.aeadEncrypt({ + message: Buffer.from(JSON.stringify(payload)), + aad: jweBuilder.aad(), + }).parts + + for (const recipientKey of recipientKeys) { + try { + recipientX25519Key = AskarKey.fromPublicBytes({ + publicKey: recipientKey.publicKey, + algorithm: keyAlg, + }) - const protectedJson = { - enc: 'xchacha20poly1305_ietf', - typ: 'JWM/1.0', - alg: senderVerkey ? 'Authcrypt' : 'Anoncrypt', - recipients: recipients.map((item) => JsonTransformer.toJSON(item)), + // According to the spec `kid` MUST be a DID URI + // https://identity.foundation/didcomm-messaging/spec/#construction + const recipientDidKey = new DidKey(recipientKey).did + const recipientKid = `${recipientDidKey}#${recipientKey.fingerprint}` + + // Wrap the recipient key using ECDH-ES + // FIXME: according to the spec `tag` must be used for the wrapping but there is not such parameter + // https://identity.foundation/didcomm-messaging/spec/#ecdh-es-key-wrapping-and-common-protected-headers + const encryptedKey = new EcdhEs({ + algId: jweBuilder.alg(), + apu: jweBuilder.apu(), + apv: jweBuilder.apv(), + }).senderWrapKey({ + wrapAlg, + ephemeralKey: epk, + recipientKey: recipientX25519Key, + cek, + }).ciphertext + + jweBuilder.setRecipient( + new JweRecipient({ + encryptedKey, + header: { + kid: recipientKid, + }, + }) + ) + } finally { + recipientX25519Key?.handle.free() } + } - const { ciphertext, tag, nonce } = cek.aeadEncrypt({ - message: Buffer.from(JSON.stringify(payload)), - aad: Buffer.from(JsonEncoder.toBase64URL(protectedJson)), - }).parts + const jwe = jweBuilder.setCiphertext(ciphertext).setIv(nonce).setTag(tag).finalize() + return jwe.toJson() as EncryptedMessage + } finally { + epk?.handle.free() + cek?.handle.free() + } + } - const envelope = new JweEnvelope({ - ciphertext: TypedArrayEncoder.toBase64URL(ciphertext), - iv: TypedArrayEncoder.toBase64URL(nonce), - protected: JsonEncoder.toBase64URL(protectedJson), - tag: TypedArrayEncoder.toBase64URL(tag), - }).toJson() + private async encryptEcdh1Pu( + payload: Record, + senderKey: Key, + recipientKeys: Key[] + ): Promise { + const wrapAlg = KeyAlgs.AesA256Kw + const encAlg = KeyAlgs.AesA256CbcHs512 + const keyAlg = keyAlgFromString(senderKey.keyType) + + let senderAskarKey: KeyEntryObject | undefined | null + let recipientAskarKey: AskarKey | undefined + let cek: AskarKey | undefined + let epk: AskarKey | undefined - return envelope as EncryptedMessage - } finally { - cek?.handle.free() - senderKey?.key.handle.free() - senderExchangeKey?.handle.free() + try { + // currently, keys are stored in the wallet by their base58 representation + senderAskarKey = await this.session.fetchKey({ name: senderKey.publicKeyBase58 }) + if (!senderAskarKey) { + throw new WalletError(`Unable to pack message. Sender key ${senderKey} not found in wallet.`) } - } else { - throw new WalletError(`DIDComm V2 message packing is not supported`) + + // According to the spec `skid` MUST be a DID URI + const senderDidKey = new DidKey(senderKey).did + const senderKid = `${senderDidKey}#${senderKey.fingerprint}` + + // Generated once for all recipients + // https://identity.foundation/didcomm-messaging/spec/#ecdh-1pu-key-wrapping-and-common-protected-headers + epk = AskarKey.generate(keyAlg, true) + + const jweBuilder = new JweEnvelopeBuilder({ + typ: DidCommV2Types.EncryptedJson, + enc: DidCommV2EncryptionAlgs.A256CbcHs512, + alg: DidCommV2KeyProtectionAlgs.Ecdh1PuA256Kw, + }) + .setSkid(senderKid) + .setEpk(JsonEncoder.toString(epk.jwkPublic)) + .setApu(senderKid) + .setApv([...recipientKeys].map((recipientKey) => recipientKey.fingerprint)) + + // As per spec we firstly need to encrypt the payload and then use tag as part of the key derivation process + // https://identity.foundation/didcomm-messaging/spec/#ecdh-1pu-key-wrapping-and-common-protected-headers + cek = AskarKey.generate(encAlg) + + const message = Buffer.from(JSON.stringify(payload)) + const { ciphertext, tag, nonce } = cek.aeadEncrypt({ + message, + aad: jweBuilder.aad(), + }).parts + + for (const recipientKey of recipientKeys) { + try { + recipientAskarKey = AskarKey.fromPublicBytes({ + publicKey: recipientKey.publicKey, + algorithm: keyAlg, + }) + + // According to the spec `kid` MUST be a DID URI + // https://identity.foundation/didcomm-messaging/spec/#construction + const recipientDidKey = new DidKey(recipientKey).did + const recipientKid = `${recipientDidKey}#${recipientKey.fingerprint}` + + // Wrap the recipient key using ECDH-1PU + const encryptedCek = new Ecdh1PU({ + algId: jweBuilder.alg(), + apv: jweBuilder.apv(), + apu: jweBuilder.apu(), + }).senderWrapKey({ + wrapAlg, + cek, + ephemeralKey: epk, + ccTag: tag, + senderKey: senderAskarKey.key, + recipientKey: recipientAskarKey, + }).ciphertext + + jweBuilder.setRecipient( + new JweRecipient({ + encryptedKey: encryptedCek, + header: { + kid: recipientKid, + }, + }) + ) + } finally { + recipientAskarKey?.handle.free() + } + } + + const jwe = jweBuilder.setCiphertext(ciphertext).setIv(nonce).setTag(tag).finalize() + return jwe.toJson() as EncryptedMessage + } finally { + epk?.handle.free() + cek?.handle.free() + senderAskarKey?.key.handle.free() } } /** - * Unpacks a JWE Envelope coded using DIDComm V1 algorithm + * Unpacks a JWE Envelope coded using DIDComm V1 of DIDComm V2 encryption algorithms * * @param messagePackage JWE Envelope - * @returns UnpackedMessageContext with plain text message, sender key and recipient key + * + * @returns UnpackedMessageContext with plain text message, sender key, recipient key, and didcomm message version */ public async unpack(messagePackage: EncryptedMessage): Promise { - const protectedJson = JsonEncoder.fromBase64(messagePackage.protected) - - const alg = protectedJson.alg - if (!['Anoncrypt', 'Authcrypt'].includes(alg)) { - throw new WalletError(`Unsupported pack algorithm: ${alg}`) + if (isDidCommV1EncryptedEnvelope(messagePackage)) { + return this.unpackDidCommV1(messagePackage) + } else { + return this.unpackDidCommV2(messagePackage) } + } + + /** + * Unpacks a JWE Envelope coded using DIDComm V1 encryption algorithms + * + * @param messagePackage JWE Envelope + * + * @returns UnpackedMessageContext with plain text message, sender key, recipient key, and didcomm message version + */ + private async unpackDidCommV1(messagePackage: EncryptedMessage): Promise { + // Decode a message using DIDComm v1 encryption. + const protected_ = JsonEncoder.fromBase64(messagePackage.protected) const recipients = [] - for (const recip of protectedJson.recipients) { + for (const recip of protected_.recipients) { const kid = recip.header.kid if (!kid) { throw new WalletError('Blank recipient key') @@ -765,7 +1001,7 @@ export class AskarWallet implements Wallet { throw new WalletError('No corresponding recipient key found') } - if (!senderKey && alg === 'Authcrypt') { + if (!senderKey && protected_.alg === 'Authcrypt') { throw new WalletError('Sender public key not provided for Authcrypt') } @@ -779,15 +1015,196 @@ export class AskarWallet implements Wallet { aad: TypedArrayEncoder.fromString(messagePackage.protected), }) return { + didCommVersion: DidCommMessageVersion.V1, plaintextMessage: JsonEncoder.fromBuffer(message), - senderKey, recipientKey, + senderKey: senderKey ? Key.fromPublicKeyBase58(senderKey, KeyType.Ed25519) : undefined, } } finally { cek?.handle.free() } } + /** + * Unpacks a JWE Envelope coded using DIDComm V2 encryption algorithms + * + * @param messagePackage JWE Envelope + * + * @returns UnpackedMessageContext with plain text message, sender key, recipient key, and didcomm message version + */ + private async unpackDidCommV2(messagePackage: EncryptedMessage): Promise { + const protected_ = JsonEncoder.fromBase64(messagePackage.protected) + if ( + protected_.alg === DidCommV2KeyProtectionAlgs.EcdhEsA128Kw || + protected_.alg === DidCommV2KeyProtectionAlgs.EcdhEsA256Kw + ) { + return this.decryptEcdhEs(messagePackage, protected_) + } + if ( + protected_.alg === DidCommV2KeyProtectionAlgs.Ecdh1PuA128Kw || + protected_.alg === DidCommV2KeyProtectionAlgs.Ecdh1PuA256Kw + ) { + return this.decryptEcdh1Pu(messagePackage, protected_) + } + throw new AriesFrameworkError(`Unsupported JWE algorithm: ${protected_.alg}`) + } + + private async decryptEcdhEs(jwe: EncryptedMessage, protected_: any): Promise { + const { alg, apu, apv, enc } = protected_ + const wrapAlg = alg.slice(8) + + if (![DidCommV2KeyProtectionAlgs.EcdhEsA128Kw, DidCommV2KeyProtectionAlgs.EcdhEsA256Kw].includes(alg)) { + throw new AriesFrameworkError(`Unsupported ECDH-ES algorithm: ${alg}`) + } + if (!['A128GCM', 'A256GCM', 'A128CBC-HS256', 'A256CBC-HS512', 'XC20P'].includes(enc)) { + throw new AriesFrameworkError(`Unsupported ECDH-ES content encryption: ${alg}`) + } + + let recipientAskarKey: KeyEntryObject | null | undefined + let cek: AskarKey | undefined + let epk: AskarKey | undefined + + try { + // Generated once for all recipients + // https://identity.foundation/didcomm-messaging/spec/#ecdh-es-key-wrapping-and-common-protected-headers + epk = AskarKey.fromJwk({ jwk: Jwk.fromString(protected_.epk) }) + + for (const recipient of jwe.recipients) { + try { + // currently, keys are stored in the wallet by their base58 representation + const recipientKey = Key.fromPublicKeyId(recipient.header.kid) + recipientAskarKey = await this.session.fetchKey({ name: recipientKey.publicKeyBase58 }) + if (!recipientAskarKey) continue + + // unwrap the key using ECDH-ES + cek = new EcdhEs({ + algId: Uint8Array.from(Buffer.from(alg)), + apv: Uint8Array.from(Buffer.from(apv ?? [])), + apu: Uint8Array.from(Buffer.from(apu ?? [])), + }).receiverUnwrapKey({ + wrapAlg, + encAlg: enc, + ephemeralKey: epk, + recipientKey: recipientAskarKey.key, + ciphertext: TypedArrayEncoder.fromBase64(recipient.encrypted_key), + // tag: TypedArrayEncoder.fromBase64(jwe.tag), + }) + + // decrypt the message using the key + const plaintext = cek.aeadDecrypt({ + ciphertext: TypedArrayEncoder.fromBase64(jwe.ciphertext), + nonce: TypedArrayEncoder.fromBase64(jwe.iv), + tag: TypedArrayEncoder.fromBase64(jwe.tag), + aad: TypedArrayEncoder.fromString(jwe.protected), + }) + + return { + didCommVersion: DidCommMessageVersion.V2, + plaintextMessage: JsonEncoder.fromBuffer(plaintext), + recipientKey, + } + } finally { + recipientAskarKey?.key.handle.free() + cek?.handle.free() + } + } + } finally { + epk?.handle.free() + } + + throw new AriesFrameworkError('Unable to decrypt message') + } + + private async decryptEcdh1Pu(jwe: EncryptedMessage, protected_: any): Promise { + const { alg, enc, apu, apv, skid } = protected_ + const wrapAlg = alg.slice(9) + + if (![DidCommV2KeyProtectionAlgs.Ecdh1PuA128Kw, DidCommV2KeyProtectionAlgs.Ecdh1PuA256Kw].includes(alg)) { + throw new AriesFrameworkError(`Unsupported ECDH-1PU algorithm: ${alg}`) + } + if (!['A128CBC-HS256', 'A256CBC-HS512'].includes(enc)) { + throw new AriesFrameworkError(`Unsupported ECDH-1PU content encryption: ${alg}`) + } + + let recipientAskarKey: KeyEntryObject | null | undefined + let senderAskarKey: AskarKey | undefined + let cek: AskarKey | undefined + let epk: AskarKey | undefined + + try { + // Validate the `apu` filed is similar to `skid` + // https://identity.foundation/didcomm-messaging/spec/#ecdh-1pu-key-wrapping-and-common-protected-headers + const senderKidApu = TypedArrayEncoder.fromBase64(apu).toString('utf-8') + if (senderKidApu && skid && senderKidApu !== skid) { + throw new AriesFrameworkError('Mismatch between skid and apu') + } + const senderKid = skid ?? senderKidApu + if (!senderKid) { + throw new AriesFrameworkError('Sender key ID not provided') + } + + // FIXME: Properly, we need to properly resolve sender key doing the following steps: + // 1. Extract a DID from DID URL + // 2. Resolve DID Doc for sender + // 3. Get matching the ID + // So it looks like we need to use DidResolver inside of the wallet + const senderKey = Key.fromPublicKeyId(senderKid) + senderAskarKey = AskarKey.fromPublicBytes({ + publicKey: senderKey.publicKey, + algorithm: keyAlgFromString(senderKey.keyType), + }) + + // Generated once for all recipients + epk = AskarKey.fromJwk({ jwk: Jwk.fromString(protected_.epk) }) + + for (const recipient of jwe.recipients) { + try { + // currently, keys are stored in the wallet by their base58 representation + const recipientKey = Key.fromPublicKeyId(recipient.header.kid) + recipientAskarKey = await this.session.fetchKey({ name: recipientKey.publicKeyBase58 }) + if (!recipientAskarKey) continue + + // unwrap the key using ECDH-1PU + cek = new Ecdh1PU({ + apv: Uint8Array.from(Buffer.from(apv)), + apu: Uint8Array.from(Buffer.from(apu)), + algId: Uint8Array.from(Buffer.from(alg)), + }).receiverUnwrapKey({ + wrapAlg: wrapAlg, + encAlg: enc, + ephemeralKey: epk, + senderKey: senderAskarKey, + recipientKey: recipientAskarKey.key, + ccTag: TypedArrayEncoder.fromBase64(jwe.tag), + ciphertext: TypedArrayEncoder.fromBase64(recipient.encrypted_key), + }) + + // decrypt the message using the key + const plaintext = cek.aeadDecrypt({ + ciphertext: TypedArrayEncoder.fromBase64(jwe.ciphertext), + nonce: TypedArrayEncoder.fromBase64(jwe.iv), + tag: TypedArrayEncoder.fromBase64(jwe.tag), + aad: TypedArrayEncoder.fromString(jwe.protected), + }) + + return { + didCommVersion: DidCommMessageVersion.V2, + plaintextMessage: JsonEncoder.fromBuffer(plaintext), + senderKey, + recipientKey, + } + } finally { + cek?.handle.free() + recipientAskarKey?.key?.handle.free() + } + } + } finally { + senderAskarKey?.handle.free() + epk?.handle.free() + } + throw new AriesFrameworkError('Unable to decrypt didcomm v2 envelop') + } + public async generateNonce(): Promise { try { // generate an 80-bit nonce suitable for AnonCreds proofs @@ -828,7 +1245,7 @@ export class AskarWallet implements Wallet { } } - public async retrieveKeyPair(publicKeyBase58: string): Promise { + private async retrieveKeyPair(publicKeyBase58: string): Promise { try { const entryObject = await this.session.fetch({ category: 'KeyPairRecord', name: `key-${publicKeyBase58}` }) diff --git a/packages/askar/src/wallet/JweEnvelope.ts b/packages/askar/src/wallet/JweEnvelope.ts deleted file mode 100644 index ac4d791f89..0000000000 --- a/packages/askar/src/wallet/JweEnvelope.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { JsonTransformer, TypedArrayEncoder } from '@aries-framework/core' -import { Expose, Type } from 'class-transformer' - -export class JweRecipient { - @Expose({ name: 'encrypted_key' }) - public encryptedKey!: string - public header?: Record - - public constructor(options: { encryptedKey: Uint8Array; header?: Record }) { - if (options) { - this.encryptedKey = TypedArrayEncoder.toBase64URL(options.encryptedKey) - - this.header = options.header - } - } -} - -export interface JweEnvelopeOptions { - protected: string - unprotected?: string - recipients?: JweRecipient[] - ciphertext: string - iv: string - tag: string - aad?: string - header?: string[] - encryptedKey?: string -} - -export class JweEnvelope { - public protected!: string - public unprotected?: string - - @Type(() => JweRecipient) - public recipients?: JweRecipient[] - public ciphertext!: string - public iv!: string - public tag!: string - public aad?: string - public header?: string[] - - @Expose({ name: 'encrypted_key' }) - public encryptedKey?: string - - public constructor(options: JweEnvelopeOptions) { - if (options) { - this.protected = options.protected - this.unprotected = options.unprotected - this.recipients = options.recipients - this.ciphertext = options.ciphertext - this.iv = options.iv - this.tag = options.tag - this.aad = options.aad - this.header = options.header - this.encryptedKey = options.encryptedKey - } - } - - public toJson() { - return JsonTransformer.toJSON(this) - } -} diff --git a/packages/askar/src/wallet/__tests__/packing.test.ts b/packages/askar/src/wallet/__tests__/packing.test.ts index 215faff9ca..7c6bc0606c 100644 --- a/packages/askar/src/wallet/__tests__/packing.test.ts +++ b/packages/askar/src/wallet/__tests__/packing.test.ts @@ -1,6 +1,13 @@ import type { WalletConfig, WalletPackOptions } from '@aries-framework/core' -import { JsonTransformer, BasicMessage, KeyType, KeyProviderRegistry, KeyDerivationMethod } from '@aries-framework/core' +import { + BasicMessage, + DidCommMessageVersion, + JsonTransformer, + KeyDerivationMethod, + KeyProviderRegistry, + KeyType, +} from '@aries-framework/core' import { describeRunInNodeVersion } from '../../../../../tests/runInVersion' import { agentDependencies } from '../../../../core/tests/helpers' @@ -15,6 +22,8 @@ const walletConfig: WalletConfig = { keyDerivationMethod: KeyDerivationMethod.Raw, } +const message = new BasicMessage({ content: 'hello' }) + describeRunInNodeVersion([18], 'askarWallet packing', () => { let askarWallet: AskarWallet @@ -27,23 +36,69 @@ describeRunInNodeVersion([18], 'askarWallet packing', () => { await askarWallet.delete() }) - test('DIDComm V1 packing and unpacking', async () => { - // Create both sender and recipient keys - const senderKey = await askarWallet.createKey({ keyType: KeyType.Ed25519 }) - const recipientKey = await askarWallet.createKey({ keyType: KeyType.Ed25519 }) + describe('DIDComm V1 packing and unpacking', () => { + test('Authcrypt', async () => { + // Create both sender and recipient keys + const senderKey = await askarWallet.createKey({ keyType: KeyType.Ed25519 }) + const recipientKey = await askarWallet.createKey({ keyType: KeyType.Ed25519 }) + + const params: WalletPackOptions = { + didCommVersion: DidCommMessageVersion.V1, + recipientKeys: [recipientKey], + senderKey: senderKey, + } + + const encryptedMessage = await askarWallet.pack(message.toJSON(), params) + const plainTextMessage = await askarWallet.unpack(encryptedMessage) + expect(JsonTransformer.fromJSON(plainTextMessage.plaintextMessage, BasicMessage)).toEqual(message) + }) + + test('Anoncrypt', async () => { + // Create recipient keys only + const recipientKey = await askarWallet.createKey({ keyType: KeyType.Ed25519 }) + + const params: WalletPackOptions = { + didCommVersion: DidCommMessageVersion.V1, + recipientKeys: [recipientKey], + senderKey: null, + } + + const encryptedMessage = await askarWallet.pack(message.toJSON(), params) + const plainTextMessage = await askarWallet.unpack(encryptedMessage) + expect(JsonTransformer.fromJSON(plainTextMessage.plaintextMessage, BasicMessage)).toEqual(message) + }) + }) + + describe('DIDComm V2 packing and unpacking', () => { + test('Authcrypt', async () => { + // Create both sender and recipient keys + const senderKey = await askarWallet.createKey({ keyType: KeyType.X25519 }) + const recipientKey = await askarWallet.createKey({ keyType: KeyType.X25519 }) - const message = new BasicMessage({ content: 'hello' }) + const params: WalletPackOptions = { + didCommVersion: DidCommMessageVersion.V2, + recipientKeys: [recipientKey], + senderKey: senderKey, + } - const params: WalletPackOptions = { - version: 'v1', - recipientKeys: [recipientKey.publicKeyBase58], - senderKey: senderKey.publicKeyBase58, - } + const encryptedMessage = await askarWallet.pack(message.toJSON(), params) + const plainTextMessage = await askarWallet.unpack(encryptedMessage) + expect(JsonTransformer.fromJSON(plainTextMessage.plaintextMessage, BasicMessage)).toEqual(message) + }) - const encryptedMessage = await askarWallet.pack(message.toJSON(), params) + test('Anoncrypt', async () => { + // Create recipient keys only + const recipientKey = await askarWallet.createKey({ keyType: KeyType.X25519 }) - const plainTextMessage = await askarWallet.unpack(encryptedMessage) + const params: WalletPackOptions = { + didCommVersion: DidCommMessageVersion.V2, + recipientKeys: [recipientKey], + senderKey: null, + } - expect(JsonTransformer.fromJSON(plainTextMessage.plaintextMessage, BasicMessage)).toEqual(message) + const encryptedMessage = await askarWallet.pack(message.toJSON(), params) + const plainTextMessage = await askarWallet.unpack(encryptedMessage) + expect(JsonTransformer.fromJSON(plainTextMessage.plaintextMessage, BasicMessage)).toEqual(message) + }) }) }) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index 8f4929f298..9ff83cbb55 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -13,7 +13,6 @@ import { concatMap, takeUntil } from 'rxjs/operators' import { InjectionSymbols } from '../constants' import { KeyProviderToken } from '../crypto' import { JwsService } from '../crypto/JwsService' -import { X25519KeyProvider } from '../crypto/key-provider/X25519KeyProvider' import { AriesFrameworkError } from '../error' import { DependencyManager } from '../plugins' import { DidCommMessageRepository, StorageUpdateService, StorageVersionRepository } from '../storage' @@ -62,7 +61,12 @@ export class Agent extends BaseAge dependencyManager.registerSingleton(StorageVersionRepository) dependencyManager.registerSingleton(StorageUpdateService) - dependencyManager.registerInstance(KeyProviderToken, new X25519KeyProvider()) + // This is a really ugly hack to make tsyringe work without any SigningProviders registered + // It is currently impossible to use @injectAll if there are no instances registered for the + // token. We register a value of `default` by default and will filter that out in the registry. + // Once we have a signing provider that should always be registered we can remove this. We can make an ed25519 + // signer using the @stablelib/ed25519 library. + dependencyManager.registerInstance(KeyProviderToken, 'default') dependencyManager.registerInstance(AgentConfig, agentConfig) dependencyManager.registerInstance(InjectionSymbols.AgentDependencies, agentConfig.agentDependencies) diff --git a/packages/core/src/agent/EnvelopeService.ts b/packages/core/src/agent/EnvelopeService.ts index fb15215956..8e56215851 100644 --- a/packages/core/src/agent/EnvelopeService.ts +++ b/packages/core/src/agent/EnvelopeService.ts @@ -1,16 +1,23 @@ import type { AgentMessage } from './AgentMessage' import type { AgentContext } from './context' -import type { DecryptedMessageContext, EncryptedMessage } from '../didcomm/types' -import type { PackMessageParams as DidCommV1PackMessageParams } from '../didcomm/versions/v1' -import type { V2PackMessageParams as DidCommV2PackMessageParams } from '../didcomm/versions/v2' -import type { WalletPackV1Options, WalletPackV2Options } from '../wallet/Wallet' +import type { Key } from '../crypto' +import type { + DidCommV1PackMessageParams, + DidCommV2PackMessageParams, + DecryptedMessageContext, + EncryptedMessage, +} from '../didcomm' +import type { DidDocument } from '../modules/dids' +import type { WalletPackOptions } from '../wallet/Wallet' import { InjectionSymbols } from '../constants' -import { Key, KeyType } from '../crypto' +import { V2Attachment } from '../decorators/attachment' +import { V2AttachmentData } from '../decorators/attachment/V2Attachment' import { DidCommMessageVersion } from '../didcomm/types' import { AriesFrameworkError } from '../error' import { Logger } from '../logger' -import { ForwardMessage } from '../modules/routing/messages' +import { DidResolverService, getAgreementKeys, keyReferenceToKey } from '../modules/dids' +import { ForwardMessage, V2ForwardMessage } from '../modules/routing/messages' import { inject, injectable } from '../plugins' export type PackMessageParams = DidCommV1PackMessageParams | DidCommV2PackMessageParams @@ -18,104 +25,193 @@ export type PackMessageParams = DidCommV1PackMessageParams | DidCommV2PackMessag @injectable() export class EnvelopeService { private logger: Logger + private didResolverService: DidResolverService - public constructor(@inject(InjectionSymbols.Logger) logger: Logger) { + public constructor(@inject(InjectionSymbols.Logger) logger: Logger, didResolverService: DidResolverService) { this.logger = logger + this.didResolverService = didResolverService } public async packMessage( agentContext: AgentContext, message: AgentMessage, - params: DidCommV1PackMessageParams | DidCommV2PackMessageParams + params: PackMessageParams ): Promise { + if (message.didCommVersion === DidCommMessageVersion.V1) { + return this.packDIDCommV1Message(agentContext, message, params as DidCommV1PackMessageParams) + } + if (message.didCommVersion === DidCommMessageVersion.V2) { + return this.packDIDCommV2Message(agentContext, message, params as DidCommV2PackMessageParams) + } + throw new AriesFrameworkError(`Unexpected pack DIDComm message params: ${params}`) + } + + public async unpackMessage( + agentContext: AgentContext, + encryptedMessage: EncryptedMessage + ): Promise { + return await agentContext.wallet.unpack(encryptedMessage) + } + + private async packDIDCommV1Message( + agentContext: AgentContext, + message: AgentMessage, + params: DidCommV1PackMessageParams + ): Promise { + const { recipientKeys, senderKey } = params + // pass whether we want to use legacy did sov prefix const unboundMessage = message.toJSON({ useDidSovPrefixWhereAllowed: agentContext.config.useDidSovPrefixWhereAllowed, }) - if (message.didCommVersion === DidCommMessageVersion.V1) { - const { recipientKeys, routingKeys, senderKey } = params as DidCommV1PackMessageParams - let recipientKeysBase58 = recipientKeys.map((key) => key.publicKeyBase58) - const routingKeysBase58 = routingKeys.map((key) => key.publicKeyBase58) - const senderKeyBase58 = senderKey && senderKey.publicKeyBase58 + this.logger.debug(`Pack outbound message ${unboundMessage['@type']}`) - this.logger.debug(`Pack outbound message ${unboundMessage['@type']}`) + // Forward messages are anon packed + const packParams: WalletPackOptions = { + didCommVersion: DidCommMessageVersion.V1, + senderKey, + recipientKeys, + } + const encryptedMessage = await agentContext.wallet.pack(unboundMessage, packParams) + return await this.wrapDIDCommV1MessageInForward(agentContext, encryptedMessage, params) + } - // Forward messages are anon packed - const packParams: WalletPackV1Options = { - version: 'v1', - senderKey: senderKeyBase58 ?? null, - recipientKeys: recipientKeysBase58, - } - let encryptedMessage = await agentContext.wallet.pack(unboundMessage, packParams) - - // If the message has routing keys (mediator) pack for each mediator - for (const routingKey of routingKeysBase58) { - const forwardMessage = new ForwardMessage({ - // Forward to first recipient key - to: recipientKeysBase58[0], - message: encryptedMessage, - }) - recipientKeysBase58 = [routingKey] - this.logger.debug('Forward message created', forwardMessage) - - const forwardJson = forwardMessage.toJSON({ - useDidSovPrefixWhereAllowed: agentContext.config.useDidSovPrefixWhereAllowed, - }) - - // Forward messages are anon packed - const forwardParams: WalletPackV1Options = { - version: 'v1', - senderKey: null, - recipientKeys: recipientKeysBase58, - } - encryptedMessage = await agentContext.wallet.pack(forwardJson, forwardParams) - } + private async wrapDIDCommV1MessageInForward( + agentContext: AgentContext, + encryptedMessage: EncryptedMessage, + params: DidCommV1PackMessageParams + ): Promise { + const { recipientKeys, routingKeys } = params + if (!recipientKeys.length || !routingKeys.length) { return encryptedMessage } - if (message.didCommVersion === DidCommMessageVersion.V2) { - const { fromDid, toDid } = params as DidCommV2PackMessageParams - const packParams: WalletPackV2Options = { - version: 'v2', - fromDid, - toDid, + + // If the message has routing keys (mediator) pack for each mediator + let to = recipientKeys[0].publicKeyBase58 + for (const routingKey of routingKeys) { + const forwardMessage = new ForwardMessage({ + to, + message: encryptedMessage, + }) + to = routingKey.publicKeyBase58 + this.logger.debug('Forward message created', forwardMessage) + + const forwardJson = forwardMessage.toJSON({ + useDidSovPrefixWhereAllowed: agentContext.config.useDidSovPrefixWhereAllowed, + }) + + // Forward messages are anon packed + const forwardParams: WalletPackOptions = { + didCommVersion: DidCommMessageVersion.V1, + recipientKeys: [routingKey], } - return await agentContext.wallet.pack(unboundMessage, packParams) + encryptedMessage = await agentContext.wallet.pack(forwardJson, forwardParams) } - throw new AriesFrameworkError(`Unexpected pack DIDComm message params: ${params}`) + + return encryptedMessage } - public async unpackMessage( + private async packDIDCommV2Message( agentContext: AgentContext, - encryptedMessage: EncryptedMessage - ): Promise { - const decryptedMessage = await agentContext.wallet.unpack(encryptedMessage) - const { recipientKey, senderKey, plaintextMessage } = decryptedMessage - return { - recipientKey: recipientKey ? Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519) : undefined, - senderKey: senderKey ? Key.fromPublicKeyBase58(senderKey, KeyType.Ed25519) : undefined, - plaintextMessage, + message: AgentMessage, + params: DidCommV2PackMessageParams + ): Promise { + const { recipientDidDoc, senderDidDoc } = params + const { senderKey, recipientKey } = EnvelopeService.findCommonSupportedEncryptionKeys(recipientDidDoc, senderDidDoc) + if (!recipientKey) { + throw new AriesFrameworkError( + `Unable to pack message ${message.id} because there is no any commonly supported key types to encrypt message` + ) + } + const unboundMessage = message.toJSON() + const packParams: WalletPackOptions = { + didCommVersion: DidCommMessageVersion.V2, + recipientKeys: [recipientKey], + senderKey, + } + const encryptedMessage = await agentContext.wallet.pack(unboundMessage, packParams) + return await this.wrapDIDCommV2MessageInForward(agentContext, encryptedMessage, params) + } + + private async wrapDIDCommV2MessageInForward( + agentContext: AgentContext, + encryptedMessage: EncryptedMessage, + params: DidCommV2PackMessageParams + ): Promise { + const { recipientDidDoc, service } = params + if (!recipientDidDoc) { + return encryptedMessage + } + + const routings: { did: string; key: Key }[] = [] + for (const routingKey of service.routingKeys ?? []) { + const routingDidDocument = await this.didResolverService.resolveDidDocument(agentContext, routingKey) + routings.push({ + did: routingDidDocument.id, + key: keyReferenceToKey(routingDidDocument, routingKey), + }) + } + + if (!routings.length) { + return encryptedMessage + } + + // If the message has routing keys (mediator) pack for each mediator + let next = recipientDidDoc.id + for (const routing of routings) { + const forwardMessage = new V2ForwardMessage({ + to: [routing.did], + body: { next }, + attachments: [ + new V2Attachment({ + data: new V2AttachmentData({ json: encryptedMessage }), + }), + ], + }) + next = routing.did + this.logger.debug('Forward message created', forwardMessage) + + const forwardJson = forwardMessage.toJSON() + + // Forward messages are anon packed + const forwardParams: WalletPackOptions = { + didCommVersion: DidCommMessageVersion.V2, + recipientKeys: [routing.key], + } + encryptedMessage = await agentContext.wallet.pack(forwardJson, forwardParams) } - // if (isEncryptedMessage(message)) { - // return this.unpackJwe(agentContext, message) - // } - // if (isSignedMessage(message)) { - // return this.unpackJws(agentContext, message) - // } - // throw new AriesFrameworkError(`Unexpected message!`) + return encryptedMessage } - // private async unpackJwe(agentContext: AgentContext, message: EncryptedMessage): Promise { - // if (isDidCommV1EncryptedEnvelope(message)) { - // return this.didCommV1EnvelopeService.unpackMessage(agentContext, message) - // } else { - // return this.getDidCommV2EnvelopeService().unpackMessage(agentContext, message) - // } - // } - // - // private async unpackJws(agentContext: AgentContext, message: SignedMessage): Promise { - // return this.getDidCommV2EnvelopeService().unpackMessage(agentContext, message) - // } + private static findCommonSupportedEncryptionKeys(recipientDidDocument: DidDocument, senderDidDocument?: DidDocument) { + const recipientAgreementKeys = getAgreementKeys(recipientDidDocument) + + if (!senderDidDocument) { + return { senderKey: undefined, recipientKey: recipientAgreementKeys[0] } + } + + const senderAgreementKeys = getAgreementKeys(senderDidDocument) + + let senderKey: Key | undefined + let recipientKey: Key | undefined + + for (const senderAgreementKey of senderAgreementKeys) { + for (const recipientAgreementKey of recipientAgreementKeys) { + if (senderAgreementKey.keyType === recipientAgreementKey.keyType) { + senderKey = senderAgreementKey + recipientKey = recipientAgreementKey + break + } + } + if (senderKey) break + } + + return { + senderKey, + recipientKey, + } + } } diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 7bae2af42c..919db39944 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -1,14 +1,14 @@ import type { AgentMessage } from './AgentMessage' import type { TransportSession } from './TransportService' import type { AgentContext } from './context' -import type { EncryptedMessage, PlaintextMessage, SignedMessage } from '../didcomm' +import type { EncryptedMessage, PlaintextMessage } from '../didcomm' import type { DecryptedMessageContext } from '../didcomm/types' import type { ConnectionRecord } from '../modules/connections' import type { InboundTransport } from '../transport' import { InjectionSymbols } from '../constants' import { isPlaintextMessageV1, isPlaintextMessageV2 } from '../didcomm' -import { getPlaintextMessageType, isEncryptedMessage, isPlaintextMessage, isSignedMessage } from '../didcomm/helpers' +import { getPlaintextMessageType, isEncryptedMessage, isPlaintextMessage } from '../didcomm/helpers' import { AriesFrameworkError } from '../error' import { Logger } from '../logger' import { ConnectionService } from '../modules/connections' @@ -102,8 +102,6 @@ export class MessageReceiver { try { if (isEncryptedMessage(inboundMessage)) { return await this.receiveEncryptedMessage(agentContext, inboundMessage, session) - } else if (isSignedMessage(inboundMessage)) { - return await this.receiveSignedMessage(agentContext, inboundMessage, session) } else if (isPlaintextMessage(inboundMessage)) { await this.receivePlaintextMessage(agentContext, inboundMessage, connection) } else { @@ -134,16 +132,6 @@ export class MessageReceiver { return this.processUnpackedMessage(agentContext, unpackedMessage, session) } - private async receiveSignedMessage( - agentContext: AgentContext, - packedMessage: SignedMessage, - session?: TransportSession - ) { - // FIXME - // const unpackedMessage = await this.envelopeService.unpackMessage(agentContext, packedMessage) - // return this.processUnpackedMessage(agentContext, undefined, session) - } - private async processUnpackedMessage( agentContext: AgentContext, unpackedMessage: DecryptedMessageContext, @@ -243,10 +231,15 @@ export class MessageReceiver { }) } if (isPlaintextMessageV2(decryptedMessageContext.plaintextMessage)) { - // Try to find the did records that holds the sender and recipient keys - const { from } = decryptedMessageContext.plaintextMessage + // Try to find the did records that hold the sender and recipient did's + const { from, to } = decryptedMessageContext.plaintextMessage + if (!from) return null - return this.connectionService.findByTheirDid(agentContext, from) + const connection = this.connectionService.findByTheirDid(agentContext, from) + if (connection) return connection + + if (!to?.length) return null + return this.connectionService.findByOurDid(agentContext, to[0]) } return null diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 1f8a051aa8..d15b79feaf 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -1,26 +1,29 @@ import type { AgentMessage } from './AgentMessage' +import type { PackMessageParams } from './EnvelopeService' import type { AgentMessageSentEvent } from './Events' import type { TransportSession } from './TransportService' import type { AgentContext } from './context' -import type { EncryptedMessage, OutboundPackage, OutboundPackagePayload } from '../didcomm/types' -import type { DidCommV1Message, PackMessageParams as DidCommV1PackMessageParams } from '../didcomm/versions/v1' -import type { V2PackMessageParams as DidCommV2PackMessageParams } from '../didcomm/versions/v2' -import type { DidCommV2Message } from '../didcomm/versions/v2/DidCommV2Message' +import type { + DidCommV1Message, + DidCommV2Message, + DidCommV2PackMessageParams, + EncryptedMessage, + EnvelopeType, + OutboundPackage, +} from '../didcomm' import type { ConnectionRecord } from '../modules/connections' import type { ResolvedDidCommService } from '../modules/didcomm' -import type { DidCommV2Service, DidDocument, DidDocumentService } from '../modules/dids' +import type { DidDocument } from '../modules/dids' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundTransport } from '../transport/OutboundTransport' import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' -import { EnvelopeType } from '../didcomm/types' -import { isDidCommV1Message } from '../didcomm/versions/v1' -import { isDidCommV2Message } from '../didcomm/versions/v2' +import { isDidCommV1Message, isDidCommV2Message } from '../didcomm/' import { AriesFrameworkError, MessageSendingError } from '../error' import { Logger } from '../logger' import { DidCommDocumentService } from '../modules/didcomm/services/DidCommDocumentService' -import { getKeyFromVerificationMethod } from '../modules/dids/domain/key-type' +import { DidCommV2Service, getAuthenticationKeys } from '../modules/dids' import { didKeyToInstanceOfKey } from '../modules/dids/helpers' import { DidResolverService } from '../modules/dids/services/DidResolverService' import { inject, injectable } from '../plugins' @@ -82,6 +85,21 @@ export class MessageSender { await outboundTransport.stop() } + public async sendMessage( + outboundMessage: OutboundMessageContext, + options?: { + transportPriority?: TransportPriorityOptions + } + ) { + if (isDidCommV1Message(outboundMessage.message)) { + return this.sendDIDCommV1Message(outboundMessage, options) + } + if (isDidCommV2Message(outboundMessage.message)) { + return this.sendDIDCommV2Message(outboundMessage, options) + } + throw new AriesFrameworkError(`Unexpected case`) + } + public async packMessage( agentContext: AgentContext, { @@ -89,7 +107,7 @@ export class MessageSender { message, endpoint, }: { - params: DidCommV1PackMessageParams + params: PackMessageParams message: AgentMessage endpoint: string } @@ -193,21 +211,6 @@ export class MessageSender { throw new AriesFrameworkError(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`) } - public async sendMessage( - outboundMessage: OutboundMessageContext, - options?: { - transportPriority?: TransportPriorityOptions - } - ) { - if (isDidCommV1Message(outboundMessage.message)) { - return this.sendDIDCommV1Message(outboundMessage, options) - } - if (isDidCommV2Message(outboundMessage.message)) { - return this.sendDIDCommV2Message(outboundMessage, options) - } - throw new AriesFrameworkError(`Unexpected case`) - } - private async sendDIDCommV1Message( outboundMessageContext: OutboundMessageContext, options?: { @@ -338,7 +341,7 @@ export class MessageSender { if (queueService) { this.logger.debug(`Queue message for connection ${connection.id} (${connection.theirLabel})`) - const params: DidCommV1PackMessageParams = { + const params = { recipientKeys: queueService.recipientKeys, routingKeys: queueService.routingKeys, senderKey: firstOurAuthenticationKey, @@ -418,7 +421,7 @@ export class MessageSender { service: { ...service, recipientKeys: 'omitted...', routingKeys: 'omitted...' }, }) - const params: DidCommV1PackMessageParams = { + const params = { recipientKeys: service.recipientKeys, routingKeys: service.routingKeys, senderKey, @@ -559,213 +562,130 @@ export class MessageSender { private async sendDIDCommV2Message( outboundMessageContext: OutboundMessageContext, options?: { - envelopeType?: EnvelopeType transportPriority?: TransportPriorityOptions } ) { const { agentContext } = outboundMessageContext const message = outboundMessageContext.message as DidCommV2Message - const envelopeType = options?.envelopeType || EnvelopeType.Encrypted - // recipient is not specified -> send to defaultTransport const recipient = message.firstRecipient if (!recipient) { + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + this.logger.error(`Unable to send message. Message doesn't sender DID.`) throw new AriesFrameworkError(`Unable to send message. Message doesn't contain recipient DID.`) } - - // find service transport supported for both sender and receiver - const senderToRecipientService = await this.findCommonSupportedServices( - agentContext, - recipient, - message.from, - options?.transportPriority - ) - if (!senderToRecipientService) { - this.logger.error( - `Unable to send message ${message.id} because there is no any commonly supported service between sender and recipient` - ) - return - } - - if (envelopeType === EnvelopeType.Plain) { - // send message plaintext - return await this.sendDIDCommV2PlaintextMessage(agentContext, message, senderToRecipientService) - } - - if (envelopeType === EnvelopeType.Signed) { - // send message signed - return await this.sendDIDCommV2SignedMessage(agentContext, message, senderToRecipientService) + if (!message.from) { + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + this.logger.error(`Unable to send message. Message doesn't sender DID.`) + throw new AriesFrameworkError(`Unable to send message. Message doesn't sender DID.`) } - if (envelopeType === EnvelopeType.Encrypted) { - // send message encrypted - return await this.sendDIDCommV2EncryptedMessage(agentContext, message, senderToRecipientService) + const { didDocument: senderDidDoc } = await this.didResolverService.resolve(agentContext, message.from) + if (!senderDidDoc) { + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + this.logger.error(`Unable to resolve did document for did '${message.from}'`) + throw new AriesFrameworkError(`Unable to resolve did document for did '${message.from}'`) } - } - - private async findCommonSupportedServices( - agentContext: AgentContext, - recipient: string, - sender?: string, - transportPriority?: TransportPriorityOptions - ): Promise { - if (!sender) return undefined - const { didDocument: senderDidDocument } = await this.didResolverService.resolve(agentContext, sender) - - const { didDocument: recipientDidDocument } = await this.didResolverService.resolve(agentContext, recipient) - if (!recipientDidDocument) { + const { didDocument: recipientDidDoc } = await this.didResolverService.resolve(agentContext, recipient) + if (!recipientDidDoc) { + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + this.logger.error(`Unable to resolve did document for did '${recipient}'`) throw new AriesFrameworkError(`Unable to resolve did document for did '${recipient}'`) } - const senderServices = senderDidDocument?.service || [] - const recipientServices = recipientDidDocument?.service || [] - - const senderTransports = senderServices.map((service) => service.protocolScheme) - const supportedTransports = transportPriority - ? [...transportPriority.schemes, ...senderTransports] - : senderTransports - - // Sort services according to supported transports - const priority = supportedTransports.map((transport) => transport.toString()) - - const services = recipientServices.sort(function (a, b) { - return priority.indexOf(a.protocolScheme) - priority.indexOf(b.protocolScheme) - }) - - const commonServices = services.filter((service) => { - if (priority.includes(service.protocolScheme)) return service - }) - - return commonServices - } - - private async sendDIDCommV2PlaintextMessage( - agentContext: AgentContext, - message: DidCommV2Message, - services: DidDocumentService[] - ) { - this.logger.debug(`Sending plaintext message ${message.id}`) - const recipientDid = message.firstRecipient - return this.sendOutboundDIDCommV2Message(agentContext, message, services, recipientDid) - } - - private async sendDIDCommV2SignedMessage( - agentContext: AgentContext, - message: DidCommV2Message, - services: DidDocumentService[] - ) { - this.logger.debug(`Sending JWS message ${message.id}`) - - const recipientDid = message.firstRecipient - - const pack = async (message: DidCommV2Message, service: DidDocumentService) => { - if (!message.from) { - throw new AriesFrameworkError(`Unable to send message signed. Message doesn't contain sender DID.`) - } - const params: DidCommV2PackMessageParams = { - signByDid: message.from, - serviceId: service?.id, - envelopeType: EnvelopeType.Signed, - } - return this.envelopeService.packMessage(agentContext, message, params) - } - - return this.sendOutboundDIDCommV2Message(agentContext, message, services, recipientDid, pack) - } - - private async sendDIDCommV2EncryptedMessage( - agentContext: AgentContext, - message: DidCommV2Message, - services: DidDocumentService[] - ) { - const recipientDid = message.firstRecipient - if (!recipientDid) { - throw new AriesFrameworkError(`Unable to send message encrypted. Message doesn't contain recipient DID.`) - } - this.logger.debug(`Sending JWE message ${message.id}`) - - const pack = async (message: DidCommV2Message, service: DidDocumentService) => { - return await this.encryptDIDCommV2Message(agentContext, message, service) - } - - return this.sendOutboundDIDCommV2Message(agentContext, message, services, recipientDid, pack) - } - - private async encryptDIDCommV2Message( - agentContext: AgentContext, - message: DidCommV2Message, - service: DidDocumentService, - forward?: boolean - ) { - const recipientDid = message.firstRecipient - if (!recipientDid) { - throw new AriesFrameworkError(`Unable to send message encrypted. Message doesn't contain recipient DID.`) - } - - const params: DidCommV2PackMessageParams = { - toDid: recipientDid, - fromDid: message.from, - signByDid: undefined, - serviceId: service?.id, - wrapIntoForward: forward, - envelopeType: EnvelopeType.Encrypted, + // find service transport supported for both sender and receiver + const services = this.findCommonSupportedDidCommV2Services( + senderDidDoc, + recipientDidDoc, + options?.transportPriority + ) + if (!services) { + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + this.logger.error( + `Unable to send message ${message.id} because there is no any commonly supported service between sender and recipient` + ) + throw new AriesFrameworkError( + `Unable to send message ${message.id} because there is no any commonly supported service between sender and recipient` + ) } - return this.envelopeService.packMessage(agentContext, message, params) - } - private async sendOutboundDIDCommV2Message( - agentContext: AgentContext, - message: DidCommV2Message, - services: DidDocumentService[], - recipientDid?: string, - packMessage?: (message: DidCommV2Message, service: DidDocumentService) => Promise - ) { + // pack and send message until first success for (const service of services) { try { - this.logger.info(`Sending message to ${service.serviceEndpoint}. Transport ${service.protocolScheme}`) - - const payload = packMessage ? await packMessage(message, service) : { ...message } - const outboundPackage = { payload, recipientDid, endpoint: service.serviceEndpoint } + this.logger.info( + `Sending message with id: ${message.id} to ${service.serviceEndpoint}. Transport ${service.protocolScheme}` + ) + const params: DidCommV2PackMessageParams = { + recipientDidDoc, + senderDidDoc, + service, + } + const payload = await this.envelopeService.packMessage(agentContext, message, params) + const outboundPackage = { + payload, + endpoint: service.serviceEndpoint, + } this.logger.trace(`Sending outbound message to transport:`, { transport: service.protocolScheme, outboundPackage, }) - for (const outboundTransport of this.outboundTransports) { if (outboundTransport.supportedSchemes.includes(service.protocolScheme)) { await outboundTransport.sendMessage(outboundPackage) break } } - + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.SentToTransport) this.logger.info( - `Message sent ${message.id} to ${service.serviceEndpoint}. Transport ${service.protocolScheme}` + `Message with id: ${message.id} sent to ${service.serviceEndpoint}. Transport ${service.protocolScheme}` ) return } catch (error) { - this.logger.warn(`Unable to send message to ${service.serviceEndpoint}. Transport failure `, { - errors: error, - }) + this.logger.warn( + `Unable to send message with id: ${message.id} to ${service.serviceEndpoint}. Transport failure `, + { + errors: error, + } + ) // ignore and try another transport } } + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) this.logger.error(`Unable to send message ${message.id} through any commonly supported transport.`) + throw new AriesFrameworkError(`Unable to send message ${message.id} through any commonly supported transport.`) + } + + private findCommonSupportedDidCommV2Services( + senderDidDocument: DidDocument, + recipientDidDocument: DidDocument, + transportPriority?: TransportPriorityOptions + ): DidCommV2Service[] { + const senderServices = senderDidDocument?.service || [] + const recipientServices = recipientDidDocument?.service || [] + let services = recipientServices.filter((recipientService) => { + return senderServices.find((senderService) => { + const service = senderService as DidCommV2Service + return ( + senderService.protocolScheme === recipientService.protocolScheme && + senderService.type === DidCommV2Service.type && + (!service.accept?.length || service.accept.includes('didcomm/v2')) + ) + }) + }) + // If transport priority is set we will sort services by our priority + if (transportPriority?.schemes) { + services = services.sort(function (a, b) { + const aScheme = getProtocolScheme(a.serviceEndpoint) + const bScheme = getProtocolScheme(b.serviceEndpoint) + return transportPriority?.schemes.indexOf(aScheme) - transportPriority?.schemes.indexOf(bScheme) + }) + } + return services } } export function isDidCommTransportQueue(serviceEndpoint: string): serviceEndpoint is typeof DID_COMM_TRANSPORT_QUEUE { return serviceEndpoint === DID_COMM_TRANSPORT_QUEUE } - -function getAuthenticationKeys(didDocument: DidDocument) { - return ( - didDocument.authentication?.map((authentication) => { - const verificationMethod = - typeof authentication === 'string' ? didDocument.dereferenceVerificationMethod(authentication) : authentication - const key = getKeyFromVerificationMethod(verificationMethod) - return key - }) ?? [] - ) -} diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 4bf4ba595d..54376087e3 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -6,7 +6,6 @@ import type { DidDocumentService } from '../../modules/dids' import type { MessageRepository } from '../../storage/MessageRepository' import type { OutboundTransport } from '../../transport' import type { AgentMessageSentEvent } from '../Events' -import type { PackMessageParams } from '@aries-framework/core' import { Subject } from 'rxjs' @@ -656,11 +655,11 @@ describe('MessageSender', () => { jest.resetAllMocks() }) - test('return outbound message context with connection, payload and endpoint', async () => { + test('return outbound message context for DidCommV1 message with connection, payload and endpoint', async () => { const message = new TestMessage() const endpoint = 'https://example.com' - const params: PackMessageParams = { + const params = { recipientKeys: [recipientKey], routingKeys: [], senderKey: senderKey, diff --git a/packages/core/src/agent/__tests__/stubs.ts b/packages/core/src/agent/__tests__/stubs.ts index b7f0d6ee6a..a55c6e1b3e 100644 --- a/packages/core/src/agent/__tests__/stubs.ts +++ b/packages/core/src/agent/__tests__/stubs.ts @@ -1,12 +1,11 @@ -import type { PackMessageParams as DidCommV1PackMessageParams } from '../../didcomm/versions/v1' -import type { V2PackMessageParams as DidCommV2PackMessageParams } from '../../didcomm/versions/v2' import type { AgentMessage } from '../AgentMessage' +import type { PackMessageParams } from '../EnvelopeService' import type { TransportSession } from '../TransportService' export class DummyTransportSession implements TransportSession { public id: string public readonly type = 'http' - public keys?: DidCommV1PackMessageParams | DidCommV2PackMessageParams + public keys?: PackMessageParams public inboundMessage?: AgentMessage public connectionId?: string diff --git a/packages/core/src/crypto/key-provider/Ed25519KeyProvider.ts b/packages/core/src/crypto/key-provider/Ed25519KeyProvider.ts deleted file mode 100644 index 155648d7c3..0000000000 --- a/packages/core/src/crypto/key-provider/Ed25519KeyProvider.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { CreateKeyPairOptions, KeyPair, KeyProvider, SignOptions, VerifyOptions } from './KeyProvider' - -import * as ed25519 from '@stablelib/ed25519' - -import { injectable } from '../../plugins' -import { TypedArrayEncoder } from '../../utils' -import { Buffer } from '../../utils/buffer' -import { KeyType } from '../KeyType' - -/** - * This will be extracted to the ed25519 package. - */ -@injectable() -export class Ed25519KeyProvider implements KeyProvider { - public readonly keyType = KeyType.Ed25519 - - /** - * Create a KeyPair with type ED25519 - * - * @throws {KeyProviderError} When a key could not be created - */ - public async createKeyPair({ seed }: CreateKeyPairOptions): Promise { - const keyPair = seed ? ed25519.generateKeyPairFromSeed(new Buffer(seed)) : ed25519.generateKeyPair() - - return { - keyType: KeyType.Ed25519, - publicKeyBase58: TypedArrayEncoder.toBase58(keyPair.publicKey), - privateKeyBase58: TypedArrayEncoder.toBase58(keyPair.secretKey), - } - } - - public async sign({ data, privateKeyBase58 }: SignOptions): Promise { - const secretKeyBytes = TypedArrayEncoder.fromBase58(privateKeyBase58) - return Buffer.from(ed25519.sign(secretKeyBytes, data as Buffer)) - } - - public async verify({ data, publicKeyBase58, signature }: VerifyOptions): Promise { - const publicKeyBytes = TypedArrayEncoder.fromBase58(publicKeyBase58) - const message = Uint8Array.from(data as Buffer) - return ed25519.verify(publicKeyBytes, message, signature) - } -} diff --git a/packages/core/src/crypto/key-provider/KeyProviderRegistry.ts b/packages/core/src/crypto/key-provider/KeyProviderRegistry.ts index 5612c734e3..ad4ea35aa9 100644 --- a/packages/core/src/crypto/key-provider/KeyProviderRegistry.ts +++ b/packages/core/src/crypto/key-provider/KeyProviderRegistry.ts @@ -10,23 +10,28 @@ export const KeyProviderToken = Symbol('KeyProviderToken') export class KeyProviderRegistry { public keyProviders: KeyProvider[] - public constructor(@injectAll(KeyProviderToken) keyProviders: KeyProvider[]) { - this.keyProviders = keyProviders + public constructor(@injectAll(KeyProviderToken) keyProviders: Array<'default' | KeyProvider>) { + // This is a really ugly hack to make tsyringe work without any SigningProviders registered + // It is currently impossible to use @injectAll if there are no instances registered for the + // token. We register a value of `default` by default and will filter that out in the registry. + // Once we have a signing provider that should always be registered we can remove this. We can make an ed25519 + // signer using the @stablelib/ed25519 library. + this.keyProviders = keyProviders.filter((provider) => provider !== 'default') as KeyProvider[] } public hasProviderForKeyType(keyType: KeyType): boolean { - const keyProvider = this.keyProviders.find((x) => x.keyType === keyType) + const signingKeyProvider = this.keyProviders.find((x) => x.keyType === keyType) - return keyProvider !== undefined + return signingKeyProvider !== undefined } public getProviderForKeyType(keyType: KeyType): KeyProvider { - const keyProvider = this.keyProviders.find((x) => x.keyType === keyType) + const signingKeyProvider = this.keyProviders.find((x) => x.keyType === keyType) - if (!keyProvider) { + if (!signingKeyProvider) { throw new AriesFrameworkError(`No key provider for key type: ${keyType}`) } - return keyProvider + return signingKeyProvider } } diff --git a/packages/core/src/crypto/key-provider/X25519KeyProvider.ts b/packages/core/src/crypto/key-provider/X25519KeyProvider.ts deleted file mode 100644 index bb10c9926b..0000000000 --- a/packages/core/src/crypto/key-provider/X25519KeyProvider.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { CreateKeyPairOptions, KeyPair, KeyProvider } from './KeyProvider' - -import * as ed25519 from '@stablelib/ed25519' - -import { injectable } from '../../plugins' -import { TypedArrayEncoder } from '../../utils' -import { Buffer } from '../../utils/buffer' -import { KeyType } from '../KeyType' - -/** - * This will be extracted to the x25519 package. - */ -@injectable() -export class X25519KeyProvider implements KeyProvider { - public readonly keyType = KeyType.X25519 - - /** - * Create a KeyPair with type X25519 - * - * @throws {KeyProviderError} When a key could not be created - */ - public async createKeyPair({ seed }: CreateKeyPairOptions): Promise { - const keyPair = seed ? ed25519.generateKeyPairFromSeed(new Buffer(seed)) : ed25519.generateKeyPair() - const privateKey = ed25519.convertSecretKeyToX25519(keyPair.secretKey) - const publicKey = ed25519.convertPublicKeyToX25519(keyPair.publicKey) - - return { - keyType: KeyType.X25519, - publicKeyBase58: TypedArrayEncoder.toBase58(publicKey), - privateKeyBase58: TypedArrayEncoder.toBase58(privateKey), - } - } - - public async sign(): Promise { - throw new Error("Method 'sign' not supported for X25519KeyProvider.") - } - - public async verify(): Promise { - throw new Error("Method 'verify' not supported for X25519KeyProvider.") - } -} diff --git a/packages/core/src/didcomm/JweEnvelope.ts b/packages/core/src/didcomm/JweEnvelope.ts new file mode 100644 index 0000000000..7201b53d54 --- /dev/null +++ b/packages/core/src/didcomm/JweEnvelope.ts @@ -0,0 +1,200 @@ +import type { DidCommV2EncryptionAlgs, DidCommV2KeyProtectionAlgs, DidCommV2Types } from './' + +import { Expose, Type } from 'class-transformer' + +import { Buffer, JsonEncoder, JsonTransformer, TypedArrayEncoder } from '../utils' + +export class JweRecipient { + @Expose({ name: 'encrypted_key' }) + public encryptedKey!: string + public header?: Record + + public constructor(options: { encryptedKey: Uint8Array; header?: Record }) { + if (options) { + this.encryptedKey = TypedArrayEncoder.toBase64URL(options.encryptedKey) + this.header = options.header + } + } +} + +export interface ProtectedOptions { + typ?: string + enc: string + alg: string + skid?: string + epk?: string + apu?: string + apv?: string +} + +export class Protected { + public typ?: string + public enc!: string + public alg!: string + public skid?: string + public epk?: string + public apu?: string + public apv?: string + + public constructor(options: ProtectedOptions) { + if (options) { + this.typ = options.typ + this.enc = options.enc + this.alg = options.alg + this.skid = options.skid + this.epk = options.epk + this.apu = options.apu + this.apv = options.apv + } + } + + public toJson() { + return JsonTransformer.toJSON(this) + } +} + +export interface JweEnvelopeOptions { + protected: string + unprotected?: string + recipients?: JweRecipient[] + ciphertext: string + iv: string + tag: string + aad?: string + header?: string[] + encryptedKey?: string +} + +export class JweEnvelope { + public protected!: string + public unprotected?: string + + @Type(() => JweRecipient) + public recipients?: JweRecipient[] + public ciphertext!: string + public iv!: string + public tag!: string + public aad?: string + public header?: string[] + + @Expose({ name: 'encrypted_key' }) + public encryptedKey?: string + + public constructor(options: JweEnvelopeOptions) { + if (options) { + this.protected = options.protected + this.unprotected = options.unprotected + this.recipients = options.recipients + this.ciphertext = options.ciphertext + this.iv = options.iv + this.tag = options.tag + this.aad = options.aad + this.header = options.header + this.encryptedKey = options.encryptedKey + } + } + + public toJson() { + return JsonTransformer.toJSON(this) + } +} + +export class JweEnvelopeBuilder { + public protected!: Protected + public unprotected?: string + + public ciphertext!: string + public iv!: string + public tag!: string + public header?: string[] + public encryptedKey?: string + public recipients!: JweRecipient[] + + public constructor({ + typ, + alg, + enc, + }: { + typ: DidCommV2Types + enc: DidCommV2EncryptionAlgs + alg: DidCommV2KeyProtectionAlgs + }) { + this.recipients = [] + this.protected = new Protected({ typ, alg, enc }) + } + + public setRecipient(recipient: JweRecipient): JweEnvelopeBuilder { + this.recipients.push(recipient) + return this + } + + public setCiphertext(ciphertext: Uint8Array | Buffer): JweEnvelopeBuilder { + this.ciphertext = TypedArrayEncoder.toBase64URL(ciphertext) + return this + } + + public setIv(iv: Uint8Array | Buffer): JweEnvelopeBuilder { + this.iv = TypedArrayEncoder.toBase64URL(iv) + return this + } + + public setTag(tag: Uint8Array | Buffer): JweEnvelopeBuilder { + this.tag = TypedArrayEncoder.toBase64URL(tag) + return this + } + + public setProtected(protected_: Protected): JweEnvelopeBuilder { + this.protected = protected_ + return this + } + + public setSkid(skid: string): JweEnvelopeBuilder { + this.protected.skid = skid + return this + } + + public setEpk(epk: string): JweEnvelopeBuilder { + this.protected.epk = epk + return this + } + + public setApu(apu: string): JweEnvelopeBuilder { + this.protected.apu = TypedArrayEncoder.toBase64URL(Buffer.from(apu)) + return this + } + + public setApv(apv: string[]): JweEnvelopeBuilder { + this.protected.apv = TypedArrayEncoder.toBase64URL(Buffer.from(apv.sort().join('.'))) + return this + } + + public apv(): Uint8Array { + return this.protected.apv ? Uint8Array.from(Buffer.from(this.protected.apv)) : Uint8Array.from([]) + } + + public apu(): Uint8Array { + return this.protected.apu ? Uint8Array.from(Buffer.from(this.protected.apu)) : Uint8Array.from([]) + } + + public alg(): Uint8Array { + return Uint8Array.from(Buffer.from(this.protected.alg)) + } + + public aad(): Buffer { + return Buffer.from(this.protected_()) + } + + private protected_(): string { + return JsonEncoder.toBase64URL(this.protected.toJson()) + } + + public finalize() { + return new JweEnvelope({ + ciphertext: this.ciphertext, + tag: this.tag, + iv: this.iv, + protected: this.protected_(), + recipients: this.recipients, + }) + } +} diff --git a/packages/core/src/didcomm/index.ts b/packages/core/src/didcomm/index.ts index ed7273c784..e74379142a 100644 --- a/packages/core/src/didcomm/index.ts +++ b/packages/core/src/didcomm/index.ts @@ -7,5 +7,6 @@ export * from './versions/v1' export * from './versions/v2' export * from './types' export * from './helpers' +export * from './JweEnvelope' export type ConstructableDidCommMessage = Constructor & { type: ParsedMessageType } diff --git a/packages/core/src/didcomm/types.ts b/packages/core/src/didcomm/types.ts index 86a874b095..1b1a6c9100 100644 --- a/packages/core/src/didcomm/types.ts +++ b/packages/core/src/didcomm/types.ts @@ -12,6 +12,7 @@ export type PlaintextMessage = PlaintextDidCommV1Message | PlaintextDidCommV2Mes export type EncryptedMessageRecipientHeader = { kid: string + epk?: string } export type EncryptedMessageRecipient = { diff --git a/packages/core/src/didcomm/versions/v1/index.ts b/packages/core/src/didcomm/versions/v1/index.ts index 766336bc99..a26c45be9d 100644 --- a/packages/core/src/didcomm/versions/v1/index.ts +++ b/packages/core/src/didcomm/versions/v1/index.ts @@ -1,18 +1,13 @@ import type { Key } from '../../../crypto' -import type { EnvelopeType } from '../../types' export { DidCommV1Message } from './DidCommV1Message' export { DidCommV1BaseMessage, DidComV1BaseMessageConstructor } from './DidCommV1BaseMessage' -export interface PackMessageParams { +export interface DidCommV1PackMessageParams { recipientKeys: Key[] routingKeys: Key[] senderKey: Key | null - envelopeType?: EnvelopeType } -export { isPlaintextMessageV1 } from './helpers' -export { isDidCommV1Message } from './helpers' -export { DidCommV1Algorithms } from './types' -export { DidCommV1Types } from './types' -export { PlaintextDidCommV1Message } from './types' +export { isPlaintextMessageV1, isDidCommV1Message, isDidCommV1EncryptedEnvelope } from './helpers' +export { DidCommV1Algorithms, DidCommV1Types, PlaintextDidCommV1Message } from './types' diff --git a/packages/core/src/didcomm/versions/v2/DidCommV2Message.ts b/packages/core/src/didcomm/versions/v2/DidCommV2Message.ts index 929a664931..e0a8d81ef9 100644 --- a/packages/core/src/didcomm/versions/v2/DidCommV2Message.ts +++ b/packages/core/src/didcomm/versions/v2/DidCommV2Message.ts @@ -8,6 +8,10 @@ import { DidCommMessageVersion } from '../../types' import { DidCommV2BaseMessage } from './DidCommV2BaseMessage' export class DidCommV2Message extends DidCommV2BaseMessage implements AgentMessage { + public get didCommVersion(): DidCommMessageVersion { + return DidCommMessageVersion.V2 + } + public toJSON(): Record { return JsonTransformer.toJSON(this) } @@ -16,10 +20,6 @@ export class DidCommV2Message extends DidCommV2BaseMessage implements AgentMessa return undefined } - public get didCommVersion(): DidCommMessageVersion { - return DidCommMessageVersion.V2 - } - public get threadId(): string | undefined { return this.thid } @@ -40,10 +40,6 @@ export class DidCommV2Message extends DidCommV2BaseMessage implements AgentMessa return this.type === Class.type.messageTypeUri } - public setRecipient(to?: string) { - this.to = to ? [to] : undefined - } - public get firstRecipient(): string | undefined { return this.to?.length ? this.to[0] : undefined } diff --git a/packages/core/src/didcomm/versions/v2/index.ts b/packages/core/src/didcomm/versions/v2/index.ts index 16cf7d1359..51582b019c 100644 --- a/packages/core/src/didcomm/versions/v2/index.ts +++ b/packages/core/src/didcomm/versions/v2/index.ts @@ -1,17 +1,13 @@ -import type { EnvelopeType } from '../../types' +import type { DidDocument, DidCommV2Service } from '../../../modules/dids' export { DidCommV2Message } from './DidCommV2Message' export { DidCommV2BaseMessage, DidComV2BaseMessageConstructor, DidCommV2MessageParams } from './DidCommV2BaseMessage' -export interface V2PackMessageParams { - toDid?: string - fromDid?: string - signByDid?: string - serviceId?: string - wrapIntoForward?: boolean - envelopeType?: EnvelopeType +export interface DidCommV2PackMessageParams { + recipientDidDoc: DidDocument + senderDidDoc?: DidDocument + service: DidCommV2Service } -export { isPlaintextMessageV2 } from './helpers' -export { isDidCommV2Message } from './helpers' -export { PlaintextDidCommV2Message } from './types' +export { isPlaintextMessageV2, isDidCommV2Message } from './helpers' +export { PlaintextDidCommV2Message, DidCommV2Types, DidCommV2EncryptionAlgs, DidCommV2KeyProtectionAlgs } from './types' diff --git a/packages/core/src/didcomm/versions/v2/types.ts b/packages/core/src/didcomm/versions/v2/types.ts index 897457b36e..abe8c9ffee 100644 --- a/packages/core/src/didcomm/versions/v2/types.ts +++ b/packages/core/src/didcomm/versions/v2/types.ts @@ -6,3 +6,19 @@ export interface PlaintextDidCommV2Message { [key: string]: unknown } + +export enum DidCommV2Types { + EncryptedJson = 'application/didcomm-encrypted+json', +} + +export enum DidCommV2EncryptionAlgs { + XC20P = 'XC20P', + A256CbcHs512 = 'A256CBC-HS512', +} + +export enum DidCommV2KeyProtectionAlgs { + EcdhEsA128Kw = 'ECDH-ES+A128KW', + EcdhEsA256Kw = 'ECDH-ES+A256KW', + Ecdh1PuA128Kw = 'ECDH-1PU+A128KW', + Ecdh1PuA256Kw = 'ECDH-1PU+A256KW', +} diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index ca6581fc16..7b494a9f6a 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -444,7 +444,9 @@ export class ConnectionsApi { new V1TrustPingMessageHandler(this.v1trustPingService, this.connectionService) ) messageHandlerRegistry.registerMessageHandler(new V1TrustPingResponseMessageHandler(this.v1trustPingService)) - messageHandlerRegistry.registerMessageHandler(new V2TrustPingMessageHandler(this.v2TrustPingService)) + messageHandlerRegistry.registerMessageHandler( + new V2TrustPingMessageHandler(this.v2TrustPingService, this.connectionService) + ) messageHandlerRegistry.registerMessageHandler(new V2TrustPingResponseMessageHandler(this.v2TrustPingService)) messageHandlerRegistry.registerMessageHandler( diff --git a/packages/core/src/modules/connections/protocols/trust-ping/TrustPingEvents.ts b/packages/core/src/modules/connections/protocols/trust-ping/TrustPingEvents.ts index 2430dcc382..ae7fa7a915 100644 --- a/packages/core/src/modules/connections/protocols/trust-ping/TrustPingEvents.ts +++ b/packages/core/src/modules/connections/protocols/trust-ping/TrustPingEvents.ts @@ -11,14 +11,16 @@ import type { ConnectionRecord } from '../../repository/ConnectionRecord' export enum TrustPingEventTypes { TrustPingReceivedEvent = 'TrustPingReceivedEvent', + V2TrustPingReceivedEvent = 'V2TrustPingReceivedEvent', TrustPingResponseReceivedEvent = 'TrustPingResponseReceivedEvent', + V2TrustPingResponseReceivedEvent = 'V2TrustPingResponseReceivedEvent', } export interface TrustPingReceivedEvent extends BaseEvent { type: typeof TrustPingEventTypes.TrustPingReceivedEvent payload: { connectionRecord?: ConnectionRecord | null - message: V1TrustPingMessage | V2TrustPingMessage + message: V1TrustPingMessage } } @@ -26,6 +28,22 @@ export interface TrustPingResponseReceivedEvent extends BaseEvent { type: typeof TrustPingEventTypes.TrustPingResponseReceivedEvent payload: { connectionRecord?: ConnectionRecord | null - message: V1TrustPingResponseMessage | V2TrustPingResponseMessage + message: V1TrustPingResponseMessage + } +} + +export interface V2TrustPingReceivedEvent extends BaseEvent { + type: typeof TrustPingEventTypes.V2TrustPingReceivedEvent + payload: { + connectionRecord?: ConnectionRecord | null + message: V2TrustPingMessage + } +} + +export interface V2TrustPingResponseReceivedEvent extends BaseEvent { + type: typeof TrustPingEventTypes.V2TrustPingResponseReceivedEvent + payload: { + connectionRecord?: ConnectionRecord | null + message: V2TrustPingResponseMessage } } diff --git a/packages/core/src/modules/connections/protocols/trust-ping/v2/V2TrustPingService.ts b/packages/core/src/modules/connections/protocols/trust-ping/v2/V2TrustPingService.ts index c5b149c6e0..78e6600670 100644 --- a/packages/core/src/modules/connections/protocols/trust-ping/v2/V2TrustPingService.ts +++ b/packages/core/src/modules/connections/protocols/trust-ping/v2/V2TrustPingService.ts @@ -1,6 +1,6 @@ import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' import type { ConnectionRecord } from '../../../repository' -import type { TrustPingReceivedEvent, TrustPingResponseReceivedEvent } from '../TrustPingEvents' +import type { V2TrustPingReceivedEvent, V2TrustPingResponseReceivedEvent } from '../TrustPingEvents' import { Dispatcher } from '../../../../../agent/Dispatcher' import { EventEmitter } from '../../../../../agent/EventEmitter' @@ -42,8 +42,8 @@ export class V2TrustPingService { public processPing({ agentContext, message }: InboundMessageContext) { this.logger.info('Trust Ping message received.', message) - this.eventEmitter.emit(agentContext, { - type: TrustPingEventTypes.TrustPingReceivedEvent, + this.eventEmitter.emit(agentContext, { + type: TrustPingEventTypes.V2TrustPingReceivedEvent, payload: { message: message, }, @@ -59,13 +59,14 @@ export class V2TrustPingService { } } - public processPingResponse({ agentContext, message }: InboundMessageContext) { + public processPingResponse({ agentContext, message, connection }: InboundMessageContext) { this.logger.info('Trust Ping Response message received.', message) - this.eventEmitter.emit(agentContext, { - type: TrustPingEventTypes.TrustPingResponseReceivedEvent, + this.eventEmitter.emit(agentContext, { + type: TrustPingEventTypes.V2TrustPingResponseReceivedEvent, payload: { message: message, + connectionRecord: connection, }, }) } diff --git a/packages/core/src/modules/connections/protocols/trust-ping/v2/handlers/TrustPingMessageHandler.ts b/packages/core/src/modules/connections/protocols/trust-ping/v2/handlers/TrustPingMessageHandler.ts index b83d6af274..06f2cf4c88 100644 --- a/packages/core/src/modules/connections/protocols/trust-ping/v2/handlers/TrustPingMessageHandler.ts +++ b/packages/core/src/modules/connections/protocols/trust-ping/v2/handlers/TrustPingMessageHandler.ts @@ -1,4 +1,5 @@ import type { MessageHandler, MessageHandlerInboundMessage } from '../../../../../../agent/MessageHandler' +import type { ConnectionService } from '../../../../services/ConnectionService' import type { V2TrustPingService } from '../V2TrustPingService' import { OutboundMessageContext } from '../../../../../../agent/models' @@ -6,14 +7,17 @@ import { TrustPingMessage } from '../messages/TrustPingMessage' export class TrustPingMessageHandler implements MessageHandler { private v2TrustPingService: V2TrustPingService + private connectionService: ConnectionService public supportedMessages = [TrustPingMessage] - public constructor(trustPingService: V2TrustPingService) { + public constructor(trustPingService: V2TrustPingService, connectionService: ConnectionService) { this.v2TrustPingService = trustPingService + this.connectionService = connectionService } public async handle(messageContext: MessageHandlerInboundMessage) { const message = await this.v2TrustPingService.processPing(messageContext) + if (message) { return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, diff --git a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts index 4877113fa0..04cff6aceb 100644 --- a/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts +++ b/packages/core/src/modules/didcomm/services/DidCommDocumentService.ts @@ -5,7 +5,7 @@ import type { ResolvedDidCommService } from '../types' import { AgentConfig } from '../../../agent/AgentConfig' import { KeyType } from '../../../crypto' import { injectable } from '../../../plugins' -import { DidResolverService } from '../../dids' +import { DidCommV2Service, DidResolverService } from '../../dids' import { DidCommV1Service, IndyAgentService, keyReferenceToKey } from '../../dids/domain' import { verkeyToInstanceOfKey } from '../../dids/helpers' import { findMatchingEd25519Key } from '../util/matchingEd25519Key' @@ -64,6 +64,22 @@ export class DidCommDocumentService { routingKeys, serviceEndpoint: didCommService.serviceEndpoint, }) + } else if (didCommService instanceof DidCommV2Service) { + // Resolve dids to DIDDocs to retrieve routingKeys + const routingKeys = [] + for (const routingKey of didCommService.routingKeys ?? []) { + const routingDidDocument = await this.didResolverService.resolveDidDocument(agentContext, routingKey) + routingKeys.push(keyReferenceToKey(routingDidDocument, routingKey)) + } + + // DidCommV2Service has keys encoded as key references + + didCommServices.push({ + id: didCommService.id, + routingKeys, + serviceEndpoint: didCommService.serviceEndpoint, + recipientKeys: [], + }) } } diff --git a/packages/core/src/modules/dids/domain/DidDocument.ts b/packages/core/src/modules/dids/domain/DidDocument.ts index b4d4525e36..c4e14c9bfb 100644 --- a/packages/core/src/modules/dids/domain/DidDocument.ts +++ b/packages/core/src/modules/dids/domain/DidDocument.ts @@ -8,7 +8,7 @@ import { JsonTransformer } from '../../../utils/JsonTransformer' import { IsStringOrStringArray } from '../../../utils/transformers' import { getKeyFromVerificationMethod } from './key-type' -import { IndyAgentService, ServiceTransformer, DidCommV1Service } from './service' +import { IndyAgentService, ServiceTransformer, DidCommV1Service, DidCommV2Service } from './service' import { VerificationMethodTransformer, VerificationMethod, IsStringOrVerificationMethod } from './verificationMethod' export type DidPurpose = @@ -169,8 +169,8 @@ export class DidDocument { * Get all DIDComm services ordered by priority descending. This means the highest * priority will be the first entry. */ - public get didCommServices(): Array { - const didCommServiceTypes = [IndyAgentService.type, DidCommV1Service.type] + public get didCommServices(): Array { + const didCommServiceTypes = [IndyAgentService.type, DidCommV1Service.type, DidCommV2Service.type] const services = (this.service?.filter((service) => didCommServiceTypes.includes(service.type)) ?? []) as Array< IndyAgentService | DidCommV1Service > @@ -248,3 +248,25 @@ export async function findVerificationMethodByKeyType( return null } + +export function getAuthenticationKeys(didDocument: DidDocument) { + return ( + didDocument.authentication?.map((authentication) => { + const verificationMethod = + typeof authentication === 'string' ? didDocument.dereferenceVerificationMethod(authentication) : authentication + const key = getKeyFromVerificationMethod(verificationMethod) + return key + }) ?? [] + ) +} + +export function getAgreementKeys(didDocument: DidDocument) { + return ( + didDocument.keyAgreement?.map((keyAgreement) => { + const verificationMethod = + typeof keyAgreement === 'string' ? didDocument.dereferenceVerificationMethod(keyAgreement) : keyAgreement + const key = getKeyFromVerificationMethod(verificationMethod) + return key + }) ?? [] + ) +} diff --git a/packages/core/src/modules/routing/messages/V2ForwardMessage.ts b/packages/core/src/modules/routing/messages/V2ForwardMessage.ts new file mode 100644 index 0000000000..248f04625b --- /dev/null +++ b/packages/core/src/modules/routing/messages/V2ForwardMessage.ts @@ -0,0 +1,42 @@ +import type { DidCommV2MessageParams } from '../../../didcomm' + +import { Type } from 'class-transformer' +import { IsString, ValidateNested } from 'class-validator' + +import { DidCommV2Message } from '../../../didcomm' +import { IsValidMessageType, parseMessageType } from '../../../utils/messageType' + +export class V2ForwardMessageBody { + @IsString() + public next!: string +} + +export type V2ForwardMessageOptions = { + body: V2ForwardMessageBody +} & DidCommV2MessageParams + +/** + * DIDComm V2 version of message defined here https://identity.foundation/didcomm-messaging/spec/#messages + */ +export class V2ForwardMessage extends DidCommV2Message { + /** + * Create new ForwardMessage instance. + * + * @param options + */ + public constructor(options: V2ForwardMessageOptions) { + super(options) + + if (options) { + this.body = options.body + } + } + + @IsValidMessageType(V2ForwardMessage.type) + public readonly type = V2ForwardMessage.type.messageTypeUri + public static readonly type = parseMessageType('https://didcomm.org/routing/2.0/forward') + + @Type(() => V2ForwardMessageBody) + @ValidateNested() + public body!: V2ForwardMessageBody +} diff --git a/packages/core/src/modules/routing/messages/index.ts b/packages/core/src/modules/routing/messages/index.ts index 06af8aeb93..4cd094bd6d 100644 --- a/packages/core/src/modules/routing/messages/index.ts +++ b/packages/core/src/modules/routing/messages/index.ts @@ -4,3 +4,4 @@ export * from './KeylistUpdateResponseMessage' export * from './MediationGrantMessage' export * from './MediationDenyMessage' export * from './MediationRequestMessage' +export * from './V2ForwardMessage' diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index e062ff96c1..5d78c083df 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -1,5 +1,5 @@ -import type { Key, KeyPair, KeyType } from '../crypto' -import type { EncryptedMessage, PlaintextMessage, EnvelopeType } from '../didcomm/types' +import type { Key, KeyType } from '../crypto' +import type { EncryptedMessage, PlaintextMessage, EnvelopeType, DidCommMessageVersion } from '../didcomm/types' import type { Disposable } from '../plugins' import type { WalletConfig, WalletConfigRekey, WalletExportImportConfig } from '../types' import type { Buffer } from '../utils/buffer' @@ -12,7 +12,6 @@ export interface Wallet extends Disposable { createAndOpen(walletConfig: WalletConfig): Promise open(walletConfig: WalletConfig): Promise rotateKey(walletConfig: WalletConfigRekey): Promise - retrieveKeyPair(kid: string): Promise close(): Promise delete(): Promise @@ -64,23 +63,15 @@ export interface WalletVerifyOptions { } export interface UnpackedMessageContext { + didCommVersion: DidCommMessageVersion plaintextMessage: PlaintextMessage - senderKey?: string - recipientKey?: string + senderKey?: Key + recipientKey?: Key } -export interface WalletPackV1Options { - version: 'v1' - recipientKeys: string[] - senderKey: string | null +export type WalletPackOptions = { + didCommVersion: DidCommMessageVersion + recipientKeys: Key[] + senderKey?: Key | null envelopeType?: EnvelopeType } -export interface WalletPackV2Options { - version: 'v2' - toDid?: string - fromDid?: string - serviceId?: string - envelopeType?: EnvelopeType -} - -export type WalletPackOptions = WalletPackV1Options | WalletPackV2Options diff --git a/packages/core/tests/TestMessage.ts b/packages/core/tests/TestMessage.ts index b40a652508..c9ca013bfb 100644 --- a/packages/core/tests/TestMessage.ts +++ b/packages/core/tests/TestMessage.ts @@ -1,4 +1,4 @@ -import { DidCommV1Message } from '../src/didcomm' +import { DidCommV1Message, DidCommV2Message } from '../src/didcomm' export class TestMessage extends DidCommV1Message { public constructor() { @@ -9,3 +9,14 @@ export class TestMessage extends DidCommV1Message { public type = 'https://didcomm.org/connections/1.0/invitation' } + +export class V2TestMessage extends DidCommV2Message { + public constructor() { + super() + + this.id = this.generateId() + this.body = {} + } + + public type = 'https://didcomm.org/connections/2.0/invitation' +} diff --git a/packages/core/tests/mocks/MockWallet.ts b/packages/core/tests/mocks/MockWallet.ts index ecbba3d4d3..5949aa1ff3 100644 --- a/packages/core/tests/mocks/MockWallet.ts +++ b/packages/core/tests/mocks/MockWallet.ts @@ -64,10 +64,6 @@ export class MockWallet implements Wallet { throw new Error('Method not implemented.') } - public retrieveKeyPair(keyId: string): Promise { - throw new Error('Method not implemented.') - } - public dispose() { // Nothing to do here } diff --git a/packages/didcomm-v2/README.md b/packages/didcomm-v2/README.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/didcomm-v2/integration-notes.md b/packages/didcomm-v2/integration-notes.md deleted file mode 100644 index 05d648cdf9..0000000000 --- a/packages/didcomm-v2/integration-notes.md +++ /dev/null @@ -1,154 +0,0 @@ -## DIDComm V2 integration notes - -This document contains the plan (what's done, questions, issues) of DIDComm V2 messaging integration into Aries Framework Javascript. - -[DIDComm V2 Specification](https://identity.foundation/didcomm-messaging/spec/) - -### Proof of work - -**State:** Done - -Update [Alice/Faber demo](../../demo/README.md) scripts.\ -Faber can generate DidComm V2 based [Out-of-Band invitation](https://identity.foundation/didcomm-messaging/spec/#invitation) -Alice can accept it and `ping` Faber using DidComm V2 messaging.\ - -> Note: Issuance and Verification protocols are not adopted! So these options not working now. - -### DidComm V2 as independent optional package - -**State:** Done - -- AFJ Core package contains: - - - Definitions for general DIDComm V1/V2 messages. - - Service interfaces for DIDComm V1 / V2 massages encryption - - DIDComm V1 encryption service implementation which is based on indy wallet. - - For DIDComm V2 stub is registered into the dependency injection container. Agent initialization works fine without providing actual implementation. Error will be thrown if DidComm V2 methods are called and there has not been done registration of actual implementation for DidComm V2 encryption service. - - Common `EnvelopeService` injecting DIDComm V1 / V2 services and used by message sender and receiver. - -- `didcomm-v2` package: - - - DIDComm V2 encryption service implementation which is based on [Sicpa didcomm libraries](https://github.com/sicpa-dlab/didcomm-rust). - - [Node](https://www.npmjs.com/package/didcomm-node) or [React Native](https://www.npmjs.com/package/@sicpa_open_source/didcomm-react-native) package must be passed into `DidCommV2Module` module constructor as parameter. - - `didCommV2` modules should be passed into Agent constructor for proper registration in the dependency injection container. - - Example of agent initialization: - - ``` - import * as didcomm from 'didcomm-node' - - const didCommV2Module = new DidCommV2Module({ didcomm }) - const modules = { didCommV2: didCommV2Module } - - const agent = new Agent({ config, modules, dependencies: agentDependencies }) - ``` - -### Private Keys out of Wallet issue - -**State:** To discuss - -`didcomm-v2` package requires resolving of private keys and passing their values into native didcomm libraries. Right now, there is no way to bypass this. - -### Connections - -**State:** Done simple option. Need to discuss - -Right now, DIDComm V2 messaging specification does not provide any definition for DID Exchange protocol. There is only definition for single out-of-band message which should be used for sharing DIDs. -At the same time, all AFJ protocols (issuance, proofs, etc.) require existence of Connection state outOfBandRecord stored in the Wallet. - -Currently, there is implemented straight forward option: - -- Sender can create DidComm V2 specific out-of-band invitation -- Receiver can accept invitation - Connection state outOfBandRecord will be created in the `ready` state. - - > Note: Current Out-of-Band implementation is really trivial and limited. It should be evaluated in the future. - - ``` - // Inviter - const outOfBandRecord = await this.agent.oob.createInvitation({ version: 'v2' }) - const invitationUrl = outOfBandRecord.v2OutOfBandInvitation.toUrl({ domain: `http://localhost:${this.port}` }) - - // Receiver - const { connectionRecord } = await this.agent.oob.receiveInvitationFromUrl(invitationUrl) - // connectionRecord.isReady === true - ``` - -- Note: So that connection will be created only on one side! Inviter still will not be able to trigger protocols. But still will be able to reply on incoming messages. - -We need to discuss whether this approach is acceptable and provide better design on how to make connections if it is not. - -### Mediator - -**State:** There is independent [open sourced implementation of Cloud Mediator agent supporting DidComm V2 protocols](https://github.com/sicpa-dlab/didcomm-v2-mediator-ts). - -AFJ core modules related to Mediator functionality have not been updated to provide mediation for DidComm V2 edge agents. -This work should be done during the next steps. - -### Mediation Recipient - -**State:** Completed initial implementation - -[Repository](https://github.com/decentralized-identity/didcomm.org/tree/main/site/content/protocols) contacting protocols specifications. - -This repository contains protocols adoptions for DidComm V2 messaging: - -- [Mediator Coordination](https://github.com/decentralized-identity/didcomm.org/tree/main/site/content/protocols/mediator-coordination/2.0) -- [Pickup](https://github.com/decentralized-identity/didcomm.org/tree/main/site/content/protocols/pickup/3.0) -- [Routing](https://github.com/decentralized-identity/didcomm.org/tree/main/site/content/protocols/routing/2.0) - -The work devoted to the adoption of these protocols already has been done in the separate [didcommv2-contribution-routing](https://github.com/sicpa-dlab/aries-framework-javascript/tree/didcommv2-contribution-routing) branch. -This branch has been created from the main `didcommv2-contribution` branch but got significantly behind and need to be updated. - -We need to merge general DidComm V2 MR firstly. After that we will actualize the branch with routing support. - -### Peer DID package - -**State:** There is [peer-did-ts](https://www.npmjs.com/package/@sicpa_open_source/peer-did-ts) package providing implementation of the Peer DID method specification in Typescript. The source code for this package has been mainly taken from AFJ Core package and probably should be reworked. - -It will be good to extract sup-packages providing TypeScript implementations for `DidDocument` and `PeerDid` from AFJ Core. - -> Should be postponed and done later in a separate PR - -### Peer DID format - -**State:** Resolved. We found another issue with our new changes and rolled back peer DID changes. There is no other work that needs to be done here. Everything is good now. - -~~There is a contradiction between the two specifications:~~ - -~~- According to the [Peer DID spec](https://identity.foundation/peer-did-method-spec/#multi-key-creation) key ids doesn't have `z` prefix in `did-url` (fragment after #).~~ -~~- According to the [DIDComm spec](https://identity.foundation/didcomm-messaging/spec/#:~:text=%22id%22%3A%20%22did%3Aexample%3A123%23zC9ByQ8aJs8vrNXyDhPHHNNMSHPcaSgNpjjsBYpMMjsTdS%22%2C) key ids must have `z` prefix in `did-url` (fragment after #).~~ - -~~In our understanding, `z` prefix should always be used for multi-base key representation which is currently used there.\ -Sicpa `didcomm-rust` library requests keys from the Wallet by id containing additional `z` letter at the start of the did-url. -If we consider this behaviour wrong, we will need to change `didcomm-rust` libraries.~~ - -~~> Should be postponed if possible and changed later in a separate PR if current behaviour is wrong~~ - -### Protocols adoptions - -**State:** To be done - -- [Trust Ping](https://identity.foundation/didcomm-messaging/spec/#trust-ping-protocol-20) protocol. - - For the DidComm V2 testing and demonstration purpose was added public `sendPing` function to Connection API. Right now, this ping function does not depend on the Connection state outOfBandRecord and requires the passing of sender and receiver DIDs to send DidComm V2-based ping message. - -> Should be postponed and done later in a separate PR - -### Rework Message Sender / Receiver - -**State:** To discuss. - -Right now seems sensible to split Message Sender classes into two subclasses providing functionality for processing of corresponding DidComm message version (V1/V2). -So instead of having sendV1/sendV2 functions there will be call send method of the corresponding subclass. -BUT: right now we do not have full integration of Connection state objects for DidComm V2 protocols. This is one of main cause of adding new `sendV2` methods. In future this issue may go away once we get Connections for DidComm V2. -So it looks lite this kind of refactoring should be postponed. - -Regarding Message Receiver, there is no actually many specific v1/v2 methods. - -> Should be postponed and done later in a separate PR - -### Envelop unpack result - -**State:** To discuss - related to the [comment](https://github.com/hyperledger/aries-framework-javascript/pull/1096#discussion_r1023938917) - -The `DecryptedMessageContext` object returning as result of message unpacking contains keys while for DidComm V2 messaging it rather should be DIDs. For the current implementation we adopted V2 unpack function to return keys as well but in the future we probably need to revise this structure. - -> Should be postponed and done later in a separate PR diff --git a/packages/didcomm-v2/jest.config.ts b/packages/didcomm-v2/jest.config.ts deleted file mode 100644 index 93c0197296..0000000000 --- a/packages/didcomm-v2/jest.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Config } from '@jest/types' - -import base from '../../jest.config.base' - -import packageJson from './package.json' - -const config: Config.InitialOptions = { - ...base, - displayName: packageJson.name, - setupFilesAfterEnv: ['./tests/setup.ts'], -} - -export default config diff --git a/packages/didcomm-v2/package.json b/packages/didcomm-v2/package.json deleted file mode 100644 index f460ba2e55..0000000000 --- a/packages/didcomm-v2/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "@aries-framework/didcomm-v2", - "main": "build/index", - "types": "build/index", - "version": "0.3.1", - "private": true, - "files": [ - "build" - ], - "license": "Apache-2.0", - "publishConfig": { - "access": "public" - }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/didcomm-v2", - "repository": { - "type": "git", - "url": "https://github.com/hyperledger/aries-framework-javascript", - "directory": "packages/didcomm-v2" - }, - "scripts": { - "build": "yarn run clean && yarn run compile", - "clean": "rimraf -rf ./build", - "compile": "tsc -p tsconfig.build.json", - "prepublishOnly": "yarn run build", - "test": "jest" - }, - "dependencies": { - "didcomm": "0.4.1" - }, - "peerDependencies": { - "@aries-framework/core": "0.3.3" - }, - "devDependencies": { - "@aries-framework/core": "0.3.3", - "@aries-framework/node": "0.3.3", - "didcomm-node": "0.4.1", - "reflect-metadata": "^0.1.13", - "rimraf": "^4.4.0", - "typescript": "~4.9.5" - } -} diff --git a/packages/didcomm-v2/src/DidCommV2Module.ts b/packages/didcomm-v2/src/DidCommV2Module.ts deleted file mode 100644 index f48a04b7b0..0000000000 --- a/packages/didcomm-v2/src/DidCommV2Module.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { DependencyManager, Module } from '@aries-framework/core' -import type { default as didcomm } from 'didcomm' - -// import { DidCommV2EnvelopeServiceToken } from '@aries-framework/core' - -import { DIDCommV2LibraryToken, DidCommV2EnvelopeServiceToken, DidCommV2EnvelopeService } from './services' -import { DidCommV2DidResolver } from './services/DidCommV2DidResolver' -import { DidCommV2SecretsResolver } from './services/DidCommV2SecretsResolver' - -export interface DidCommV2ModuleParams { - // Should be passed on of packages: - // - NodeJS: `didcomm-node` - https://www.npmjs.com/package/didcomm-node - // - React Native - `@sicpa_open_source/didcomm-react-native` - https://www.npmjs.com/package/@sicpa_open_source/didcomm-react-native - didcomm: typeof didcomm -} - -export class DidCommV2Module implements Module { - private didcomm: typeof didcomm - - /** - * Registers the dependencies of the didcomm-v2 module on the dependency manager. - */ - - public constructor(props: DidCommV2ModuleParams) { - this.didcomm = props.didcomm - } - - public register(dependencyManager: DependencyManager) { - dependencyManager.registerInstance(DIDCommV2LibraryToken, this.didcomm) - dependencyManager.registerSingleton(DidCommV2EnvelopeServiceToken, DidCommV2EnvelopeService) - dependencyManager.registerContextScoped(DidCommV2DidResolver) - dependencyManager.registerContextScoped(DidCommV2SecretsResolver) - } -} diff --git a/packages/didcomm-v2/src/index.ts b/packages/didcomm-v2/src/index.ts deleted file mode 100644 index ab59a585a1..0000000000 --- a/packages/didcomm-v2/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './DidCommV2Module' -export { DidCommV2EnvelopeService, DIDCommV2LibraryToken } from './services/DidCommV2EnvelopeService' diff --git a/packages/didcomm-v2/src/services/DidCommV2DidResolver.ts b/packages/didcomm-v2/src/services/DidCommV2DidResolver.ts deleted file mode 100644 index 7360184767..0000000000 --- a/packages/didcomm-v2/src/services/DidCommV2DidResolver.ts +++ /dev/null @@ -1,93 +0,0 @@ -import type { DidDocumentService } from '@aries-framework/core' -import type { DIDDoc, DIDResolver } from 'didcomm' - -import { - injectable, - AgentContext, - DidResolverService, - DidCommV2Service, - IndyAgentService, - DidCommV1Service, -} from '@aries-framework/core' - -@injectable() -export class DidCommV2DidResolver implements DIDResolver { - private agentContext: AgentContext - private didResolverService: DidResolverService - - public constructor(agentContext: AgentContext, didResolverService: DidResolverService) { - this.agentContext = agentContext - this.didResolverService = didResolverService - } - - public async resolve(did: string): Promise { - const result = await this.didResolverService.resolve(this.agentContext, did) - if (!result.didDocument) { - return null - } - - const services = result.didDocument.service?.map((service) => DidCommV2DidResolver.mapService(service)) - - const didDod: DIDDoc = { - id: result.didDocument.id, - verificationMethod: result.didDocument.verificationMethod || [], - service: services || [], - keyAgreement: [], - authentication: [], - } - - const keyAgreements = result.didDocument.keyAgreement || [] - for (const keyAgreement of keyAgreements) { - if (typeof keyAgreement === 'string') { - didDod.keyAgreement.push(keyAgreement) - } else { - didDod.keyAgreement.push(keyAgreement.id) - didDod.verificationMethod.push(keyAgreement) - } - } - - const authentications = result.didDocument.authentication || [] - for (const authentication of authentications) { - if (typeof authentication === 'string') { - didDod.authentication.push(authentication) - } else { - didDod.authentication.push(authentication.id) - didDod.verificationMethod.push(authentication) - } - } - - return didDod - } - - private static mapService(service: DidDocumentService) { - if (service instanceof DidCommV2Service) { - return { - id: service.id, - type: 'DIDCommMessaging', - serviceEndpoint: { - uri: service.serviceEndpoint, - accept: service.accept ?? [], - routingKeys: service.routingKeys ?? [], - }, - } - } else if (service instanceof DidCommV1Service) { - return { - id: service.id, - type: 'DIDCommMessaging', - serviceEndpoint: { - uri: service.serviceEndpoint, - accept: service.accept ?? [], - routingKeys: service.routingKeys ?? [], - }, - } - } else { - return { - id: service.id, - type: 'DIDCommMessaging', - serviceEndpoint: { - uri: service.serviceEndpoint, - }, - } - } - } -} diff --git a/packages/didcomm-v2/src/services/DidCommV2EnvelopeService.ts b/packages/didcomm-v2/src/services/DidCommV2EnvelopeService.ts deleted file mode 100644 index 56bd9bc610..0000000000 --- a/packages/didcomm-v2/src/services/DidCommV2EnvelopeService.ts +++ /dev/null @@ -1,104 +0,0 @@ -import type { - AgentContext, - DidCommV2Message, - V2PackMessageParams, - EncryptedMessage, - SignedMessage, - DecryptedMessageContext, -} from '@aries-framework/core' -import type { default as didcommLibrary, IMessage } from 'didcomm' - -import { - injectable, - EnvelopeType, - JsonEncoder, - AriesFrameworkError, - Key, - DidCommMessageVersion, - inject, -} from '@aries-framework/core' - -import { DidCommV2DidResolver } from './DidCommV2DidResolver' -import { DidCommV2SecretsResolver } from './DidCommV2SecretsResolver' - -export const DIDCommV2LibraryToken = Symbol('DIDCommV2LibraryToken') -export const DidCommV2EnvelopeServiceToken = Symbol('DidCommV2EnvelopeServiceToken') - -@injectable() -export class DidCommV2EnvelopeService { - private didcomm: typeof didcommLibrary - - public constructor(@inject(DIDCommV2LibraryToken) didcomm: typeof didcommLibrary) { - this.didcomm = didcomm - } - - public async packMessage( - agentContext: AgentContext, - payload: DidCommV2Message, - params: V2PackMessageParams - ): Promise { - const message = new this.didcomm.Message(payload.toJSON() as IMessage) - - const didResolver = agentContext.dependencyManager.resolve(DidCommV2DidResolver) - const secretsResolver = agentContext.dependencyManager.resolve(DidCommV2SecretsResolver) - - if (params.envelopeType === EnvelopeType.Signed && params.signByDid) { - const [encryptedMsg] = await message.pack_signed(params.signByDid, didResolver, secretsResolver) - return JsonEncoder.fromString(encryptedMsg) - } - if ((params.envelopeType === EnvelopeType.Encrypted || !params.envelopeType) && params.toDid) { - const [encryptedMsg] = await message.pack_encrypted( - params.toDid, - params.fromDid || null, - params.signByDid || null, - didResolver, - secretsResolver, - { - forward: true, - } - ) - return JsonEncoder.fromString(encryptedMsg) - } - throw new AriesFrameworkError('Unexpected case') - } - - public async unpackMessage( - agentContext: AgentContext, - packedMessage: EncryptedMessage | SignedMessage - ): Promise { - const didResolver = agentContext.dependencyManager.resolve(DidCommV2DidResolver) - const secretsResolver = agentContext.dependencyManager.resolve(DidCommV2SecretsResolver) - - const [unpackedMsg, unpackMetadata] = await this.didcomm.Message.unpack( - JsonEncoder.toString(packedMessage), - didResolver, - secretsResolver, - {} - ) - - let senderKey: Key | undefined = undefined - let recipientKey: Key | undefined = undefined - - try { - // FIXME: DIDComm V2 returns `kid` instead of base58 key. - // We cannot simply create Key object as for DIDComm V1 from base58 representation - // So we use helper parsing kid - // TODO: Properly we should return either DecryptedV1MessageContext or DecryptedV2MessageContext depending on the unpacked message - senderKey = unpackMetadata.encrypted_from_kid ? Key.fromPublicKeyId(unpackMetadata.encrypted_from_kid) : undefined - - recipientKey = - unpackMetadata.encrypted_to_kids?.length && unpackMetadata.encrypted_to_kids[0] - ? Key.fromPublicKeyId(unpackMetadata.encrypted_to_kids[0]) - : undefined - } catch (e) { - // nothing - } - - return { - senderKey, - recipientKey, - plaintextMessage: unpackedMsg.as_value(), - didCommVersion: DidCommMessageVersion.V2, - } - } -} diff --git a/packages/didcomm-v2/src/services/DidCommV2SecretsResolver.ts b/packages/didcomm-v2/src/services/DidCommV2SecretsResolver.ts deleted file mode 100644 index cf3bc1b1f5..0000000000 --- a/packages/didcomm-v2/src/services/DidCommV2SecretsResolver.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { Secret, SecretsResolver } from 'didcomm' - -import { InjectionSymbols, Wallet, inject, injectable, Key, getKeyDidMappingByKeyType } from '@aries-framework/core' - -@injectable() -export class DidCommV2SecretsResolver implements SecretsResolver { - private wallet: Wallet - - public constructor(@inject(InjectionSymbols.Wallet) wallet: Wallet) { - this.wallet = wallet - } - - public async find_secrets(secret_ids: Array): Promise> { - const secrets = [] - for (const secret_id of secret_ids) { - // Workaround: AFJ core stores keys in the Wallet by their base58 representation, so we need to parse kid to get it - const keyId = Key.fromPublicKeyId(secret_id).publicKeyBase58 - const secret = await this.wallet.retrieveKeyPair(keyId) - if (secret) { - secrets.push(secret_id) - } - } - return secrets - } - - public async get_secret(secret_id: string): Promise { - // Workaround: AFJ core stores keys in the Wallet by their base58 representation, so we need to parse kid to get it - const keyId = Key.fromPublicKeyId(secret_id).publicKeyBase58 - const key = await this.wallet.retrieveKeyPair(keyId) - if (!key) return null - - const { supportedVerificationMethodTypes } = getKeyDidMappingByKeyType(key.keyType) - return { - id: secret_id, - type: supportedVerificationMethodTypes[0], - privateKeyBase58: key.privateKeyBase58, - } - } -} diff --git a/packages/didcomm-v2/src/services/index.ts b/packages/didcomm-v2/src/services/index.ts deleted file mode 100644 index 2c625a1ffd..0000000000 --- a/packages/didcomm-v2/src/services/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { - DIDCommV2LibraryToken, - DidCommV2EnvelopeServiceToken, - DidCommV2EnvelopeService, -} from './DidCommV2EnvelopeService' diff --git a/packages/didcomm-v2/tests/DidCommV2DidResolver.test.ts b/packages/didcomm-v2/tests/DidCommV2DidResolver.test.ts deleted file mode 100644 index c9263896df..0000000000 --- a/packages/didcomm-v2/tests/DidCommV2DidResolver.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -import type { DIDDoc } from 'didcomm' - -import { - DidCommV2Service, - DidDocument, - DidDocumentService, - DidResolverService, - IndyAgentService, - VerificationMethod, -} from '@aries-framework/core' - -import { DidCommV2DidResolver } from '../src/services/DidCommV2DidResolver' - -import { getAgentContext } from './helpers' - -const didDocument = new DidDocument({ - id: 'did:example:alice', - keyAgreement: ['did:example:alice#key-x25519'], - verificationMethod: [ - new VerificationMethod({ - id: 'did:example:alice#key-x25519', - type: 'X25519KeyAgreementKey2019', - controller: 'did:example:alice', - publicKeyBase58: '9hFgmPVfmBZwRvFEyniQDBkz9LmV7gDEqytWyGZLmDXE', - }), - ], - service: [ - new DidDocumentService({ - id: 'did:example:alice#mediator', - type: 'Mediator', - serviceEndpoint: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h', - }), - new DidDocumentService({ - id: 'did:example:alice#endpoint', - type: 'endpoint', - serviceEndpoint: 'https://agent.com', - }), - new IndyAgentService({ - id: 'did:example:alice#indy-agent', - serviceEndpoint: 'did:sov:LjgpST2rjsoxYegQDRm7EL', - recipientKeys: ['did:sov:WJz9mHyW9BZksioQnRsrAo#key-agreement-1'], - routingKeys: ['did:sov:mediator1#key-agreement-1', 'did:sov:mediator2#key-agreement-2'], - priority: 5, - }), - new DidCommV2Service({ - id: 'did:example:alice#did-comm-v2', - serviceEndpoint: 'https://agent.com/did-comm-v2', - routingKeys: ['did:example:mediator1#key-x25519', 'did:example:mediator2#key-x25519'], - accept: ['didcomm/v2', 'didcomm/aip2;env=rfc587'], - }), - ], -}) - -const resolveMock = jest.fn().mockResolvedValue({ - didResolutionMetadata: {}, - didDocument: didDocument, - didDocumentMetadata: {}, -}) - -jest.mock('@aries-framework/core', () => { - const original = jest.requireActual('@aries-framework/core') - return { - __esModule: true, - ...original, - DidResolverService: jest.fn().mockImplementation(() => { - return { resolve: resolveMock } - }), - } -}) - -const DidResolverServiceMock = DidResolverService as jest.Mock - -describe('DidCommV2DidResolver', () => { - const agentContext = getAgentContext() - const didResolverService = new DidResolverServiceMock() - - const didCommV2DidResolver = new DidCommV2DidResolver(agentContext, didResolverService) - - it('converts DidDocumentService of Mediator type into didcomm lib Service of Other kind', async () => { - const result = await didCommV2DidResolver.resolve('did:example:alice') - expect(result).not.toBeNull() - - const adaptedDidDocument = result as DIDDoc - expect(adaptedDidDocument.service).toHaveLength(4) - - expect(adaptedDidDocument.service[0].id).toBe('did:example:alice#mediator') - expect(adaptedDidDocument.service[0].serviceEndpoint).toStrictEqual({ uri: 'did:sov:Q4zqM7aXqm7gDQkUVLng9h' }) - }) - - it('converts DidDocumentService of endpoint type into didcomm lib Service of Other kind', async () => { - const result = await didCommV2DidResolver.resolve('did:example:alice') - expect(result).not.toBeNull() - - const adaptedDidDocument = result as DIDDoc - expect(adaptedDidDocument.service).toHaveLength(4) - - expect(adaptedDidDocument.service[1].id).toBe('did:example:alice#endpoint') - expect(adaptedDidDocument.service[1].serviceEndpoint).toStrictEqual({ uri: 'https://agent.com' }) - }) - - it('converts IndyAgentService into didcomm lib Service of Other kind', async () => { - const result = await didCommV2DidResolver.resolve('did:example:alice') - expect(result).not.toBeNull() - - const adaptedDidDocument = result as DIDDoc - expect(adaptedDidDocument.service).toHaveLength(4) - - expect(adaptedDidDocument.service[2].id).toBe('did:example:alice#indy-agent') - expect(adaptedDidDocument.service[2].serviceEndpoint).toStrictEqual({ uri: 'did:sov:LjgpST2rjsoxYegQDRm7EL' }) - }) - - it('converts DidCommV2Service into didcomm lib Service of DIDCommMessaging kind', async () => { - const result = await didCommV2DidResolver.resolve('did:example:alice') - expect(result).not.toBeNull() - - const adaptedDidDocument = result as DIDDoc - expect(adaptedDidDocument.service).toHaveLength(4) - - expect(adaptedDidDocument.service[3].id).toBe('did:example:alice#did-comm-v2') - expect(adaptedDidDocument.service[3].serviceEndpoint).toStrictEqual({ - accept: ['didcomm/v2', 'didcomm/aip2;env=rfc587'], - routing_keys: ['did:example:mediator1#key-x25519', 'did:example:mediator2#key-x25519'], - uri: 'https://agent.com/did-comm-v2', - }) - }) -}) diff --git a/packages/didcomm-v2/tests/DidCommV2EnvelopeService.test.ts b/packages/didcomm-v2/tests/DidCommV2EnvelopeService.test.ts deleted file mode 100644 index ca0a7037ac..0000000000 --- a/packages/didcomm-v2/tests/DidCommV2EnvelopeService.test.ts +++ /dev/null @@ -1,297 +0,0 @@ -import type { - DidCommV2MessageParams, - DidResolutionResult, - EncryptedMessage, - PlaintextMessage, - AgentContext, -} from '@aries-framework/core' -import type { Secret } from 'didcomm' - -import { - DidCommV2Message, - DidCommV2Service, - DidDocument, - DidResolverService, - isJsonObject, - IsValidMessageType, - parseMessageType, - VerificationMethod, -} from '@aries-framework/core' -import { default as didcomm } from 'didcomm-node' - -import { DidCommV2EnvelopeService } from '../src/services' -import { DidCommV2DidResolver } from '../src/services/DidCommV2DidResolver' -import { DidCommV2SecretsResolver } from '../src/services/DidCommV2SecretsResolver' - -import { getAgentContext, mockFunction } from './helpers' - -jest.mock('@aries-framework/core', () => { - const original = jest.requireActual('@aries-framework/core') - return { - __esModule: true, - ...original, - DidResolverService: jest.fn().mockImplementation(() => { - return { resolve: jest.fn() } - }), - } -}) - -const DidResolverServiceMock = DidResolverService as jest.Mock - -jest.mock('../src/services/DidCommV2SecretsResolver') -const SecretsResolverMock = DidCommV2SecretsResolver as jest.Mock - -interface ForwardMessage { - next: string - forwardedMessage: EncryptedMessage -} - -function parseForward(plaintextMessage: PlaintextMessage): ForwardMessage { - expect(plaintextMessage.type).toBe('https://didcomm.org/routing/2.0/forward') - - expect(isJsonObject(plaintextMessage.body)).toBe(true) - const body = plaintextMessage.body as Record - - expect(typeof body.next).toBe('string') - const next = body.next as string - - expect(Array.isArray(plaintextMessage.attachments)).toBe(true) - const attachments = plaintextMessage.attachments as Array - - expect(isJsonObject(attachments[0])).toBe(true) - const attachment = attachments[0] as Record - - expect(isJsonObject(attachment.data)).toBe(true) - const attachmentData = attachment.data as Record - - expect(isJsonObject(attachmentData.json)).toBe(true) - const attachmentDataJson = attachmentData.json as Record - - expect(typeof attachmentDataJson.protected).toBe('string') - expect(typeof attachmentDataJson.iv).toBe('string') - expect(typeof attachmentDataJson.ciphertext).toBe('string') - expect(typeof attachmentDataJson.tag).toBe('string') - const forwardedMessage = attachmentDataJson as EncryptedMessage - - return { - next, - forwardedMessage, - } -} - -type TestMessageParams = DidCommV2MessageParams - -class TestMessage extends DidCommV2Message { - public constructor(options?: TestMessageParams) { - super(options) - } - - @IsValidMessageType(TestMessage.type) - public readonly type = TestMessage.type.messageTypeUri - public static readonly type = parseMessageType('https://didcomm.org/test/2.0/example') -} - -async function didResolutionSuccessResult(didDocument: DidDocument): Promise { - return Promise.resolve({ - didResolutionMetadata: { contentType: 'application/did+ld+json' }, - didDocument, - didDocumentMetadata: {}, - }) -} - -async function didResolutionFailureResult(): Promise { - return Promise.resolve({ - didResolutionMetadata: { - error: 'notFound', - message: `DIDDoc not found.`, - }, - didDocument: null, - didDocumentMetadata: {}, - }) -} - -function oneKeySecretsResolver(secret: Secret) { - const service = new SecretsResolverMock() - mockFunction(service.find_secrets).mockImplementation(async (secret_ids) => { - const a = await Promise.resolve(secret_ids.filter((value) => value == secret.id)) - return a - }) - mockFunction(service.get_secret).mockImplementation(async (secret_id) => { - const a = await Promise.resolve(secret_id == secret.id ? secret : null) - return a - }) - return service -} - -const bobDidDocument = new DidDocument({ - id: 'did:example:bob', - keyAgreement: ['did:example:bob#key-x25519'], - verificationMethod: [ - new VerificationMethod({ - id: 'did:example:bob#key-x25519', - type: 'JsonWebKey2020', - controller: 'did:example:bob', - publicKeyJwk: { - kty: 'OKP', - crv: 'X25519', - x: 'GDTrI66K0pFfO54tlCSvfjjNapIs44dzpneBgyx0S3E', - }, - }), - ], - service: [ - new DidCommV2Service({ - id: 'did:example:bob#did-comm-v2', - serviceEndpoint: 'https://agent.com/did-comm-v2', - routingKeys: ['did:example:mediator1', 'did:example:mediator2'], - accept: ['didcomm/v2', 'didcomm/aip2;env=rfc587'], - }), - ], -}) - -const mediator1DidDocument = new DidDocument({ - id: 'did:example:mediator1', - keyAgreement: ['did:example:mediator1#key-x25519'], - verificationMethod: [ - new VerificationMethod({ - id: 'did:example:mediator1#key-x25519', - type: 'JsonWebKey2020', - controller: 'did:example:mediator1', - publicKeyJwk: { - kty: 'OKP', - crv: 'X25519', - x: 'UT9S3F5ep16KSNBBShU2wh3qSfqYjlasZimn0mB8_VM', - }, - }), - ], -}) - -const mediator2DidDocument = new DidDocument({ - id: 'did:example:mediator2', - keyAgreement: ['did:example:mediator2#key-x25519'], - verificationMethod: [ - new VerificationMethod({ - id: 'did:example:mediator2#key-x25519', - type: 'JsonWebKey2020', - controller: 'did:example:mediator2', - publicKeyJwk: { - kty: 'OKP', - crv: 'X25519', - x: '82k2BTUiywKv49fKLZa-WwDi8RBf0tB0M8bvSAUQ3yY', - }, - }), - ], -}) - -const bobSecret = { - id: 'did:example:bob#key-x25519', - type: 'JsonWebKey2020', - privateKeyJwk: { - kty: 'OKP', - crv: 'X25519', - x: 'GDTrI66K0pFfO54tlCSvfjjNapIs44dzpneBgyx0S3E', - d: 'b9NnuOCB0hm7YGNvaE9DMhwH_wjZA1-gWD6dA0JWdL0', - }, -} - -const mediator1Secret = { - id: 'did:example:mediator1#key-x25519', - type: 'JsonWebKey2020', - privateKeyJwk: { - kty: 'OKP', - crv: 'X25519', - x: 'UT9S3F5ep16KSNBBShU2wh3qSfqYjlasZimn0mB8_VM', - d: 'p-vteoF1gopny1HXywt76xz_uC83UUmrgszsI-ThBKk', - }, -} - -const mediator2Secret = { - id: 'did:example:mediator2#key-x25519', - type: 'JsonWebKey2020', - privateKeyJwk: { - kty: 'OKP', - crv: 'X25519', - x: '82k2BTUiywKv49fKLZa-WwDi8RBf0tB0M8bvSAUQ3yY', - d: 'f9WJeuQXEItkGM8shN4dqFr5fLQLBasHnWZ-8dPaSo0', - }, -} - -// FIXME: Forwrad wrapping seems to be broken -describe('DIDCommV2EnvelopeService', () => { - const envelopeService = new DidCommV2EnvelopeService(didcomm) - - const didResolverService = new DidResolverServiceMock() - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - mockFunction(didResolverService.resolve).mockImplementation(async (agentContext, didUrl, _options) => { - switch (didUrl) { - case bobDidDocument.id: - return didResolutionSuccessResult(bobDidDocument) - case mediator1DidDocument.id: - return didResolutionSuccessResult(mediator1DidDocument) - case mediator2DidDocument.id: - return didResolutionSuccessResult(mediator2DidDocument) - default: - return didResolutionFailureResult() - } - }) - - const aliceAgentContext = getAgentContext() - const aliceDidResolver = new DidCommV2DidResolver(aliceAgentContext, didResolverService) - const aliceSecretsResolver = new SecretsResolverMock() - registerResolversInContext(aliceAgentContext, aliceDidResolver, aliceSecretsResolver) - - const bobAgentContext = getAgentContext() - const bobDidResolver = new DidCommV2DidResolver(bobAgentContext, didResolverService) - const bobSecretsResolver = oneKeySecretsResolver(bobSecret) - registerResolversInContext(bobAgentContext, bobDidResolver, bobSecretsResolver) - - const mediator1AgentContext = getAgentContext() - const mediator1DidResolver = new DidCommV2DidResolver(mediator1AgentContext, didResolverService) - const mediator1SecretsResolver = oneKeySecretsResolver(mediator1Secret) - registerResolversInContext(mediator1AgentContext, mediator1DidResolver, mediator1SecretsResolver) - - const mediator2AgentContext = getAgentContext() - const mediator2DidResolver = new DidCommV2DidResolver(mediator2AgentContext, didResolverService) - const mediator2SecretsResolver = oneKeySecretsResolver(mediator2Secret) - registerResolversInContext(mediator2AgentContext, mediator2DidResolver, mediator2SecretsResolver) - - test("packMessageEncrypted uses recipient's routing", async () => { - const message = new TestMessage({ - to: 'did:example:bob', - body: { - greeting: 'Hello, world!', - }, - }) as DidCommV2Message - - const packedForMediator1 = await envelopeService.packMessage(aliceAgentContext, message, { - toDid: 'did:example:bob', - }) - - const unpackedByMediator1 = await envelopeService.unpackMessage(mediator1AgentContext, packedForMediator1) - const forwardForMediator1 = parseForward(unpackedByMediator1.plaintextMessage) - expect(forwardForMediator1.next).toBe('did:example:mediator2') - const packedForMediator2 = forwardForMediator1.forwardedMessage - - const unpackedByMediator2 = await envelopeService.unpackMessage(mediator2AgentContext, packedForMediator2) - const forwardForMediator2 = parseForward(unpackedByMediator2.plaintextMessage) - expect(forwardForMediator2.next).toBe('did:example:bob') - const packedForBob = forwardForMediator2.forwardedMessage - - const unpackedByBob = await envelopeService.unpackMessage(bobAgentContext, packedForBob) - expect(unpackedByBob.plaintextMessage.type).toBe('https://didcomm.org/test/2.0/example') - - expect(isJsonObject(unpackedByBob.plaintextMessage.body)).toBe(true) - const unpackedMessageBody = unpackedByBob.plaintextMessage.body as Record - - expect(unpackedMessageBody.greeting).toBe('Hello, world!') - }) -}) - -function registerResolversInContext( - agentContext: AgentContext, - didResolver: DidCommV2DidResolver, - secretsResolver: DidCommV2SecretsResolver -) { - agentContext.dependencyManager.registerInstance(DidCommV2DidResolver, didResolver) - agentContext.dependencyManager.registerInstance(DidCommV2SecretsResolver, secretsResolver) -} diff --git a/packages/didcomm-v2/tests/DidCommV2Module.test.ts b/packages/didcomm-v2/tests/DidCommV2Module.test.ts deleted file mode 100644 index e0f0288de3..0000000000 --- a/packages/didcomm-v2/tests/DidCommV2Module.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { DependencyManager } from '@aries-framework/core' - -// import { DidCommV2EnvelopeServiceToken } from '@aries-framework/core' -import { default as didcomm } from 'didcomm-node' - -import { DIDCommV2LibraryToken, DidCommV2Module } from '../src' - -const dependencyManager = { - registerInstance: jest.fn(), - registerSingleton: jest.fn(), - registerContextScoped: jest.fn(), -} as unknown as DependencyManager - -describe('DidCommV2Module', () => { - test('registers dependencies on the dependency manager', () => { - const didCommV2Module = new DidCommV2Module({ didcomm }) - didCommV2Module.register(dependencyManager) - - expect(dependencyManager.registerInstance).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerInstance).toHaveBeenCalledWith(DIDCommV2LibraryToken, didcomm) - - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) - // expect(dependencyManager.registerSingleton).toHaveBeenCalledWith( - // DidCommV2EnvelopeServiceToken, - // DidCommV2EnvelopeServiceImpl - // ) - }) -}) diff --git a/packages/didcomm-v2/tests/helpers.ts b/packages/didcomm-v2/tests/helpers.ts deleted file mode 100644 index fc45d856be..0000000000 --- a/packages/didcomm-v2/tests/helpers.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { Wallet } from '@aries-framework/core' - -import { AgentConfig, AgentContext, DependencyManager, InjectionSymbols } from '@aries-framework/core' - -export function mockFunction any>(fn: T): jest.MockedFunction { - return fn as jest.MockedFunction -} - -export function getAgentContext({ - dependencyManager = new DependencyManager(), - wallet, - agentConfig, - contextCorrelationId = 'mock', -}: { - dependencyManager?: DependencyManager - wallet?: Wallet - agentConfig?: AgentConfig - contextCorrelationId?: string -} = {}) { - if (wallet) dependencyManager.registerInstance(InjectionSymbols.Wallet, wallet) - if (agentConfig) dependencyManager.registerInstance(AgentConfig, agentConfig) - return new AgentContext({ dependencyManager, contextCorrelationId }) -} diff --git a/packages/didcomm-v2/tests/setup.ts b/packages/didcomm-v2/tests/setup.ts deleted file mode 100644 index 00b77cc0fe..0000000000 --- a/packages/didcomm-v2/tests/setup.ts +++ /dev/null @@ -1,3 +0,0 @@ -import 'reflect-metadata' - -jest.setTimeout(30000) diff --git a/packages/didcomm-v2/tsconfig.build.json b/packages/didcomm-v2/tsconfig.build.json deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/didcomm-v2/tsconfig.json b/packages/didcomm-v2/tsconfig.json deleted file mode 100644 index 46efe6f721..0000000000 --- a/packages/didcomm-v2/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "types": ["jest"] - } -} diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index 96bc1d86c9..b740484f84 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -13,20 +13,21 @@ import type { WalletSignOptions, WalletVerifyOptions, } from '@aries-framework/core' -import type { OpenWalletCredentials, WalletConfig as IndySdkWalletConfig, WalletStorageConfig } from 'indy-sdk' - // eslint-disable-next-line import/order +import type { OpenWalletCredentials, WalletConfig as IndySdkWalletConfig, WalletStorageConfig } from 'indy-sdk' import { AriesFrameworkError, + DidCommMessageVersion, InjectionSymbols, + isDidCommV1EncryptedEnvelope, isValidPrivateKey, isValidSeed, JsonEncoder, Key, + KeyProviderRegistry, KeyType, Logger, RecordNotFoundError, - KeyProviderRegistry, TypedArrayEncoder, WalletDuplicateError, WalletError, @@ -35,14 +36,13 @@ import { WalletKeyExistsError, WalletNotFoundError, } from '@aries-framework/core' - -const isError = (error: unknown): error is Error => error instanceof Error - import { inject, injectable } from 'tsyringe' import { IndySdkError, isIndyError } from '../error' import { IndySdk, IndySdkSymbol } from '../types' +const isError = (error: unknown): error is Error => error instanceof Error + @injectable() export class IndySdkWallet implements Wallet { private walletConfig?: WalletConfig @@ -55,10 +55,10 @@ export class IndySdkWallet implements Wallet { public constructor( @inject(IndySdkSymbol) indySdk: IndySdk, @inject(InjectionSymbols.Logger) logger: Logger, - signingKeyProviderRegistry: KeyProviderRegistry + keyProviderRegistry: KeyProviderRegistry ) { this.logger = logger - this.keyProviderRegistry = signingKeyProviderRegistry + this.keyProviderRegistry = keyProviderRegistry this.indySdk = indySdk } @@ -547,15 +547,22 @@ export class IndySdkWallet implements Wallet { } public async pack(payload: Record, params: WalletPackOptions): Promise { + if (params.didCommVersion === DidCommMessageVersion.V1) { + return this.packDidCommV1(payload, params) + } + if (params.didCommVersion === DidCommMessageVersion.V2) { + throw new AriesFrameworkError(`DidComm V2 message encryption is not supported for Indy wallet`) + } + throw new AriesFrameworkError(`Unsupported DidComm version: ${params.didCommVersion}`) + } + + private async packDidCommV1(payload: Record, params: WalletPackOptions): Promise { try { - if (params.version === 'v1') { - const messageRaw = JsonEncoder.toBuffer(payload) - const { recipientKeys, senderKey } = params - const packedMessage = await this.indySdk.packMessage(this.handle, messageRaw, recipientKeys, senderKey) - return JsonEncoder.fromBuffer(packedMessage) - } else { - throw new AriesFrameworkError('DIDComm V2 message packing is not supported for IndyWallet') - } + const messageRaw = JsonEncoder.toBuffer(payload) + const recipientKeys = params.recipientKeys.map((recipientKey) => recipientKey.publicKeyBase58) + const senderKey = params.senderKey?.publicKeyBase58 ?? null + const packedMessage = await this.indySdk.packMessage(this.handle, messageRaw, recipientKeys, senderKey) + return JsonEncoder.fromBuffer(packedMessage) } catch (error) { if (!isError(error)) { throw new AriesFrameworkError('Attempted to throw error, but it was not of type Error', { cause: error }) @@ -565,12 +572,23 @@ export class IndySdkWallet implements Wallet { } public async unpack(messagePackage: EncryptedMessage): Promise { + if (isDidCommV1EncryptedEnvelope(messagePackage)) { + return this.unpackDidCommV1(messagePackage) + } else { + throw new AriesFrameworkError(`DidComm V2 message encryption is not supported for Indy wallet`) + } + } + + private async unpackDidCommV1(messagePackage: EncryptedMessage): Promise { try { const unpackedMessageBuffer = await this.indySdk.unpackMessage(this.handle, JsonEncoder.toBuffer(messagePackage)) const unpackedMessage = JsonEncoder.fromBuffer(unpackedMessageBuffer) return { - senderKey: unpackedMessage.sender_verkey, - recipientKey: unpackedMessage.recipient_verkey, + didCommVersion: DidCommMessageVersion.V1, + senderKey: unpackedMessage.sender_verkey + ? Key.fromPublicKeyBase58(unpackedMessage.sender_verkey, KeyType.Ed25519) + : undefined, + recipientKey: Key.fromPublicKeyBase58(unpackedMessage.recipient_verkey, KeyType.Ed25519), plaintextMessage: JsonEncoder.fromString(unpackedMessage.message), } } catch (error) { @@ -592,7 +610,7 @@ export class IndySdkWallet implements Wallet { } } - public async retrieveKeyPair(publicKeyBase58: string): Promise { + private async retrieveKeyPair(publicKeyBase58: string): Promise { try { const { value } = await this.indySdk.getWalletRecord(this.handle, 'KeyPairRecord', `key-${publicKeyBase58}`, {}) if (value) { diff --git a/yarn.lock b/yarn.lock index 4ad6bb13e9..5b6b66c9c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4784,16 +4784,6 @@ did-resolver@^4.0.0, did-resolver@^4.1.0: resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.1.0.tgz#740852083c4fd5bf9729d528eca5d105aff45eb6" integrity sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA== -didcomm-node@0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/didcomm-node/-/didcomm-node-0.4.1.tgz#85f8cd55cb637370d4c0528f06693df5719af700" - integrity sha512-5vR2T6AyFhw20LI3Hu7Xps1nYimpooEIbefweIAsZ964p8sSnWW07qulvnRwd1fMihtUaAK+DlwGkR4jZIf6Mg== - -didcomm@0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/didcomm/-/didcomm-0.4.1.tgz#f94923f6d7f00dbf27818a5965d9a432874cdf96" - integrity sha512-fcGSfLL5ri18vbbjRexHWVk2RAxZ/eBlyKeLCzS/3k5RdogqJbnUEcNsq64at1nA1u16ENa1bsZodGeRoq84ZA== - diff-sequences@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2"